/* * Copyright 2005-2023, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license. * * Authors: * Robert Polic * Stephan Aßmus * * Copyright 1999, Be Incorporated. All Rights Reserved. * This file may be used under the terms of the Be Sample Code License. */ #include "PersonWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PeopleApp.h" #include "PersonView.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "People" PersonWindow::PersonWindow(BRect frame, const char* title, const char* nameAttribute, const char* categoryAttribute, const entry_ref* ref) : BWindow(frame, title, B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), fRef(NULL), fPanel(NULL), fNameAttribute(nameAttribute) { BMenu* menu; BMenuItem* item; BMenuBar* menuBar = new BMenuBar(""); menu = new BMenu(B_TRANSLATE("File")); menu->AddItem(item = new BMenuItem( B_TRANSLATE("New person" B_UTF8_ELLIPSIS), new BMessage(M_NEW), 'N')); item->SetTarget(be_app); menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), new BMessage(B_QUIT_REQUESTED), 'W')); menu->AddSeparatorItem(); menu->AddItem(fSave = new BMenuItem(B_TRANSLATE("Save"), new BMessage(M_SAVE), 'S')); fSave->SetEnabled(FALSE); menu->AddItem(new BMenuItem( B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(M_SAVE_AS))); menu->AddItem(fRevert = new BMenuItem(B_TRANSLATE("Revert"), new BMessage(M_REVERT), 'R')); fRevert->SetEnabled(FALSE); menu->AddSeparatorItem(); item = new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED), 'Q'); item->SetTarget(be_app); menu->AddItem(item); menuBar->AddItem(menu); menu = new BMenu(B_TRANSLATE("Edit")); menu->AddItem(fUndo = new BMenuItem(B_TRANSLATE("Undo"), new BMessage(B_UNDO), 'Z')); fUndo->SetEnabled(false); menu->AddSeparatorItem(); menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"), new BMessage(B_CUT), 'X')); menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"), new BMessage(B_COPY), 'C')); menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"), new BMessage(B_PASTE), 'V')); BMenuItem* selectAllItem = new BMenuItem(B_TRANSLATE("Select all"), new BMessage(M_SELECT), 'A'); menu->AddItem(selectAllItem); menu->AddSeparatorItem(); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Configure attributes"), new BMessage(M_CONFIGURE_ATTRIBUTES), 'F')); item->SetTarget(be_app); menuBar->AddItem(menu); if (ref != NULL) { SetTitle(ref->name); _SetToRef(new entry_ref(*ref)); } else _SetToRef(NULL); fView = new PersonView("PeopleView", categoryAttribute, fRef); BScrollView* scrollView = new BScrollView("PeopleScrollView", fView, 0, false, true, B_NO_BORDER); scrollView->SetExplicitMinSize(BSize(scrollView->MinSize().width, 0)); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .SetInsets(0, 0, -1, 0) .Add(menuBar) .Add(scrollView); fRevert->SetTarget(fView); selectAllItem->SetTarget(fView); } PersonWindow::~PersonWindow() { _SetToRef(NULL); } void PersonWindow::MenusBeginning() { bool enabled = !fView->IsSaved(); fSave->SetEnabled(enabled); fRevert->SetEnabled(enabled); bool isRedo = false; bool undoEnabled = false; bool cutAndCopyEnabled = false; BTextView* textView = dynamic_cast(CurrentFocus()); if (textView != NULL) { undo_state state = textView->UndoState(&isRedo); undoEnabled = state != B_UNDO_UNAVAILABLE; cutAndCopyEnabled = fView->IsTextSelected(); } if (isRedo) fUndo->SetLabel(B_TRANSLATE("Redo")); else fUndo->SetLabel(B_TRANSLATE("Undo")); fUndo->SetEnabled(undoEnabled); fCut->SetEnabled(cutAndCopyEnabled); fCopy->SetEnabled(cutAndCopyEnabled); be_clipboard->Lock(); fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); be_clipboard->Unlock(); fView->BuildGroupMenu(); } void PersonWindow::MessageReceived(BMessage* msg) { switch (msg->what) { case M_SAVE: if (!fRef) { SaveAs(); break; } // supposed to fall through case M_REVERT: case M_SELECT: fView->MessageReceived(msg); break; case M_SAVE_AS: SaveAs(); break; case B_UNDO: // fall through case B_CUT: case B_COPY: case B_PASTE: { BView* view = CurrentFocus(); if (view != NULL) view->MessageReceived(msg); break; } case B_SAVE_REQUESTED: { entry_ref dir; if (msg->FindRef("directory", &dir) == B_OK) { const char* name = NULL; msg->FindString("name", &name); BDirectory directory; directory.SetTo(&dir); if (directory.InitCheck() == B_NO_ERROR) { BFile file; directory.CreateFile(name, &file); if (file.InitCheck() == B_NO_ERROR) { BNodeInfo* node = new BNodeInfo(&file); node->SetType("application/x-person"); delete node; BEntry entry; directory.FindEntry(name, &entry); entry.GetRef(&dir); _SetToRef(new entry_ref(dir)); SetTitle(fRef->name); fView->CreateFile(fRef); } else { BString str; str.SetToFormat(B_TRANSLATE("Could not create %s."), name); BAlert* alert = new BAlert("", str.String(), B_TRANSLATE("Sorry")); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); } } } break; } case B_NODE_MONITOR: { int32 opcode; if (msg->FindInt32("opcode", &opcode) == B_OK) { switch (opcode) { case B_ENTRY_REMOVED: // We lost our file. Close the window. PostMessage(B_QUIT_REQUESTED); break; case B_ENTRY_MOVED: { // We may have renamed our entry. Obtain relevant data // from message. BString name; msg->FindString("name", &name); int64 directory; msg->FindInt64("to directory", &directory); int32 device; msg->FindInt32("device", &device); // Update our ref. delete fRef; fRef = new entry_ref(device, directory, name.String()); // And our window title. SetTitle(name); // If moved to Trash, close window. BVolume volume(device); BPath trash; find_directory(B_TRASH_DIRECTORY, &trash, false, &volume); BPath folder(fRef); folder.GetParent(&folder); if (folder == trash) PostMessage(B_QUIT_REQUESTED); break; } case B_ATTR_CHANGED: { // An attribute was updated. BString attr; if (msg->FindString("attr", &attr) == B_OK) fView->SetAttribute(attr.String(), true); break; } case B_STAT_CHANGED: fView->UpdatePicture(fRef); break; } } break; } default: BWindow::MessageReceived(msg); } } bool PersonWindow::QuitRequested() { status_t result; if (!fView->IsSaved()) { BAlert* alert = new BAlert("", B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); alert->SetShortcut(0, B_ESCAPE); alert->SetShortcut(1, 'd'); alert->SetShortcut(2, 's'); result = alert->Go(); if (result == 2) { if (fRef) fView->Save(); else { SaveAs(); return false; } } else if (result == 0) return false; } delete fPanel; BMessage message(M_WINDOW_QUITS); message.AddRect("frame", Frame()); if (be_app->Lock()) { be_app->PostMessage(&message); be_app->Unlock(); } return true; } void PersonWindow::Show() { BRect screenFrame = BScreen(this).Frame(); if (Frame().bottom > screenFrame.bottom) ResizeBy(0, screenFrame.bottom - Frame().bottom - 10); fView->MakeFocus(); BWindow::Show(); } void PersonWindow::AddAttribute(const char* label, const char* attribute) { fView->AddAttribute(label, attribute); } void PersonWindow::SetInitialValues(BMessage* message) { char* attribute; uint32 type; int32 count; for (int32 i = 0; message->GetInfo(B_STRING_TYPE, i, &attribute, &type, &count) == B_OK; i++) { BString text = ""; if (message->FindString(attribute, &text) == B_OK) fView->SetAttribute(attribute, text.String(), true); } } void PersonWindow::SaveAs() { char name[B_FILE_NAME_LENGTH]; _GetDefaultFileName(name); if (fPanel == NULL) { BMessenger target(this); fPanel = new BFilePanel(B_SAVE_PANEL, &target); BPath path; find_directory(B_USER_DIRECTORY, &path, true); BDirectory dir; dir.SetTo(path.Path()); BEntry entry; if (dir.FindEntry("people", &entry) == B_OK || (dir.CreateDirectory("people", &dir) == B_OK && dir.GetEntry(&entry) == B_OK)) { fPanel->SetPanelDirectory(&entry); } } if (fPanel->Window()->Lock()) { fPanel->SetSaveText(name); if (fPanel->Window()->IsHidden()) fPanel->Window()->Show(); else fPanel->Window()->Activate(); fPanel->Window()->Unlock(); } } bool PersonWindow::RefersPersonFile(const entry_ref& ref) const { if (fRef == NULL) return false; return *fRef == ref; } void PersonWindow::_GetDefaultFileName(char* name) { strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH); while (*name) { if (*name == '/') *name = '-'; name++; } } void PersonWindow::_SetToRef(entry_ref* ref) { if (fRef != NULL) { _WatchChanges(false); delete fRef; } fRef = ref; _WatchChanges(true); } void PersonWindow::_WatchChanges(bool enable) { if (fRef == NULL) return; node_ref nodeRef; BNode node(fRef); node.GetNodeRef(&nodeRef); uint32 flags; BString action; if (enable) { // Start watching. flags = B_WATCH_ALL; action = "starting"; } else { // Stop watching. flags = B_STOP_WATCHING; action = "stoping"; } if (watch_node(&nodeRef, flags, this) != B_OK) { printf("Error %s node monitor.\n", action.String()); } }