libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / apps / people / PersonView.cpp
blob6788dab984dd800ad3d435d4bcba4964f601b506
1 /*
2 * Copyright 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.
14 #include "PersonView.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <BitmapStream.h>
21 #include <Catalog.h>
22 #include <fs_attr.h>
23 #include <Box.h>
24 #include <ControlLook.h>
25 #include <GridLayout.h>
26 #include <Locale.h>
27 #include <MenuField.h>
28 #include <MenuItem.h>
29 #include <PopUpMenu.h>
30 #include <Query.h>
31 #include <TranslationUtils.h>
32 #include <Translator.h>
33 #include <VolumeRoster.h>
34 #include <Window.h>
36 #include "AttributeTextControl.h"
37 #include "PictureView.h"
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "People"
44 PersonView::PersonView(const char* name, const char* categoryAttribute,
45 const entry_ref *ref)
47 BGridView(),
48 fLastModificationTime(0),
49 fGroups(NULL),
50 fControls(20, false),
51 fCategoryAttribute(categoryAttribute),
52 fPictureView(NULL),
53 fSaving(false)
55 SetName(name);
56 SetFlags(Flags() | B_WILL_DRAW);
58 fRef = ref;
59 BFile* file = NULL;
60 if (fRef != NULL)
61 file = new BFile(fRef, B_READ_ONLY);
63 // Add picture "field", using ID photo 35mm x 45mm ratio
64 fPictureView = new PictureView(70, 90, ref);
66 BGridLayout* layout = GridLayout();
68 float spacing = be_control_look->DefaultItemSpacing();
69 layout->SetInsets(spacing, spacing, spacing, spacing);
71 layout->AddView(fPictureView, 0, 0, 1, 5);
72 layout->ItemAt(0, 0)->SetExplicitAlignment(
73 BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP));
75 if (file != NULL)
76 file->GetModificationTime(&fLastModificationTime);
77 delete file;
81 PersonView::~PersonView()
86 void
87 PersonView::AddAttribute(const char* label, const char* attribute)
89 // Check if this attribute has already been added.
90 AttributeTextControl* control = NULL;
91 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
92 if (fControls.ItemAt(i)->Attribute() == attribute) {
93 return;
97 control = new AttributeTextControl(label, attribute);
98 fControls.AddItem(control);
100 BGridLayout* layout = GridLayout();
101 int32 row = fControls.CountItems();
103 if (fCategoryAttribute == attribute) {
104 // Special case the category attribute. The Group popup field will
105 // be added as the label instead.
106 fGroups = new BPopUpMenu(label);
107 fGroups->SetRadioMode(false);
108 BuildGroupMenu();
110 BMenuField* field = new BMenuField("", "", fGroups);
111 field->SetEnabled(true);
112 layout->AddView(field, 1, row);
114 control->SetLabel("");
115 layout->AddView(control, 2, row);
116 } else {
117 layout->AddItem(control->CreateLabelLayoutItem(), 1, row);
118 layout->AddItem(control->CreateTextViewLayoutItem(), 2, row);
121 SetAttribute(attribute, true);
125 void
126 PersonView::MakeFocus(bool focus)
128 if (focus && fControls.CountItems() > 0)
129 fControls.ItemAt(0)->MakeFocus();
130 else
131 BView::MakeFocus(focus);
135 void
136 PersonView::MessageReceived(BMessage* msg)
138 switch (msg->what) {
139 case M_SAVE:
140 Save();
141 break;
143 case M_REVERT:
144 if (fPictureView)
145 fPictureView->Revert();
147 for (int32 i = fControls.CountItems() - 1; i >= 0; i--)
148 fControls.ItemAt(i)->Revert();
149 break;
151 case M_SELECT:
152 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
153 BTextView* text = fControls.ItemAt(i)->TextView();
154 if (text->IsFocus()) {
155 text->Select(0, text->TextLength());
156 break;
159 break;
161 case M_GROUP_MENU:
163 const char* name = NULL;
164 if (msg->FindString("group", &name) == B_OK)
165 SetAttribute(fCategoryAttribute, name, false);
166 break;
173 void
174 PersonView::Draw(BRect updateRect)
176 if (!fPictureView)
177 return;
179 // Draw a alert/get info-like strip
180 BRect stripeRect = Bounds();
181 stripeRect.right = GridLayout()->HorizontalSpacing()
182 + fPictureView->Bounds().Width() / 2;
183 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
184 FillRect(stripeRect);
188 void
189 PersonView::BuildGroupMenu()
191 if (fGroups == NULL)
192 return;
194 BMenuItem* item;
195 while ((item = fGroups->ItemAt(0)) != NULL) {
196 fGroups->RemoveItem(item);
197 delete item;
200 int32 count = 0;
202 BVolumeRoster volumeRoster;
203 BVolume volume;
204 while (volumeRoster.GetNextVolume(&volume) == B_OK) {
205 BQuery query;
206 query.SetVolume(&volume);
208 char buffer[256];
209 snprintf(buffer, sizeof(buffer), "%s=*", fCategoryAttribute.String());
210 query.SetPredicate(buffer);
211 query.Fetch();
213 BEntry entry;
214 while (query.GetNextEntry(&entry) == B_OK) {
215 BFile file(&entry, B_READ_ONLY);
216 attr_info info;
218 if (file.InitCheck() == B_OK
219 && file.GetAttrInfo(fCategoryAttribute, &info) == B_OK
220 && info.size > 1) {
221 if (info.size > (off_t)sizeof(buffer))
222 info.size = sizeof(buffer);
224 if (file.ReadAttr(fCategoryAttribute.String(), B_STRING_TYPE,
225 0, buffer, info.size) < 0) {
226 continue;
229 const char *text = buffer;
230 while (true) {
231 char* offset = strstr(text, ",");
232 if (offset != NULL)
233 offset[0] = '\0';
235 if (!fGroups->FindItem(text)) {
236 int32 index = 0;
237 while ((item = fGroups->ItemAt(index)) != NULL) {
238 if (strcmp(text, item->Label()) < 0)
239 break;
240 index++;
242 BMessage* message = new BMessage(M_GROUP_MENU);
243 message->AddString("group", text);
244 fGroups->AddItem(new BMenuItem(text, message), index);
245 count++;
247 if (offset) {
248 text = offset + 1;
249 while (*text == ' ')
250 text++;
252 else
253 break;
259 if (count == 0) {
260 fGroups->AddItem(item = new BMenuItem(
261 B_TRANSLATE_CONTEXT("none", "Groups list"),
262 new BMessage(M_GROUP_MENU)));
263 item->SetEnabled(false);
266 fGroups->SetTargetForItems(this);
270 void
271 PersonView::CreateFile(const entry_ref* ref)
273 fRef = ref;
274 Save();
278 bool
279 PersonView::IsSaved() const
281 if (fPictureView && fPictureView->HasChanged())
282 return false;
284 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
285 if (fControls.ItemAt(i)->HasChanged())
286 return false;
289 return true;
293 void
294 PersonView::Save()
296 BFile file(fRef, B_READ_WRITE);
297 if (file.InitCheck() != B_NO_ERROR)
298 return;
300 fSaving = true;
302 int32 count = fControls.CountItems();
303 for (int32 i = 0; i < count; i++) {
304 AttributeTextControl* control = fControls.ItemAt(i);
305 const char* value = control->Text();
306 file.WriteAttr(control->Attribute().String(), B_STRING_TYPE, 0,
307 value, strlen(value) + 1);
308 control->Update();
311 // Write the picture, if any, in the person file content
312 if (fPictureView) {
313 // Trim any previous content
314 file.Seek(0, SEEK_SET);
315 file.SetSize(0);
317 BBitmap* picture = fPictureView->Bitmap();
318 if (picture) {
319 BBitmapStream stream(picture);
320 // Detach *our* bitmap from stream to avoid its deletion
321 // at stream object destruction
322 stream.DetachBitmap(&picture);
324 BTranslatorRoster* roster = BTranslatorRoster::Default();
325 roster->Translate(&stream, NULL, NULL, &file,
326 fPictureView->SuggestedType(), B_TRANSLATOR_BITMAP,
327 fPictureView->SuggestedMIMEType());
331 fPictureView->Update();
334 file.GetModificationTime(&fLastModificationTime);
336 fSaving = false;
340 const char*
341 PersonView::AttributeValue(const char* attribute) const
343 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
344 if (fControls.ItemAt(i)->Attribute() == attribute)
345 return fControls.ItemAt(i)->Text();
348 return "";
352 void
353 PersonView::SetAttribute(const char* attribute, bool update)
355 char* value = NULL;
356 attr_info info;
357 BFile* file = NULL;
359 if (fRef != NULL)
360 file = new(std::nothrow) BFile(fRef, B_READ_ONLY);
362 if (file != NULL && file->GetAttrInfo(attribute, &info) == B_OK) {
363 value = (char*)calloc(info.size, 1);
364 file->ReadAttr(attribute, B_STRING_TYPE, 0, value, info.size);
367 SetAttribute(attribute, value, update);
369 free(value);
370 delete file;
374 void
375 PersonView::SetAttribute(const char* attribute, const char* value,
376 bool update)
378 if (!LockLooper())
379 return;
381 AttributeTextControl* control = NULL;
382 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
383 if (fControls.ItemAt(i)->Attribute() == attribute) {
384 control = fControls.ItemAt(i);
385 break;
389 if (control == NULL)
390 return;
392 if (update) {
393 control->SetText(value);
394 control->Update();
395 } else {
396 BTextView* text = control->TextView();
398 int32 start, end;
399 text->GetSelection(&start, &end);
400 if (start != end) {
401 text->Delete();
402 text->Insert(value);
403 } else if ((end = text->TextLength())) {
404 text->Select(end, end);
405 text->Insert(",");
406 text->Insert(value);
407 text->Select(text->TextLength(), text->TextLength());
408 } else
409 control->SetText(value);
412 UnlockLooper();
416 void
417 PersonView::UpdatePicture(const entry_ref* ref)
419 if (fPictureView == NULL)
420 return;
422 if (fSaving)
423 return;
425 time_t modificationTime = 0;
426 BEntry entry(ref);
427 entry.GetModificationTime(&modificationTime);
429 if (entry.InitCheck() == B_OK
430 && modificationTime <= fLastModificationTime) {
431 return;
434 fPictureView->Update(ref);
438 bool
439 PersonView::IsTextSelected() const
441 for (int32 i = fControls.CountItems() - 1; i >= 0; i--) {
442 BTextView* text = fControls.ItemAt(i)->TextView();
444 int32 start, end;
445 text->GetSelection(&start, &end);
446 if (start != end)
447 return true;
449 return false;