Reimplemented how People works. The previously hard-coded attribute list

is now only a fallback attribute list which is install in case the Person
MIME type has no attributes at all. Otherwise the GUI is completely driven
by the current Person attributes, as configured (and sorted) by FileTypes.
This has been on my mental TODO list since years. Finally one can add
a "Cell phone" attribute in Filetypes and actually see and edit it in
People. Or a birth day attribute. If I had read the previous code correctly,
it was only due to a bug that People did not remove those any attributes
on every launch.

I've tested this quite a bit, but it wouldn't hurt to test it some more.
The only "regression" I am aware of is that State and Zip code are not
in one line anymore. Perhaps this feature could be reintroduced by looking
at the display width and if two attributes are short, put them in one line...


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40381 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2011-02-07 23:25:16 +00:00
parent 2099a79214
commit bb5f99a5a6
12 changed files with 661 additions and 554 deletions

View File

@ -0,0 +1,57 @@
/*
* Copyright 2005-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#include "AttributeTextControl.h"
#include <string.h>
#include <malloc.h>
#include <Font.h>
AttributeTextControl::AttributeTextControl(const char* label,
const char* attribute)
:
BTextControl(NULL, "", NULL),
fAttribute(attribute),
fOriginalValue()
{
if (label != NULL && label[0] != 0)
SetLabel(BString(label).Append(":"));
SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
}
AttributeTextControl::~AttributeTextControl()
{
}
bool
AttributeTextControl::HasChanged()
{
return fOriginalValue != Text();
}
void
AttributeTextControl::Revert()
{
if (HasChanged())
SetText(fOriginalValue);
}
void
AttributeTextControl::Update()
{
fOriginalValue = Text();
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2005-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#ifndef TEXT_CONTROL_H
#define TEXT_CONTROL_H
#include <String.h>
#include <TextControl.h>
class AttributeTextControl : public BTextControl {
public:
AttributeTextControl(const char* label,
const char* attribute);
virtual ~AttributeTextControl();
bool HasChanged();
void Revert();
void Update();
const BString& Attribute() const
{ return fAttribute; }
private:
BString fAttribute;
BString fOriginalValue;
};
#endif // TEXT_CONTROL_H

View File

@ -1,10 +1,12 @@
SubDir HAIKU_TOP src apps people ;
UsePrivateHeaders shared ;
Application People : main.cpp
AttributeTextControl.cpp
PeopleApp.cpp
PeopleView.cpp
PeopleWindow.cpp
TTextControl.cpp
: libbe.so libtracker.so $(TARGET_LIBSUPC++) $(HAIKU_LOCALE_LIBS)
: People.rdef
;

View File

@ -1,10 +1,11 @@
/*
* Copyright 2005-2009, Haiku, Inc. All rights reserved.
* Copyright 2005-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
* Axel Dörfler, axeld@pinc-software.de
* Stephan Aßmus <superstippi@gmx.de>
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
@ -12,6 +13,7 @@
#include <Alert.h>
#include <AutoDeleter.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Directory.h>
@ -34,8 +36,18 @@
#define B_TRANSLATE_CONTEXT "People"
struct people_field gFields[] = {
{ "META:name", 120, B_TRANSLATE("Contact name") },
struct DefaultAttribute {
const char* attribute;
int32 width;
const char* name;
};
// TODO: Add flags in attribute info message to find these.
static const char* kNameAttribute = "META:name";
static const char* kCategoryAttribute = "META:group";
struct DefaultAttribute sDefaultAttributes[] = {
{ kNameAttribute, 120, B_TRANSLATE("Contact name") },
{ "META:nickname", 120, B_TRANSLATE("Nickname") },
{ "META:company", 120, B_TRANSLATE("Company") },
{ "META:address", 120, B_TRANSLATE("Address") },
@ -48,16 +60,19 @@ struct people_field gFields[] = {
{ "META:fax", 90, B_TRANSLATE("Fax") },
{ "META:email", 120, B_TRANSLATE("E-mail") },
{ "META:url", 120, B_TRANSLATE("URL") },
{ "META:group", 120, B_TRANSLATE("Group") },
{ kCategoryAttribute, 120, B_TRANSLATE("Group") },
{ NULL, 0, NULL }
};
TPeopleApp::TPeopleApp(void)
: BApplication(APP_SIG),
fWindowCount(0)
TPeopleApp::TPeopleApp()
:
BApplication(APP_SIG),
fWindowCount(0),
fAttributes(20, true)
{
fPosition.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH, TITLE_BAR_HEIGHT + WIND_HEIGHT);
fPosition.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
TITLE_BAR_HEIGHT + WIND_HEIGHT);
BPoint pos = fPosition.LeftTop();
BPath path;
@ -65,7 +80,7 @@ TPeopleApp::TPeopleApp(void)
BDirectory dir(path.Path());
BEntry entry;
if (dir.FindEntry("People_data", &entry) == B_NO_ERROR) {
if (dir.FindEntry("People_data", &entry) == B_OK) {
fPrefs = new BFile(&entry, B_READ_WRITE);
if (fPrefs->InitCheck() == B_NO_ERROR) {
fPrefs->Read(&pos, sizeof(BPoint));
@ -74,23 +89,15 @@ TPeopleApp::TPeopleApp(void)
}
} else {
fPrefs = new BFile();
if (dir.CreateFile("People_data", fPrefs) != B_NO_ERROR) {
if (dir.CreateFile("People_data", fPrefs) != B_OK) {
delete fPrefs;
fPrefs = NULL;
}
}
// create indices on all volumes
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
for (int32 i = 0; gFields[i].attribute; i++) {
fs_create_index(volume.Device(), gFields[i].attribute, B_STRING_TYPE, 0);
}
}
// install person mime type
// Read attributes from person mime type. If it does not exist,
// or if it contains no attribute definitions, install a "clean"
// person mime type from the hard-coded default attributes.
bool valid = false;
BMimeType mime;
@ -99,17 +106,42 @@ TPeopleApp::TPeopleApp(void)
if (mime.IsInstalled()) {
BMessage info;
if (mime.GetAttrInfo(&info) == B_NO_ERROR) {
const char *string;
int32 index = 0;
while (info.FindString("attr:name", index++, &string) == B_OK) {
if (!strcmp(string, gFields[0].attribute)) {
valid = true;
while (true) {
int32 type;
if (info.FindInt32("attr:type", index, &type) != B_OK)
break;
bool editable;
if (info.FindBool("attr:editable", index, &editable) != B_OK)
break;
// TODO: Support other types besides string attributes.
if (type != B_STRING_TYPE || !editable)
break;
Attribute* attribute = new Attribute();
ObjectDeleter<Attribute> deleter(attribute);
if (info.FindString("attr:public_name", index,
&attribute->name) != B_OK) {
break;
}
if (info.FindString("attr:name", index,
&attribute->attribute) != B_OK) {
break;
}
if (!fAttributes.AddItem(attribute))
break;
deleter.Detach();
index++;
}
if (!valid)
mime.Delete();
}
if (fAttributes.CountItems() == 0) {
valid = false;
mime.Delete();
} else
valid = true;
}
if (!valid) {
mime.Install();
@ -121,154 +153,185 @@ TPeopleApp::TPeopleApp(void)
mime.SetIcon(kPersonIcon, sizeof(kPersonIcon));
mime.SetPreferredApp(APP_SIG);
// add relevant person fields to meta-mime type
// add default person fields to meta-mime type
BMessage fields;
for (int32 i = 0; gFields[i].attribute; i++) {
fields.AddString("attr:public_name", gFields[i].name);
fields.AddString("attr:name", gFields[i].attribute);
for (int32 i = 0; sDefaultAttributes[i].attribute; i++) {
fields.AddString("attr:public_name", sDefaultAttributes[i].name);
fields.AddString("attr:name", sDefaultAttributes[i].attribute);
fields.AddInt32("attr:type", B_STRING_TYPE);
fields.AddBool("attr:viewable", true);
fields.AddBool("attr:editable", true);
fields.AddInt32("attr:width", gFields[i].width);
fields.AddInt32("attr:width", sDefaultAttributes[i].width);
fields.AddInt32("attr:alignment", B_ALIGN_LEFT);
fields.AddBool("attr:extra", false);
// Add the default attribute to the attribute list, too.
Attribute* attribute = new Attribute();
attribute->name = sDefaultAttributes[i].name;
attribute->attribute = sDefaultAttributes[i].attribute;
if (!fAttributes.AddItem(attribute))
delete attribute;
}
mime.SetAttrInfo(&fields);
}
// create indices on all volumes for the found attributes.
int32 count = fAttributes.CountItems();
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
for (int32 i = 0; i < count; i++) {
Attribute* attribute = fAttributes.ItemAt(i);
fs_create_index(volume.Device(), attribute->attribute,
B_STRING_TYPE, 0);
}
}
}
TPeopleApp::~TPeopleApp(void)
TPeopleApp::~TPeopleApp()
{
delete fPrefs;
}
void
TPeopleApp::AboutRequested(void)
TPeopleApp::AboutRequested()
{
(new BAlert("", "...by Robert Polic", B_TRANSLATE("OK")))->Go();
(new BAlert(B_TRANSLATE("About"), B_UTF8_ELLIPSIS "by Robert Polic",
B_TRANSLATE("OK")))->Go();
}
void
TPeopleApp::ArgvReceived(int32 argc, char **argv)
TPeopleApp::ArgvReceived(int32 argc, char** argv)
{
TPeopleWindow* window = NULL;
BMessage message(B_REFS_RECEIVED);
for (int32 loop = 1; loop < argc; loop++) {
char* arg = argv[loop];
int32 index;
for (index = 0; gFields[index].attribute; index++) {
if (!strncmp(gFields[index].attribute, arg, strlen(gFields[index].attribute)))
break;
}
if (gFields[index].attribute != NULL) {
if (!window)
window = NewWindow();
while (arg[0] && arg[0] != ' ' && arg[0] != '=')
arg++;
if (arg[0]) {
arg++;
window->SetField(index, arg);
}
}
for (int32 i = 1; i < argc; i++) {
BEntry entry(argv[i]);
entry_ref ref;
if (entry.Exists() && entry.GetRef(&ref) == B_OK)
message.AddRef("refs", &ref);
}
RefsReceived(&message);
}
void
TPeopleApp::MessageReceived(BMessage *msg)
TPeopleApp::MessageReceived(BMessage* message)
{
switch (msg->what) {
switch (message->what) {
case M_NEW:
case B_SILENT_RELAUNCH:
NewWindow();
_NewWindow();
break;
case M_WINDOW_QUITS:
SavePreferences(msg);
_SavePreferences(message);
fWindowCount--;
if (fWindowCount < 1)
PostMessage(B_QUIT_REQUESTED);
break;
default:
BApplication::MessageReceived(msg);
BApplication::MessageReceived(message);
}
}
void
TPeopleApp::RefsReceived(BMessage *message)
TPeopleApp::RefsReceived(BMessage* message)
{
int32 index = 0;
while (message->HasRef("refs", index)) {
entry_ref ref;
message->FindRef("refs", index++, &ref);
TPeopleWindow* window = FindWindow(ref);
TPeopleWindow* window = _FindWindow(ref);
if (window != NULL)
window->Activate(true);
else {
BFile file(&ref, B_READ_ONLY);
if (file.InitCheck() == B_OK)
NewWindow(&ref);
_NewWindow(&ref);
}
}
}
void
TPeopleApp::ReadyToRun(void)
TPeopleApp::ReadyToRun()
{
if (fWindowCount < 1)
NewWindow();
_NewWindow();
}
// #pragma mark -
TPeopleWindow*
TPeopleApp::NewWindow(entry_ref *ref)
TPeopleApp::_NewWindow(entry_ref* ref)
{
TPeopleWindow *window;
TPeopleWindow* window = new TPeopleWindow(fPosition,
B_TRANSLATE("New person"), kNameAttribute,
kCategoryAttribute, ref);
_AddAttributes(window);
window = new TPeopleWindow(fPosition, B_TRANSLATE("New person"), ref);
window->Show();
fWindowCount++;
fPosition.OffsetBy(20, 20);
if (fPosition.bottom > BScreen(B_MAIN_SCREEN_ID).Frame().bottom)
fWindowCount++;
// Offset the position for the next window which will be opened and
// reset it if it would open outside the screen bounds.
fPosition.OffsetBy(20, 20);
BScreen screen(window);
if (fPosition.bottom > screen.Frame().bottom)
fPosition.OffsetTo(fPosition.left, TITLE_BAR_HEIGHT);
if (fPosition.right > BScreen(B_MAIN_SCREEN_ID).Frame().right)
if (fPosition.right > screen.Frame().right)
fPosition.OffsetTo(6, fPosition.top);
return window;
}
TPeopleWindow*
TPeopleApp::FindWindow(entry_ref ref)
void
TPeopleApp::_AddAttributes(TPeopleWindow* window) const
{
TPeopleWindow* window;
int32 index = 0;
int32 count = fAttributes.CountItems();
for (int32 i = 0; i < count; i++) {
Attribute* attribute = fAttributes.ItemAt(i);
const char* label = attribute->name;
if (attribute->attribute == kNameAttribute)
label = B_TRANSLATE("Name");
while ((window = (TPeopleWindow *)WindowAt(index++))) {
if (window->FindView("PeopleView") != NULL && window->fRef && *window->fRef == ref)
return window;
window->AddAttribute(label, attribute->attribute);
}
}
TPeopleWindow*
TPeopleApp::_FindWindow(const entry_ref& ref) const
{
for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
TPeopleWindow* personWindow = dynamic_cast<TPeopleWindow*>(window);
if (personWindow == NULL)
continue;
if (personWindow->RefersPersonFile(ref))
return personWindow;
}
return NULL;
}
void
TPeopleApp::SavePreferences(BMessage* message)
TPeopleApp::_SavePreferences(BMessage* message) const
{
BRect frame;
if (message->FindRect("frame", &frame) != B_OK)
@ -276,7 +339,7 @@ TPeopleApp::SavePreferences(BMessage* message)
BPoint leftTop = frame.LeftTop();
if (fPrefs) {
if (fPrefs != NULL) {
fPrefs->Seek(0, 0);
fPrefs->Write(&leftTop, sizeof(BPoint));
}

View File

@ -1,51 +1,39 @@
//--------------------------------------------------------------------
//
// PeopleApp.h
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef PEOPLEAPP_H
#define PEOPLEAPP_H
* Copyright 2005-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
* Stephan Aßmus <superstippi@gmx.de>
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#ifndef PEOPLE_APP_H
#define PEOPLE_APP_H
#include <Application.h>
class BFile;
#include <ObjectList.h>
#include <String.h>
#define B_PERSON_MIMETYPE "application/x-person"
#define APP_SIG "application/x-vnd.Be-PEPL"
struct people_field {
const char* attribute;
int32 width;
const char* name;
};
extern people_field gFields[];
enum messages{
M_NEW = 128, M_SAVE, M_SAVE_AS, M_REVERT,
M_UNDO, M_SELECT, M_GROUP_MENU, M_WINDOW_QUITS
};
class BFile;
enum fields {
F_NAME = 0, F_NICKNAME, F_COMPANY, F_ADDRESS,
F_CITY, F_STATE, F_ZIP, F_COUNTRY, F_HPHONE,
F_WPHONE, F_FAX, F_EMAIL, F_URL, F_GROUP, F_END
enum {
M_NEW = 'newp',
M_SAVE_AS = 'svas',
M_WINDOW_QUITS = 'wndq'
};
class TPeopleWindow;
//====================================================================
class TPeopleApp : public BApplication {
public:
public:
TPeopleApp();
virtual ~TPeopleApp();
@ -54,17 +42,26 @@ class TPeopleApp : public BApplication {
virtual void MessageReceived(BMessage*);
virtual void RefsReceived(BMessage*);
virtual void ReadyToRun();
TPeopleWindow* FindWindow(entry_ref);
TPeopleWindow* NewWindow(entry_ref* = NULL);
private:
TPeopleWindow* _FindWindow(const entry_ref&) const;
TPeopleWindow* _NewWindow(entry_ref* = NULL);
void _AddAttributes(TPeopleWindow* window) const;
void _SavePreferences(BMessage* message) const;
private:
BFile* fPrefs;
private:
void SavePreferences(BMessage* message);
uint32 fWindowCount;
BRect fPosition;
struct Attribute {
BString attribute;
int32 width;
BString name;
};
BObjectList<Attribute> fAttributes;
};
#endif /* PEOPLEAPP_H */
#endif // PEOPLE_APP_H

View File

@ -1,14 +1,15 @@
//--------------------------------------------------------------------
//
// PeopleView.cpp
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
* Copyright 2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
* Stephan Aßmus <superstippi@gmx.de>
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#include "PeopleView.h"
@ -29,88 +30,77 @@
#include <VolumeRoster.h>
#include <Window.h>
#include "TTextControl.h"
#include "AttributeTextControl.h"
#undef B_TRANSLATE_CONTEXT
#define B_TRANSLATE_CONTEXT "People"
TPeopleView::TPeopleView(const char* name, entry_ref *ref)
TPeopleView::TPeopleView(const char* name, const char* categoryAttribute,
const entry_ref *ref)
:
BGridView()
BGridView(),
fControls(20, false),
fCategoryAttribute(categoryAttribute)
{
SetName(name);
if (ref)
fFile = new BFile(ref, O_RDWR);
else
fFile = NULL;
float spacing = be_control_look->DefaultItemSpacing();
GridLayout()->SetInsets(spacing, spacing, spacing, spacing);
}
TPeopleView::~TPeopleView(void)
TPeopleView::~TPeopleView()
{
delete fFile;
}
void
TPeopleView::AttachedToWindow(void)
TPeopleView::AddAttribute(const char* label, const char* attribute)
{
// TODO: We could check if this attribute has already been added.
AttributeTextControl* control = new AttributeTextControl(label, attribute);
fControls.AddItem(control);
BGridLayout* layout = GridLayout();
int32 row = layout->CountRows();
int32 row = 0;
for (int32 i = 0; gFields[i].attribute; i++, row++) {
const char *name = gFields[i].name;
if (fCategoryAttribute == attribute) {
// Special case the category attribute. The Group popup field will
// be added as the label instead.
fGroups = new BPopUpMenu(label);
fGroups->SetRadioMode(false);
BuildGroupMenu();
if (i == F_NAME)
name = B_TRANSLATE("Name");
BMenuField* field = new BMenuField("", "", fGroups);
field->SetEnabled(true);
layout->AddView(field, 0, row);
char *text = NULL;
attr_info info;
if (fFile && fFile->GetAttrInfo(gFields[i].attribute, &info) == B_OK) {
text = (char *)calloc(info.size, 1);
fFile->ReadAttr(gFields[i].attribute, B_STRING_TYPE,
0, text, info.size);
}
fField[i] = new TTextControl(name, text);
free(text);
int32 labelColumn = 0;
int32 textViewColumn = 1;
int32 textViewWidth = 3;
if (i == F_STATE)
textViewWidth = 1;
else if (i == F_ZIP) {
row--;
labelColumn = 2;
textViewColumn = 3;
textViewWidth = 1;
}
if (i != F_GROUP) {
layout->AddItem(fField[i]->CreateLabelLayoutItem(),
labelColumn, row, 1, 1);
layout->AddItem(fField[i]->CreateTextViewLayoutItem(),
textViewColumn, row, textViewWidth, 1);
} else {
fField[i]->SetLabel("");
layout->AddView(fField[i], textViewColumn, row, textViewWidth, 1);
}
control->SetLabel("");
layout->AddView(control, 1, row);
} else {
layout->AddItem(control->CreateLabelLayoutItem(), 0, row);
layout->AddItem(control->CreateTextViewLayoutItem(), 1, row);
}
fGroups = new BPopUpMenu(gFields[F_GROUP].name);
fGroups->SetRadioMode(false);
BMenuField *field = new BMenuField("", "", fGroups);
field->SetEnabled(true);
layout->AddView(field, 0, --row);
SetAttribute(attribute, true);
}
float spacing = be_control_look->DefaultItemSpacing();
layout->SetSpacing(spacing, spacing);
// TODO: remove this after #5614 is fixed
layout->SetInsets(spacing, spacing, spacing, spacing);
fField[F_NAME]->MakeFocus();
void
TPeopleView::MakeFocus(bool focus)
{
if (focus && fControls.CountItems() > 0)
fControls.ItemAt(0)->MakeFocus();
else
BView::MakeFocus(focus);
}
@ -123,25 +113,34 @@ TPeopleView::MessageReceived(BMessage* msg)
break;
case M_REVERT:
for (int32 loop = 0; loop < F_END; loop++)
fField[loop]->Revert();
for (int32 i = fControls.CountItems() - 1; i >= 0; i--)
fControls.ItemAt(i)->Revert();
break;
case M_SELECT:
for (int32 loop = 0; loop < F_END; loop++) {
BTextView* text = fField[loop]->TextView();
for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
BTextView* text = fControls.ItemAt(i)->TextView();
if (text->IsFocus()) {
text->Select(0, text->TextLength());
break;
}
}
break;
break;
case M_GROUP_MENU:
{
const char* name = NULL;
if (msg->FindString("group", &name) == B_OK)
SetAttribute(fCategoryAttribute, name, false);
break;
}
}
}
void
TPeopleView::BuildGroupMenu(void)
TPeopleView::BuildGroupMenu()
{
BMenuItem* item;
while ((item = fGroups->ItemAt(0)) != NULL) {
@ -149,7 +148,6 @@ TPeopleView::BuildGroupMenu(void)
delete item;
}
const char *groupAttribute = gFields[F_GROUP].attribute;
int32 count = 0;
BVolumeRoster volumeRoster;
@ -159,7 +157,7 @@ TPeopleView::BuildGroupMenu(void)
query.SetVolume(&volume);
char buffer[256];
sprintf(buffer, "%s=*", groupAttribute);
snprintf(buffer, sizeof(buffer), "%s=*", fCategoryAttribute.String());
query.SetPredicate(buffer);
query.Fetch();
@ -169,13 +167,15 @@ TPeopleView::BuildGroupMenu(void)
attr_info info;
if (file.InitCheck() == B_OK
&& file.GetAttrInfo(groupAttribute, &info) == B_OK
&& file.GetAttrInfo(fCategoryAttribute, &info) == B_OK
&& info.size > 1) {
if (info.size > sizeof(buffer))
info.size = sizeof(buffer);
if (file.ReadAttr(groupAttribute, B_STRING_TYPE, 0, buffer, info.size) < B_OK)
if (file.ReadAttr(fCategoryAttribute.String(), B_STRING_TYPE,
0, buffer, info.size) < 0) {
continue;
}
const char *text = buffer;
while (true) {
@ -207,39 +207,19 @@ TPeopleView::BuildGroupMenu(void)
}
}
if (!count) {
if (count == 0) {
fGroups->AddItem(item = new BMenuItem(
B_TRANSLATE_WITH_CONTEXT("none", "Groups list"),
new BMessage(M_GROUP_MENU)));
item->SetEnabled(false);
}
}
bool
TPeopleView::CheckSave(void)
{
for (int32 loop = 0; loop < F_END; loop++) {
if (fField[loop]->Changed())
return true;
}
return false;
}
const char*
TPeopleView::GetField(int32 index)
{
if (index < F_END)
return fField[index]->Text();
return NULL;
fGroups->SetTargetForItems(this);
}
void
TPeopleView::NewFile(entry_ref *ref)
TPeopleView::CreateFile(const entry_ref* ref)
{
delete fFile;
fFile = new BFile(ref, B_READ_WRITE);
@ -247,68 +227,107 @@ TPeopleView::NewFile(entry_ref *ref)
}
void
TPeopleView::Save(void)
bool
TPeopleView::IsSaved() const
{
for (int32 i = 0; gFields[i].attribute; i++) {
const char *text = fField[i]->Text();
fFile->WriteAttr(gFields[i].attribute, B_STRING_TYPE, 0, text, strlen(text) + 1);
fField[i]->Update();
for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
if (fControls.ItemAt(i)->HasChanged())
return false;
}
return true;
}
void
TPeopleView::SetField(int32 index, bool update)
TPeopleView::Save()
{
char *text = NULL;
attr_info info;
if (fFile && fFile->GetAttrInfo(gFields[index].attribute, &info) == B_OK) {
text = (char *)calloc(info.size, 1);
fFile->ReadAttr(gFields[index].attribute, B_STRING_TYPE, 0, text,
info.size);
int32 count = fControls.CountItems();
for (int32 i = 0; i < count; i++) {
AttributeTextControl* control = fControls.ItemAt(i);
const char* value = control->Text();
fFile->WriteAttr(control->Attribute().String(), B_STRING_TYPE, 0,
value, strlen(value) + 1);
control->Update();
}
}
SetField(index, text, update);
const char*
TPeopleView::AttributeValue(const char* attribute) const
{
for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
if (fControls.ItemAt(i)->Attribute() == attribute)
return fControls.ItemAt(i)->Text();
}
free(text);
return "";
}
void
TPeopleView::SetField(int32 index, char *data, bool update)
TPeopleView::SetAttribute(const char* attribute, bool update)
{
Window()->Lock();
char* value = NULL;
attr_info info;
if (fFile != NULL && fFile->GetAttrInfo(attribute, &info) == B_OK) {
value = (char*)calloc(info.size, 1);
fFile->ReadAttr(attribute, B_STRING_TYPE, 0, value, info.size);
}
SetAttribute(attribute, value, update);
free(value);
}
void
TPeopleView::SetAttribute(const char* attribute, const char* value,
bool update)
{
if (!LockLooper())
return;
AttributeTextControl* control = NULL;
for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
if (fControls.ItemAt(i)->Attribute() == attribute) {
control = fControls.ItemAt(i);
break;
}
}
if (control == NULL)
return;
if (update) {
fField[index]->SetText(data);
fField[index]->Update();
control->SetText(value);
control->Update();
} else {
BTextView* text = fField[index]->TextView();
BTextView* text = control->TextView();
int32 start, end;
text->GetSelection(&start, &end);
if (start != end) {
text->Delete();
text->Insert(data);
text->Insert(value);
} else if ((end = text->TextLength())) {
text->Select(end, end);
text->Insert(",");
text->Insert(data);
text->Insert(value);
text->Select(text->TextLength(), text->TextLength());
} else
fField[index]->SetText(data);
control->SetText(value);
}
Window()->Unlock();
UnlockLooper();
}
bool
TPeopleView::TextSelected(void)
TPeopleView::IsTextSelected() const
{
for (int32 loop = 0; loop < F_END; loop++) {
BTextView* text = fField[loop]->TextView();
for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
BTextView* text = fControls.ItemAt(i)->TextView();
int32 start, end;
text->GetSelection(&start, &end);

View File

@ -1,48 +1,69 @@
//--------------------------------------------------------------------
//
// PeopleView.h
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
* Copyright 2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
* Stephan Aßmus <superstippi@gmx.de>
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#ifndef PEOPLE_VIEW_H
#define PEOPLE_VIEW_H
#ifndef PEOPLEVIEW_H
#define PEOPLEVIEW_H
#include "PeopleApp.h"
#include <GridView.h>
#include <ObjectList.h>
#include <String.h>
class AttributeTextControl;
class BFile;
class BPopUpMenu;
class TTextControl;
enum {
M_SAVE = 'save',
M_REVERT = 'rvrt',
M_SELECT = 'slct',
M_GROUP_MENU = 'grmn',
};
class TPeopleView : public BGridView {
public:
TPeopleView(const char* name, entry_ref* ref);
~TPeopleView(void);
public:
TPeopleView(const char* name,
const char* categoryAttribute,
const entry_ref* ref);
virtual ~TPeopleView();
virtual void AttachedToWindow(void);
virtual void MessageReceived(BMessage*);
void BuildGroupMenu(void);
bool CheckSave(void);
const char* GetField(int32);
void NewFile(entry_ref*);
void Save(void);
void SetField(int32, bool);
void SetField(int32, char*, bool);
bool TextSelected(void);
virtual void MakeFocus(bool focus = true);
virtual void MessageReceived(BMessage* message);
private:
BFile *fFile;
BPopUpMenu *fGroups;
TTextControl *fField[F_END];
void AddAttribute(const char* label,
const char* attribute);
void BuildGroupMenu();
void CreateFile(const entry_ref* ref);
bool IsSaved() const;
void Save();
const char* AttributeValue(const char* attribute) const;
void SetAttribute(const char* attribute, bool update);
void SetAttribute(const char* attribute,
const char* value, bool update);
bool IsTextSelected() const;
private:
BFile* fFile;
BPopUpMenu* fGroups;
typedef BObjectList<AttributeTextControl> AttributeList;
AttributeList fControls;
BString fCategoryAttribute;
};
#endif /* PEOPLEVIEW_H */
#endif // PEOPLE_VIEW_H

View File

@ -1,14 +1,14 @@
//--------------------------------------------------------------------
//
// PeopleWindow.cpp
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
* Copyright 2005-2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
* Stephan Aßmus <superstippi@gmx.de>
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#include "PeopleWindow.h"
@ -41,11 +41,14 @@
#define B_TRANSLATE_CONTEXT "People"
TPeopleWindow::TPeopleWindow(BRect frame, const char *title, entry_ref *ref)
TPeopleWindow::TPeopleWindow(BRect frame, const char* title,
const char* nameAttribute, const char* categoryAttribute,
const entry_ref* ref)
:
BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
| B_AUTO_UPDATE_SIZE_LIMITS),
fPanel(NULL)
fPanel(NULL),
fNameAttribute(nameAttribute)
{
BMenu* menu;
BMenuItem* item;
@ -55,7 +58,7 @@ TPeopleWindow::TPeopleWindow(BRect frame, const char *title, entry_ref *ref)
menu->AddItem(item = new BMenuItem(
B_TRANSLATE("New person" B_UTF8_ELLIPSIS),
new BMessage(M_NEW), 'N'));
item->SetTarget(NULL, be_app);
item->SetTarget(be_app);
menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
new BMessage(B_QUIT_REQUESTED), 'W'));
menu->AddSeparatorItem();
@ -71,66 +74,59 @@ TPeopleWindow::TPeopleWindow(BRect frame, const char *title, entry_ref *ref)
menu->AddSeparatorItem();
item = new BMenuItem(B_TRANSLATE("Quit"),
new BMessage(B_QUIT_REQUESTED), 'Q');
item->SetTarget(NULL, be_app);
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->SetTarget(NULL, this);
fUndo->SetEnabled(false);
menu->AddSeparatorItem();
menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"),
new BMessage(B_CUT), 'X'));
fCut->SetTarget(NULL, this);
menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"),
new BMessage(B_COPY), 'C'));
fCopy->SetTarget(NULL, this);
menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"),
new BMessage(B_PASTE), 'V'));
fPaste->SetTarget(NULL, this);
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(M_SELECT), 'A'));
item->SetTarget(NULL, this);
BMenuItem* selectAllItem = new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(M_SELECT), 'A');
menu->AddItem(selectAllItem);
menuBar->AddItem(menu);
if (ref) {
fRef = new entry_ref(*ref);
if (ref != NULL) {
SetTitle(ref->name);
WatchChanges(true);
_SetToRef(new entry_ref(*ref));
} else
fRef = NULL;
_SetToRef(NULL);
fView = new TPeopleView("PeopleView", fRef);
fView = new TPeopleView("PeopleView", categoryAttribute, fRef);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(0, 0, 0, 0)
.Add(menuBar)
.Add(fView);
fRevert->SetTarget(fView);
selectAllItem->SetTarget(fView);
}
TPeopleWindow::~TPeopleWindow(void)
TPeopleWindow::~TPeopleWindow()
{
if (fRef)
WatchChanges(false);
delete fRef;
_SetToRef(NULL);
}
void
TPeopleWindow::MenusBeginning(void)
TPeopleWindow::MenusBeginning()
{
bool enabled;
bool isRedo;
enabled = fView->CheckSave();
bool enabled = !fView->IsSaved();
fSave->SetEnabled(enabled);
fRevert->SetEnabled(enabled);
undo_state state = ((BTextView *)CurrentFocus())->UndoState(&isRedo);
bool isRedo = false;
undo_state state = ((BTextView*)CurrentFocus())->UndoState(&isRedo);
fUndo->SetEnabled(state != B_UNDO_UNAVAILABLE);
if (isRedo)
@ -138,7 +134,7 @@ TPeopleWindow::MenusBeginning(void)
else
fUndo->SetLabel(B_TRANSLATE("Undo"));
enabled = fView->TextSelected();
enabled = fView->IsTextSelected();
fCut->SetEnabled(enabled);
fCopy->SetEnabled(enabled);
@ -154,7 +150,6 @@ void
TPeopleWindow::MessageReceived(BMessage* msg)
{
char str[256];
entry_ref dir;
BDirectory directory;
BEntry entry;
BFile file;
@ -166,6 +161,7 @@ TPeopleWindow::MessageReceived(BMessage* msg)
SaveAs();
break;
}
// supposed to fall through
case M_REVERT:
case M_SELECT:
fView->MessageReceived(msg);
@ -175,16 +171,11 @@ TPeopleWindow::MessageReceived(BMessage* msg)
SaveAs();
break;
case M_GROUP_MENU:
{
char *name = NULL;
msg->FindString("group", (const char **)&name);
fView->SetField(F_GROUP, name, FALSE);
break;
}
case B_SAVE_REQUESTED:
if (msg->FindRef("directory", &dir) == B_NO_ERROR) {
const char *name = NULL;
{
entry_ref dir;
if (msg->FindRef("directory", &dir) == B_OK) {
const char* name = NULL;
msg->FindString("name", &name);
directory.SetTo(&dir);
if (directory.InitCheck() == B_NO_ERROR) {
@ -196,14 +187,9 @@ TPeopleWindow::MessageReceived(BMessage* msg)
directory.FindEntry(name, &entry);
entry.GetRef(&dir);
if (fRef) {
WatchChanges(false);
delete fRef;
}
fRef = new entry_ref(dir);
WatchChanges(true);
_SetToRef(new entry_ref(dir));
SetTitle(fRef->name);
fView->NewFile(fRef);
fView->CreateFile(fRef);
}
else {
sprintf(str, B_TRANSLATE("Could not create %s."), name);
@ -212,18 +198,15 @@ TPeopleWindow::MessageReceived(BMessage* msg)
}
}
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 by quiting its
// looper.
delete fRef;
fRef = NULL;
// We lost our file. Close the window.
PostMessage(B_QUIT_REQUESTED);
break;
@ -244,7 +227,6 @@ TPeopleWindow::MessageReceived(BMessage* msg)
delete fRef;
fRef = new entry_ref(device, directory, name.String());
// And our window title.
SetTitle(name);
@ -255,11 +237,8 @@ TPeopleWindow::MessageReceived(BMessage* msg)
&volume);
BPath folder(fRef);
folder.GetParent(&folder);
if (folder == trash) {
delete fRef;
fRef = NULL;
if (folder == trash)
PostMessage(B_QUIT_REQUESTED);
}
break;
}
@ -268,13 +247,8 @@ TPeopleWindow::MessageReceived(BMessage* msg)
{
// An attribute was updated.
BString attr;
if (msg->FindString("attr", &attr) == B_OK) {
for (int32 i = 0; gFields[i].attribute; i++) {
if (attr == gFields[i].attribute) {
fView->SetField(i, true);
}
}
}
if (msg->FindString("attr", &attr) == B_OK)
fView->SetAttribute(attr.String(), true);
break;
}
}
@ -289,11 +263,11 @@ TPeopleWindow::MessageReceived(BMessage* msg)
bool
TPeopleWindow::QuitRequested(void)
TPeopleWindow::QuitRequested()
{
status_t result;
if (fView->CheckSave()) {
if (!fView->IsSaved()) {
result = (new BAlert("", B_TRANSLATE("Save changes before quitting?"),
B_TRANSLATE("Cancel"), B_TRANSLATE("Quit"),
B_TRANSLATE("Save")))->Go();
@ -322,9 +296,68 @@ TPeopleWindow::QuitRequested(void)
void
TPeopleWindow::DefaultName(char *name)
TPeopleWindow::Show()
{
strncpy(name, fView->GetField(F_NAME), B_FILE_NAME_LENGTH);
fView->MakeFocus();
BWindow::Show();
}
void
TPeopleWindow::AddAttribute(const char* label, const char* attribute)
{
fView->AddAttribute(label, attribute);
}
void
TPeopleWindow::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
TPeopleWindow::RefersPersonFile(const entry_ref& ref) const
{
if (fRef == NULL)
return false;
return *fRef == ref;
}
void
TPeopleWindow::_GetDefaultFileName(char* name)
{
strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH);
while (*name) {
if (*name == '/')
*name = '-';
@ -334,52 +367,21 @@ TPeopleWindow::DefaultName(char *name)
void
TPeopleWindow::SetField(int32 index, char *text)
TPeopleWindow::_SetToRef(entry_ref* ref)
{
fView->SetField(index, text, true);
if (fRef != NULL) {
_WatchChanges(false);
delete fRef;
}
fRef = ref;
_WatchChanges(true);
}
void
TPeopleWindow::SaveAs(void)
{
char name[B_FILE_NAME_LENGTH];
BDirectory dir;
BEntry entry;
BMessenger window(this);
BPath path;
DefaultName(name);
if (!fPanel) {
fPanel = new BFilePanel(B_SAVE_PANEL, &window);
fPanel->SetSaveText(name);
find_directory(B_USER_DIRECTORY, &path, true);
dir.SetTo(path.Path());
if (dir.FindEntry("people", &entry) == B_NO_ERROR)
fPanel->SetPanelDirectory(&entry);
else if (dir.CreateDirectory("people", &dir) == B_NO_ERROR) {
dir.GetEntry(&entry);
fPanel->SetPanelDirectory(&entry);
}
}
else if (fPanel->Window()->Lock()) {
if (!fPanel->Window()->IsHidden())
fPanel->Window()->Activate();
else
fPanel->SetSaveText(name);
fPanel->Window()->Unlock();
}
if (fPanel->Window()->Lock()) {
if (fPanel->Window()->IsHidden())
fPanel->Window()->Show();
fPanel->Window()->Unlock();
}
}
void
TPeopleWindow::WatchChanges(bool enable)
TPeopleWindow::_WatchChanges(bool enable)
{
if (fRef == NULL)
return;

View File

@ -1,56 +1,71 @@
//--------------------------------------------------------------------
//
// PeopleWindow.h
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
* Copyright 2010, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Robert Polic
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#ifndef PEOPLE_WINDOW_H
#define PEOPLE_WINDOW_H
#include <String.h>
#include <Window.h>
#ifndef PEOPLEWINDOW_H
#define PEOPLEWINDOW_H
#define TITLE_BAR_HEIGHT 25
#define WIND_WIDTH 321
#define WIND_HEIGHT 340
class TPeopleView;
class BFilePanel;
class BMenuItem;
#include <Window.h>
//====================================================================
class TPeopleWindow : public BWindow {
private:
void WatchChanges(bool);
BFilePanel *fPanel;
BMenuItem *fCopy;
BMenuItem *fCut;
BMenuItem *fPaste;
BMenuItem *fRevert;
BMenuItem *fSave;
BMenuItem *fUndo;
TPeopleView *fView;
public:
entry_ref *fRef;
TPeopleWindow(BRect frame, const char* title,
const char* nameAttribute,
const char* categoryAttribute,
const entry_ref* ref);
virtual ~TPeopleWindow();
TPeopleWindow(BRect, const char*, entry_ref*);
~TPeopleWindow(void);
virtual void MenusBeginning(void);
virtual void MessageReceived(BMessage*);
virtual bool QuitRequested(void);
void DefaultName(char*);
void SetField(int32, char*);
void SaveAs(void);
virtual void MenusBeginning();
virtual void MessageReceived(BMessage* message);
virtual bool QuitRequested();
virtual void Show();
void AddAttribute(const char* label,
const char* attribute);
void SaveAs();
bool RefersPersonFile(const entry_ref& ref) const;
private:
void _GetDefaultFileName(char* name);
void _SetToRef(entry_ref* ref);
void _WatchChanges(bool doIt);
private:
entry_ref* fRef;
BFilePanel* fPanel;
BMenuItem* fCopy;
BMenuItem* fCut;
BMenuItem* fPaste;
BMenuItem* fRevert;
BMenuItem* fSave;
BMenuItem* fUndo;
TPeopleView* fView;
BString fNameAttribute;
};
#endif /* PEOPLEWINDOW_H */
#endif // PEOPLE_WINDOW_H

View File

@ -1,66 +0,0 @@
//--------------------------------------------------------------------
//
// TTextControl.cpp
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <string.h>
#include <malloc.h>
#include <Font.h>
#include "TTextControl.h"
TTextControl::TTextControl(const char *label, const char *text)
:
BTextControl(NULL, text, NULL)
{
if (label && label[0])
SetLabel(BString(label).Append(":"));
SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
fOriginal = text;
}
TTextControl::~TTextControl()
{
}
void
TTextControl::AttachedToWindow()
{
BTextControl::AttachedToWindow();
BTextView* text = (BTextView *)ChildAt(0);
text->SetMaxBytes(255);
}
bool
TTextControl::Changed()
{
return fOriginal != Text();
}
void
TTextControl::Revert(void)
{
if (Changed())
SetText(fOriginal);
}
void
TTextControl::Update(void)
{
fOriginal = Text();
}

View File

@ -1,34 +0,0 @@
//--------------------------------------------------------------------
//
// TTextControl.h
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef TEXTCONTROL_H
#define TEXTCONTROL_H
#include <String.h>
#include <TextControl.h>
class TTextControl : public BTextControl {
public:
TTextControl(const char* label, const char* text);
virtual ~TTextControl();
virtual void AttachedToWindow();
bool Changed();
void Revert();
void Update();
private:
BString fOriginal;
};
#endif /* TEXTCONTROL_H */

View File

@ -1,24 +1,19 @@
//--------------------------------------------------------------------
//
// main.cpp
//
// Written by: Robert Polic
//
//--------------------------------------------------------------------
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*
* Authors:
* Robert Polic
*/
#include "PeopleApp.h"
int main(void)
int
main(int, char**)
{
TPeopleApp *app;
app = new TPeopleApp();
app->Run();
delete app;
TPeopleApp app;
app.Run();
return B_NO_ERROR;
}