xref: /haiku/src/apps/people/PersonView.cpp (revision 968ec77e1f783265bc393eae565c5166c5a13bd8)
1*968ec77eSStephan Aßmus /*
2*968ec77eSStephan Aßmus  * Copyright 2010, Haiku, Inc. All rights reserved.
3*968ec77eSStephan Aßmus  * Distributed under the terms of the MIT license.
4*968ec77eSStephan Aßmus  *
5*968ec77eSStephan Aßmus  * Authors:
6*968ec77eSStephan Aßmus  *		Robert Polic
7*968ec77eSStephan Aßmus  *		Stephan Aßmus <superstippi@gmx.de>
8*968ec77eSStephan Aßmus  *
9*968ec77eSStephan Aßmus  * Copyright 1999, Be Incorporated.   All Rights Reserved.
10*968ec77eSStephan Aßmus  * This file may be used under the terms of the Be Sample Code License.
11*968ec77eSStephan Aßmus  */
12*968ec77eSStephan Aßmus 
13*968ec77eSStephan Aßmus 
14*968ec77eSStephan Aßmus #include "PersonView.h"
15*968ec77eSStephan Aßmus 
16*968ec77eSStephan Aßmus #include <stdio.h>
17*968ec77eSStephan Aßmus #include <stdlib.h>
18*968ec77eSStephan Aßmus #include <string.h>
19*968ec77eSStephan Aßmus 
20*968ec77eSStephan Aßmus #include <Catalog.h>
21*968ec77eSStephan Aßmus #include <fs_attr.h>
22*968ec77eSStephan Aßmus #include <Box.h>
23*968ec77eSStephan Aßmus #include <ControlLook.h>
24*968ec77eSStephan Aßmus #include <GridLayout.h>
25*968ec77eSStephan Aßmus #include <Locale.h>
26*968ec77eSStephan Aßmus #include <MenuField.h>
27*968ec77eSStephan Aßmus #include <MenuItem.h>
28*968ec77eSStephan Aßmus #include <PopUpMenu.h>
29*968ec77eSStephan Aßmus #include <Query.h>
30*968ec77eSStephan Aßmus #include <VolumeRoster.h>
31*968ec77eSStephan Aßmus #include <Window.h>
32*968ec77eSStephan Aßmus 
33*968ec77eSStephan Aßmus #include "AttributeTextControl.h"
34*968ec77eSStephan Aßmus 
35*968ec77eSStephan Aßmus 
36*968ec77eSStephan Aßmus #undef B_TRANSLATE_CONTEXT
37*968ec77eSStephan Aßmus #define B_TRANSLATE_CONTEXT "People"
38*968ec77eSStephan Aßmus 
39*968ec77eSStephan Aßmus 
40*968ec77eSStephan Aßmus PersonView::PersonView(const char* name, const char* categoryAttribute,
41*968ec77eSStephan Aßmus 		const entry_ref *ref)
42*968ec77eSStephan Aßmus 	:
43*968ec77eSStephan Aßmus 	BGridView(),
44*968ec77eSStephan Aßmus 	fControls(20, false),
45*968ec77eSStephan Aßmus 	fCategoryAttribute(categoryAttribute)
46*968ec77eSStephan Aßmus {
47*968ec77eSStephan Aßmus 	SetName(name);
48*968ec77eSStephan Aßmus 	if (ref)
49*968ec77eSStephan Aßmus 		fFile = new BFile(ref, O_RDWR);
50*968ec77eSStephan Aßmus 	else
51*968ec77eSStephan Aßmus 		fFile = NULL;
52*968ec77eSStephan Aßmus 
53*968ec77eSStephan Aßmus 	float spacing = be_control_look->DefaultItemSpacing();
54*968ec77eSStephan Aßmus 	GridLayout()->SetInsets(spacing, spacing, spacing, spacing);
55*968ec77eSStephan Aßmus }
56*968ec77eSStephan Aßmus 
57*968ec77eSStephan Aßmus 
58*968ec77eSStephan Aßmus PersonView::~PersonView()
59*968ec77eSStephan Aßmus {
60*968ec77eSStephan Aßmus 	delete fFile;
61*968ec77eSStephan Aßmus }
62*968ec77eSStephan Aßmus 
63*968ec77eSStephan Aßmus 
64*968ec77eSStephan Aßmus void
65*968ec77eSStephan Aßmus PersonView::AddAttribute(const char* label, const char* attribute)
66*968ec77eSStephan Aßmus {
67*968ec77eSStephan Aßmus 	// TODO: We could check if this attribute has already been added.
68*968ec77eSStephan Aßmus 
69*968ec77eSStephan Aßmus 	AttributeTextControl* control = new AttributeTextControl(label, attribute);
70*968ec77eSStephan Aßmus 	fControls.AddItem(control);
71*968ec77eSStephan Aßmus 
72*968ec77eSStephan Aßmus 	BGridLayout* layout = GridLayout();
73*968ec77eSStephan Aßmus 	int32 row = layout->CountRows();
74*968ec77eSStephan Aßmus 
75*968ec77eSStephan Aßmus 	if (fCategoryAttribute == attribute) {
76*968ec77eSStephan Aßmus 		// Special case the category attribute. The Group popup field will
77*968ec77eSStephan Aßmus 		// be added as the label instead.
78*968ec77eSStephan Aßmus 		fGroups = new BPopUpMenu(label);
79*968ec77eSStephan Aßmus 		fGroups->SetRadioMode(false);
80*968ec77eSStephan Aßmus 		BuildGroupMenu();
81*968ec77eSStephan Aßmus 
82*968ec77eSStephan Aßmus 		BMenuField* field = new BMenuField("", "", fGroups);
83*968ec77eSStephan Aßmus 		field->SetEnabled(true);
84*968ec77eSStephan Aßmus 		layout->AddView(field, 0, row);
85*968ec77eSStephan Aßmus 
86*968ec77eSStephan Aßmus 		control->SetLabel("");
87*968ec77eSStephan Aßmus 		layout->AddView(control, 1, row);
88*968ec77eSStephan Aßmus 	} else {
89*968ec77eSStephan Aßmus 		layout->AddItem(control->CreateLabelLayoutItem(), 0, row);
90*968ec77eSStephan Aßmus 		layout->AddItem(control->CreateTextViewLayoutItem(), 1, row);
91*968ec77eSStephan Aßmus 	}
92*968ec77eSStephan Aßmus 
93*968ec77eSStephan Aßmus 	SetAttribute(attribute, true);
94*968ec77eSStephan Aßmus }
95*968ec77eSStephan Aßmus 
96*968ec77eSStephan Aßmus 
97*968ec77eSStephan Aßmus void
98*968ec77eSStephan Aßmus PersonView::MakeFocus(bool focus)
99*968ec77eSStephan Aßmus {
100*968ec77eSStephan Aßmus 	if (focus && fControls.CountItems() > 0)
101*968ec77eSStephan Aßmus 		fControls.ItemAt(0)->MakeFocus();
102*968ec77eSStephan Aßmus 	else
103*968ec77eSStephan Aßmus 		BView::MakeFocus(focus);
104*968ec77eSStephan Aßmus }
105*968ec77eSStephan Aßmus 
106*968ec77eSStephan Aßmus 
107*968ec77eSStephan Aßmus void
108*968ec77eSStephan Aßmus PersonView::MessageReceived(BMessage* msg)
109*968ec77eSStephan Aßmus {
110*968ec77eSStephan Aßmus 	switch (msg->what) {
111*968ec77eSStephan Aßmus 		case M_SAVE:
112*968ec77eSStephan Aßmus 			Save();
113*968ec77eSStephan Aßmus 			break;
114*968ec77eSStephan Aßmus 
115*968ec77eSStephan Aßmus 		case M_REVERT:
116*968ec77eSStephan Aßmus 			for (int32 i = fControls.CountItems() - 1; i >= 0; i--)
117*968ec77eSStephan Aßmus 				fControls.ItemAt(i)->Revert();
118*968ec77eSStephan Aßmus 			break;
119*968ec77eSStephan Aßmus 
120*968ec77eSStephan Aßmus 		case M_SELECT:
121*968ec77eSStephan Aßmus 			for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
122*968ec77eSStephan Aßmus 				BTextView* text = fControls.ItemAt(i)->TextView();
123*968ec77eSStephan Aßmus 				if (text->IsFocus()) {
124*968ec77eSStephan Aßmus 					text->Select(0, text->TextLength());
125*968ec77eSStephan Aßmus 					break;
126*968ec77eSStephan Aßmus 				}
127*968ec77eSStephan Aßmus 			}
128*968ec77eSStephan Aßmus 			break;
129*968ec77eSStephan Aßmus 
130*968ec77eSStephan Aßmus 		case M_GROUP_MENU:
131*968ec77eSStephan Aßmus 		{
132*968ec77eSStephan Aßmus 			const char* name = NULL;
133*968ec77eSStephan Aßmus 			if (msg->FindString("group", &name) == B_OK)
134*968ec77eSStephan Aßmus 				SetAttribute(fCategoryAttribute, name, false);
135*968ec77eSStephan Aßmus 			break;
136*968ec77eSStephan Aßmus 		}
137*968ec77eSStephan Aßmus 
138*968ec77eSStephan Aßmus 	}
139*968ec77eSStephan Aßmus }
140*968ec77eSStephan Aßmus 
141*968ec77eSStephan Aßmus 
142*968ec77eSStephan Aßmus void
143*968ec77eSStephan Aßmus PersonView::BuildGroupMenu()
144*968ec77eSStephan Aßmus {
145*968ec77eSStephan Aßmus 	BMenuItem* item;
146*968ec77eSStephan Aßmus 	while ((item = fGroups->ItemAt(0)) != NULL) {
147*968ec77eSStephan Aßmus 		fGroups->RemoveItem(item);
148*968ec77eSStephan Aßmus 		delete item;
149*968ec77eSStephan Aßmus 	}
150*968ec77eSStephan Aßmus 
151*968ec77eSStephan Aßmus 	int32 count = 0;
152*968ec77eSStephan Aßmus 
153*968ec77eSStephan Aßmus 	BVolumeRoster volumeRoster;
154*968ec77eSStephan Aßmus 	BVolume volume;
155*968ec77eSStephan Aßmus 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
156*968ec77eSStephan Aßmus 		BQuery query;
157*968ec77eSStephan Aßmus 		query.SetVolume(&volume);
158*968ec77eSStephan Aßmus 
159*968ec77eSStephan Aßmus 		char buffer[256];
160*968ec77eSStephan Aßmus 		snprintf(buffer, sizeof(buffer), "%s=*", fCategoryAttribute.String());
161*968ec77eSStephan Aßmus 		query.SetPredicate(buffer);
162*968ec77eSStephan Aßmus 		query.Fetch();
163*968ec77eSStephan Aßmus 
164*968ec77eSStephan Aßmus 		BEntry entry;
165*968ec77eSStephan Aßmus 		while (query.GetNextEntry(&entry) == B_OK) {
166*968ec77eSStephan Aßmus 			BFile file(&entry, B_READ_ONLY);
167*968ec77eSStephan Aßmus 			attr_info info;
168*968ec77eSStephan Aßmus 
169*968ec77eSStephan Aßmus 			if (file.InitCheck() == B_OK
170*968ec77eSStephan Aßmus 				&& file.GetAttrInfo(fCategoryAttribute, &info) == B_OK
171*968ec77eSStephan Aßmus 				&& info.size > 1) {
172*968ec77eSStephan Aßmus 				if (info.size > sizeof(buffer))
173*968ec77eSStephan Aßmus 					info.size = sizeof(buffer);
174*968ec77eSStephan Aßmus 
175*968ec77eSStephan Aßmus 				if (file.ReadAttr(fCategoryAttribute.String(), B_STRING_TYPE,
176*968ec77eSStephan Aßmus 						0, buffer, info.size) < 0) {
177*968ec77eSStephan Aßmus 					continue;
178*968ec77eSStephan Aßmus 				}
179*968ec77eSStephan Aßmus 
180*968ec77eSStephan Aßmus 				const char *text = buffer;
181*968ec77eSStephan Aßmus 				while (true) {
182*968ec77eSStephan Aßmus 					char* offset = strstr(text, ",");
183*968ec77eSStephan Aßmus 					if (offset != NULL)
184*968ec77eSStephan Aßmus 						offset[0] = '\0';
185*968ec77eSStephan Aßmus 
186*968ec77eSStephan Aßmus 					if (!fGroups->FindItem(text)) {
187*968ec77eSStephan Aßmus 						int32 index = 0;
188*968ec77eSStephan Aßmus 						while ((item = fGroups->ItemAt(index)) != NULL) {
189*968ec77eSStephan Aßmus 							if (strcmp(text, item->Label()) < 0)
190*968ec77eSStephan Aßmus 								break;
191*968ec77eSStephan Aßmus 							index++;
192*968ec77eSStephan Aßmus 						}
193*968ec77eSStephan Aßmus 						BMessage* message = new BMessage(M_GROUP_MENU);
194*968ec77eSStephan Aßmus 						message->AddString("group", text);
195*968ec77eSStephan Aßmus 						fGroups->AddItem(new BMenuItem(text, message), index);
196*968ec77eSStephan Aßmus 						count++;
197*968ec77eSStephan Aßmus 					}
198*968ec77eSStephan Aßmus 					if (offset) {
199*968ec77eSStephan Aßmus 						text = offset + 1;
200*968ec77eSStephan Aßmus 						while (*text == ' ')
201*968ec77eSStephan Aßmus 							text++;
202*968ec77eSStephan Aßmus 					}
203*968ec77eSStephan Aßmus 					else
204*968ec77eSStephan Aßmus 						break;
205*968ec77eSStephan Aßmus 				}
206*968ec77eSStephan Aßmus 			}
207*968ec77eSStephan Aßmus 		}
208*968ec77eSStephan Aßmus 	}
209*968ec77eSStephan Aßmus 
210*968ec77eSStephan Aßmus 	if (count == 0) {
211*968ec77eSStephan Aßmus 		fGroups->AddItem(item = new BMenuItem(
212*968ec77eSStephan Aßmus 			B_TRANSLATE_WITH_CONTEXT("none", "Groups list"),
213*968ec77eSStephan Aßmus 			new BMessage(M_GROUP_MENU)));
214*968ec77eSStephan Aßmus 		item->SetEnabled(false);
215*968ec77eSStephan Aßmus 	}
216*968ec77eSStephan Aßmus 
217*968ec77eSStephan Aßmus 	fGroups->SetTargetForItems(this);
218*968ec77eSStephan Aßmus }
219*968ec77eSStephan Aßmus 
220*968ec77eSStephan Aßmus 
221*968ec77eSStephan Aßmus void
222*968ec77eSStephan Aßmus PersonView::CreateFile(const entry_ref* ref)
223*968ec77eSStephan Aßmus {
224*968ec77eSStephan Aßmus 	delete fFile;
225*968ec77eSStephan Aßmus 	fFile = new BFile(ref, B_READ_WRITE);
226*968ec77eSStephan Aßmus 	Save();
227*968ec77eSStephan Aßmus }
228*968ec77eSStephan Aßmus 
229*968ec77eSStephan Aßmus 
230*968ec77eSStephan Aßmus bool
231*968ec77eSStephan Aßmus PersonView::IsSaved() const
232*968ec77eSStephan Aßmus {
233*968ec77eSStephan Aßmus 	for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
234*968ec77eSStephan Aßmus 		if (fControls.ItemAt(i)->HasChanged())
235*968ec77eSStephan Aßmus 			return false;
236*968ec77eSStephan Aßmus 	}
237*968ec77eSStephan Aßmus 
238*968ec77eSStephan Aßmus 	return true;
239*968ec77eSStephan Aßmus }
240*968ec77eSStephan Aßmus 
241*968ec77eSStephan Aßmus 
242*968ec77eSStephan Aßmus void
243*968ec77eSStephan Aßmus PersonView::Save()
244*968ec77eSStephan Aßmus {
245*968ec77eSStephan Aßmus 	int32 count = fControls.CountItems();
246*968ec77eSStephan Aßmus 	for (int32 i = 0; i < count; i++) {
247*968ec77eSStephan Aßmus 		AttributeTextControl* control = fControls.ItemAt(i);
248*968ec77eSStephan Aßmus 		const char* value = control->Text();
249*968ec77eSStephan Aßmus 		fFile->WriteAttr(control->Attribute().String(), B_STRING_TYPE, 0,
250*968ec77eSStephan Aßmus 			value, strlen(value) + 1);
251*968ec77eSStephan Aßmus 		control->Update();
252*968ec77eSStephan Aßmus 	}
253*968ec77eSStephan Aßmus }
254*968ec77eSStephan Aßmus 
255*968ec77eSStephan Aßmus 
256*968ec77eSStephan Aßmus const char*
257*968ec77eSStephan Aßmus PersonView::AttributeValue(const char* attribute) const
258*968ec77eSStephan Aßmus {
259*968ec77eSStephan Aßmus 	for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
260*968ec77eSStephan Aßmus 		if (fControls.ItemAt(i)->Attribute() == attribute)
261*968ec77eSStephan Aßmus 			return fControls.ItemAt(i)->Text();
262*968ec77eSStephan Aßmus 	}
263*968ec77eSStephan Aßmus 
264*968ec77eSStephan Aßmus 	return "";
265*968ec77eSStephan Aßmus }
266*968ec77eSStephan Aßmus 
267*968ec77eSStephan Aßmus 
268*968ec77eSStephan Aßmus void
269*968ec77eSStephan Aßmus PersonView::SetAttribute(const char* attribute, bool update)
270*968ec77eSStephan Aßmus {
271*968ec77eSStephan Aßmus 	char* value = NULL;
272*968ec77eSStephan Aßmus 	attr_info info;
273*968ec77eSStephan Aßmus 	if (fFile != NULL && fFile->GetAttrInfo(attribute, &info) == B_OK) {
274*968ec77eSStephan Aßmus 		value = (char*)calloc(info.size, 1);
275*968ec77eSStephan Aßmus 		fFile->ReadAttr(attribute, B_STRING_TYPE, 0, value, info.size);
276*968ec77eSStephan Aßmus 	}
277*968ec77eSStephan Aßmus 
278*968ec77eSStephan Aßmus 	SetAttribute(attribute, value, update);
279*968ec77eSStephan Aßmus 
280*968ec77eSStephan Aßmus 	free(value);
281*968ec77eSStephan Aßmus }
282*968ec77eSStephan Aßmus 
283*968ec77eSStephan Aßmus 
284*968ec77eSStephan Aßmus void
285*968ec77eSStephan Aßmus PersonView::SetAttribute(const char* attribute, const char* value,
286*968ec77eSStephan Aßmus 	bool update)
287*968ec77eSStephan Aßmus {
288*968ec77eSStephan Aßmus 	if (!LockLooper())
289*968ec77eSStephan Aßmus 		return;
290*968ec77eSStephan Aßmus 
291*968ec77eSStephan Aßmus 	AttributeTextControl* control = NULL;
292*968ec77eSStephan Aßmus 	for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
293*968ec77eSStephan Aßmus 		if (fControls.ItemAt(i)->Attribute() == attribute) {
294*968ec77eSStephan Aßmus 			control = fControls.ItemAt(i);
295*968ec77eSStephan Aßmus 			break;
296*968ec77eSStephan Aßmus 		}
297*968ec77eSStephan Aßmus 	}
298*968ec77eSStephan Aßmus 
299*968ec77eSStephan Aßmus 	if (control == NULL)
300*968ec77eSStephan Aßmus 		return;
301*968ec77eSStephan Aßmus 
302*968ec77eSStephan Aßmus 	if (update) {
303*968ec77eSStephan Aßmus 		control->SetText(value);
304*968ec77eSStephan Aßmus 		control->Update();
305*968ec77eSStephan Aßmus 	} else {
306*968ec77eSStephan Aßmus 		BTextView* text = control->TextView();
307*968ec77eSStephan Aßmus 
308*968ec77eSStephan Aßmus 		int32 start, end;
309*968ec77eSStephan Aßmus 		text->GetSelection(&start, &end);
310*968ec77eSStephan Aßmus 		if (start != end) {
311*968ec77eSStephan Aßmus 			text->Delete();
312*968ec77eSStephan Aßmus 			text->Insert(value);
313*968ec77eSStephan Aßmus 		} else if ((end = text->TextLength())) {
314*968ec77eSStephan Aßmus 			text->Select(end, end);
315*968ec77eSStephan Aßmus 			text->Insert(",");
316*968ec77eSStephan Aßmus 			text->Insert(value);
317*968ec77eSStephan Aßmus 			text->Select(text->TextLength(), text->TextLength());
318*968ec77eSStephan Aßmus 		} else
319*968ec77eSStephan Aßmus 			control->SetText(value);
320*968ec77eSStephan Aßmus 	}
321*968ec77eSStephan Aßmus 
322*968ec77eSStephan Aßmus 	UnlockLooper();
323*968ec77eSStephan Aßmus }
324*968ec77eSStephan Aßmus 
325*968ec77eSStephan Aßmus 
326*968ec77eSStephan Aßmus bool
327*968ec77eSStephan Aßmus PersonView::IsTextSelected() const
328*968ec77eSStephan Aßmus {
329*968ec77eSStephan Aßmus 	for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
330*968ec77eSStephan Aßmus 		BTextView* text = fControls.ItemAt(i)->TextView();
331*968ec77eSStephan Aßmus 
332*968ec77eSStephan Aßmus 		int32 start, end;
333*968ec77eSStephan Aßmus 		text->GetSelection(&start, &end);
334*968ec77eSStephan Aßmus 		if (start != end)
335*968ec77eSStephan Aßmus 			return true;
336*968ec77eSStephan Aßmus 	}
337*968ec77eSStephan Aßmus 	return false;
338*968ec77eSStephan Aßmus }
339