1 /*
2 Open Tracker License
3
4 Terms and Conditions
5
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34
35
36 #include "WidgetAttributeText.h"
37
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <strings.h>
41
42 #include <fs_attr.h>
43 #include <parsedate.h>
44
45 #include <Alert.h>
46 #include <AppFileInfo.h>
47 #include <Catalog.h>
48 #include <DateFormat.h>
49 #include <DateTimeFormat.h>
50 #include <Debug.h>
51 #include <Locale.h>
52 #include <NodeInfo.h>
53 #include <NumberFormat.h>
54 #include <Path.h>
55 #include <StringFormat.h>
56 #include <StringForSize.h>
57 #include <SupportDefs.h>
58 #include <TextView.h>
59 #include <Volume.h>
60 #include <VolumeRoster.h>
61
62 #include "Attributes.h"
63 #include "FSUtils.h"
64 #include "Model.h"
65 #include "OpenWithWindow.h"
66 #include "MimeTypes.h"
67 #include "PoseView.h"
68 #include "SettingsViews.h"
69 #include "Utilities.h"
70 #include "ViewState.h"
71
72
73 #undef B_TRANSLATION_CONTEXT
74 #define B_TRANSLATION_CONTEXT "WidgetAttributeText"
75
76
77 const int32 kGenericReadBufferSize = 1024;
78
79
80 bool NameAttributeText::sSortFolderNamesFirst = false;
81 bool RealNameAttributeText::sSortFolderNamesFirst = false;
82
83
84 template <class View>
85 float
TruncFileSizeBase(BString * outString,int64 value,const View * view,float width)86 TruncFileSizeBase(BString* outString, int64 value, const View* view,
87 float width)
88 {
89 // format file size value
90 if (value == kUnknownSize) {
91 *outString = "-";
92 return view->StringWidth("-");
93 }
94
95 char sizeBuffer[128];
96 BString buffer = string_for_size(value, sizeBuffer, sizeof(sizeBuffer));
97
98 if (value < kKBSize) {
99 if (view->StringWidth(buffer.String()) > width) {
100 buffer.SetToFormat(B_TRANSLATE_COMMENT("%lld B", "The filesize symbol for byte"),
101 (long long int)value);
102 }
103 } else {
104 // strip off an insignificant zero so we don't get readings
105 // such as 1.00
106 BNumberFormat numberFormat;
107 BString separator(numberFormat.GetSeparator(B_DECIMAL_SEPARATOR));
108 if (!separator.IsEmpty()) {
109 int32 position = buffer.FindFirst(separator);
110 if (position != B_ERROR && buffer.ByteAt(position + 2) == '0')
111 buffer.Remove(position + 2, 1);
112 }
113 float resultWidth = view->StringWidth(buffer);
114 if (resultWidth <= width) {
115 *outString = buffer.String();
116 return resultWidth;
117 }
118 }
119
120 return TruncStringBase(outString, buffer.String(), buffer.Length(), view,
121 width, (uint32)B_TRUNCATE_END);
122 }
123
124
125 template <class View>
126 float
TruncStringBase(BString * outString,const char * inString,int32 length,const View * view,float width,uint32 truncMode=B_TRUNCATE_MIDDLE)127 TruncStringBase(BString* outString, const char* inString, int32 length,
128 const View* view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE)
129 {
130 // we are using a template version of this call to make sure
131 // the right StringWidth gets picked up for BView x BPoseView
132 // for max speed and flexibility
133
134 // a standard ellipsis inserting fitting algorithm
135 if (view->StringWidth(inString, length) <= width)
136 *outString = inString;
137 else {
138 const char* source[1];
139 char* results[1];
140
141 source[0] = inString;
142 results[0] = outString->LockBuffer(length + 3);
143
144 BFont font;
145 view->GetFont(&font);
146
147 font.GetTruncatedStrings(source, 1, truncMode, width, results);
148 outString->UnlockBuffer();
149 }
150
151 return view->StringWidth(outString->String(), outString->Length());
152 }
153
154
155 template <class View>
156 float
TruncTimeBase(BString * outString,int64 value,const View * view,float width)157 TruncTimeBase(BString* outString, int64 value, const View* view, float width)
158 {
159 float resultWidth = width + 1;
160
161 time_t timeValue = (time_t)value;
162
163 // Find the longest possible format that will fit the available space
164 struct {
165 BDateFormatStyle dateStyle;
166 BTimeFormatStyle timeStyle;
167 } formats[] = {
168 { B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT },
169 { B_LONG_DATE_FORMAT, B_SHORT_TIME_FORMAT },
170 { B_MEDIUM_DATE_FORMAT, B_SHORT_TIME_FORMAT },
171 { B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT },
172 };
173
174 BString date;
175 BDateTimeFormat formatter;
176 for (unsigned int i = 0; i < B_COUNT_OF(formats); ++i) {
177 if (formatter.Format(date, timeValue, formats[i].dateStyle,
178 formats[i].timeStyle) == B_OK) {
179 resultWidth = view->StringWidth(date.String(), date.Length());
180 if (resultWidth <= width) {
181 // Found a format that fits the available space, stop searching
182 break;
183 }
184 }
185 }
186
187 // If we couldn't fit the date, try with just the time
188 // TODO we could use only the time for "today" dates
189 if (resultWidth > width
190 && BDateFormat().Format(date, timeValue,
191 B_SHORT_DATE_FORMAT) == B_OK) {
192 resultWidth = view->StringWidth(date.String(), date.Length());
193 }
194
195 if (resultWidth > width) {
196 // even the shortest format string didn't do it, insert ellipsis
197 resultWidth = TruncStringBase(outString, date.String(),
198 (ssize_t)date.Length(), view, width);
199 } else
200 *outString = date;
201
202 return resultWidth;
203 }
204
205
206 // #pragma mark - WidgetAttributeText base class
207
208
209 WidgetAttributeText*
NewWidgetText(const Model * model,const BColumn * column,const BPoseView * view)210 WidgetAttributeText::NewWidgetText(const Model* model,
211 const BColumn* column, const BPoseView* view)
212 {
213 // call this to make the right WidgetAttributeText type for a
214 // given column
215
216 const char* attrName = column->AttrName();
217
218 if (strcmp(attrName, kAttrPath) == 0)
219 return new PathAttributeText(model, column);
220
221 if (strcmp(attrName, kAttrMIMEType) == 0)
222 return new KindAttributeText(model, column);
223
224 if (strcmp(attrName, kAttrStatName) == 0)
225 return new NameAttributeText(model, column);
226
227 if (strcmp(attrName, kAttrRealName) == 0)
228 return new RealNameAttributeText(model, column);
229
230 if (strcmp(attrName, kAttrStatSize) == 0)
231 return new SizeAttributeText(model, column);
232
233 if (strcmp(attrName, kAttrStatModified) == 0)
234 return new ModificationTimeAttributeText(model, column);
235
236 if (strcmp(attrName, kAttrStatCreated) == 0)
237 return new CreationTimeAttributeText(model, column);
238
239 #ifdef OWNER_GROUP_ATTRIBUTES
240 if (strcmp(attrName, kAttrStatOwner) == 0)
241 return new OwnerAttributeText(model, column);
242
243 if (strcmp(attrName, kAttrStatGroup) == 0)
244 return new GroupAttributeText(model, column);
245 #endif
246 if (strcmp(attrName, kAttrStatMode) == 0)
247 return new ModeAttributeText(model, column);
248
249 if (strcmp(attrName, kAttrOpenWithRelation) == 0)
250 return new OpenWithRelationAttributeText(model, column, view);
251
252 if (strcmp(attrName, kAttrAppVersion) == 0)
253 return new AppShortVersionAttributeText(model, column);
254
255 if (strcmp(attrName, kAttrSystemVersion) == 0)
256 return new SystemShortVersionAttributeText(model, column);
257
258 if (strcmp(attrName, kAttrOriginalPath) == 0)
259 return new OriginalPathAttributeText(model, column);
260
261 if (column->DisplayAs() != NULL) {
262 if (!strncmp(column->DisplayAs(), "checkbox", 8))
263 return new CheckboxAttributeText(model, column);
264
265 if (!strncmp(column->DisplayAs(), "duration", 8))
266 return new DurationAttributeText(model, column);
267
268 if (!strncmp(column->DisplayAs(), "rating", 6))
269 return new RatingAttributeText(model, column);
270 }
271
272 return new GenericAttributeText(model, column);
273 }
274
275
WidgetAttributeText(const Model * model,const BColumn * column)276 WidgetAttributeText::WidgetAttributeText(const Model* model,
277 const BColumn* column)
278 :
279 fModel(const_cast<Model*>(model)),
280 fColumn(column),
281 fOldWidth(-1.0f),
282 fTruncatedWidth(-1.0f),
283 fDirty(true),
284 fValueIsDefined(false)
285 {
286 ASSERT(fColumn != NULL);
287
288 if (fColumn == NULL)
289 return;
290
291 ASSERT(fColumn->Width() > 0);
292 }
293
294
~WidgetAttributeText()295 WidgetAttributeText::~WidgetAttributeText()
296 {
297 }
298
299
300 const char*
FittingText(const BPoseView * view)301 WidgetAttributeText::FittingText(const BPoseView* view)
302 {
303 if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged()
304 || !fValueIsDefined) {
305 CheckViewChanged(view);
306 }
307
308 ASSERT(!fDirty);
309 return fText.String();
310 }
311
312
313 bool
CheckViewChanged(const BPoseView * view)314 WidgetAttributeText::CheckViewChanged(const BPoseView* view)
315 {
316 BString newText;
317 FitValue(&newText, view);
318 if (newText == fText)
319 return false;
320
321 fText = newText;
322 return true;
323 }
324
325
326 bool
CheckSettingsChanged()327 WidgetAttributeText::CheckSettingsChanged()
328 {
329 return false;
330 }
331
332
333 float
TruncString(BString * outString,const char * inString,int32 length,const BPoseView * view,float width,uint32 truncMode)334 WidgetAttributeText::TruncString(BString* outString, const char* inString,
335 int32 length, const BPoseView* view, float width, uint32 truncMode)
336 {
337 return TruncStringBase(outString, inString, length, view, width, truncMode);
338 }
339
340
341 float
TruncFileSize(BString * outString,int64 value,const BPoseView * view,float width)342 WidgetAttributeText::TruncFileSize(BString* outString, int64 value,
343 const BPoseView* view, float width)
344 {
345 return TruncFileSizeBase(outString, value, view, width);
346 }
347
348
349 float
TruncTime(BString * outString,int64 value,const BPoseView * view,float width)350 WidgetAttributeText::TruncTime(BString* outString, int64 value,
351 const BPoseView* view, float width)
352 {
353 return TruncTimeBase(outString, value, view, width);
354 }
355
356
357 float
CurrentWidth() const358 WidgetAttributeText::CurrentWidth() const
359 {
360 return fTruncatedWidth;
361 }
362
363
364 float
Width(const BPoseView * pose)365 WidgetAttributeText::Width(const BPoseView* pose)
366 {
367 FittingText(pose);
368 return CurrentWidth();
369 }
370
371
372 void
SetupEditing(BTextView *)373 WidgetAttributeText::SetupEditing(BTextView*)
374 {
375 ASSERT(fColumn->Editable());
376 }
377
378
379 bool
CommitEditedText(BTextView *)380 WidgetAttributeText::CommitEditedText(BTextView*)
381 {
382 // can't do anything here at this point
383 TRESPASS();
384 return false;
385 }
386
387
388 status_t
AttrAsString(const Model * model,BString * outString,const char * attrName,int32 attrType,float width,BView * view,int64 * resultingValue)389 WidgetAttributeText::AttrAsString(const Model* model, BString* outString,
390 const char* attrName, int32 attrType, float width, BView* view,
391 int64* resultingValue)
392 {
393 int64 value;
394
395 status_t error = model->InitCheck();
396 if (error != B_OK)
397 return error;
398
399 switch (attrType) {
400 case B_TIME_TYPE:
401 if (strcmp(attrName, kAttrStatModified) == 0)
402 value = model->StatBuf()->st_mtime;
403 else if (strcmp(attrName, kAttrStatCreated) == 0)
404 value = model->StatBuf()->st_crtime;
405 else {
406 TRESPASS();
407 // not yet supported
408 return B_ERROR;
409 }
410 TruncTimeBase(outString, value, view, width);
411 if (resultingValue)
412 *resultingValue = value;
413
414 return B_OK;
415
416 case B_STRING_TYPE:
417 if (strcmp(attrName, kAttrPath) == 0) {
418 BEntry entry(model->EntryRef());
419 BPath path;
420 BString tmp;
421
422 if (entry.InitCheck() == B_OK
423 && entry.GetPath(&path) == B_OK) {
424 tmp = path.Path();
425 TruncateLeaf(&tmp);
426 } else
427 tmp = "-";
428
429 if (width > 0) {
430 TruncStringBase(outString, tmp.String(), tmp.Length(), view,
431 width);
432 } else
433 *outString = tmp.String();
434
435 return B_OK;
436 }
437 break;
438
439 case kSizeType:
440 // TruncFileSizeBase(outString, model->StatBuf()->st_size, view,
441 // width);
442 return B_OK;
443 break;
444
445 default:
446 TRESPASS();
447 // not yet supported
448 return B_ERROR;
449
450 }
451
452 TRESPASS();
453 return B_ERROR;
454 }
455
456
457 bool
IsEditable() const458 WidgetAttributeText::IsEditable() const
459 {
460 return fColumn->Editable();
461 }
462
463
464 void
SetDirty(bool value)465 WidgetAttributeText::SetDirty(bool value)
466 {
467 fDirty = value;
468 }
469
470
471 // #pragma mark - StringAttributeText
472
473
StringAttributeText(const Model * model,const BColumn * column)474 StringAttributeText::StringAttributeText(const Model* model,
475 const BColumn* column)
476 :
477 WidgetAttributeText(model, column),
478 fValueDirty(true)
479 {
480 }
481
482
483 const char*
ValueAsText(const BPoseView *)484 StringAttributeText::ValueAsText(const BPoseView* /*view*/)
485 {
486 if (fValueDirty)
487 ReadValue(&fFullValueText);
488
489 return fFullValueText.String();
490 }
491
492
493 bool
CheckAttributeChanged()494 StringAttributeText::CheckAttributeChanged()
495 {
496 BString newString;
497 ReadValue(&newString);
498
499 if (newString == fFullValueText)
500 return false;
501
502 fFullValueText = newString;
503 fDirty = true; // have to redo fitted string
504 return true;
505 }
506
507
508 void
FitValue(BString * outString,const BPoseView * view)509 StringAttributeText::FitValue(BString* outString, const BPoseView* view)
510 {
511 if (fValueDirty)
512 ReadValue(&fFullValueText);
513 fOldWidth = fColumn->Width();
514
515 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
516 fFullValueText.Length(), view, fOldWidth);
517 fDirty = false;
518 }
519
520
521 float
PreferredWidth(const BPoseView * pose) const522 StringAttributeText::PreferredWidth(const BPoseView* pose) const
523 {
524 return pose->StringWidth(fFullValueText.String());
525 }
526
527
528 int
Compare(WidgetAttributeText & attr,BPoseView * view)529 StringAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
530 {
531 StringAttributeText* compareTo = dynamic_cast<StringAttributeText*>(&attr);
532 ThrowOnAssert(compareTo != NULL);
533
534 if (fValueDirty)
535 ReadValue(&fFullValueText);
536
537 return NaturalCompare(fFullValueText.String(),
538 compareTo->ValueAsText(view));
539 }
540
541
542 bool
CommitEditedText(BTextView * textView)543 StringAttributeText::CommitEditedText(BTextView* textView)
544 {
545 ASSERT(fColumn->Editable());
546 const char* text = textView->Text();
547
548 if (fFullValueText == text) {
549 // no change
550 return false;
551 }
552
553 if (textView->TextLength() == 0) {
554 // cannot do an empty name
555 return false;
556 }
557
558 // cause re-truncation
559 fDirty = true;
560
561 if (!CommitEditedTextFlavor(textView))
562 return false;
563
564 // update text and width in this widget
565 fFullValueText = text;
566
567 return true;
568 }
569
570
571 // #pragma mark - ScalarAttributeText
572
573
ScalarAttributeText(const Model * model,const BColumn * column)574 ScalarAttributeText::ScalarAttributeText(const Model* model,
575 const BColumn* column)
576 :
577 WidgetAttributeText(model, column),
578 fValue(0),
579 fValueDirty(true)
580 {
581 }
582
583
584 int64
Value()585 ScalarAttributeText::Value()
586 {
587 if (fValueDirty)
588 fValue = ReadValue();
589
590 return fValue;
591 }
592
593
594 bool
CheckAttributeChanged()595 ScalarAttributeText::CheckAttributeChanged()
596 {
597 int64 newValue = ReadValue();
598 if (newValue == fValue)
599 return false;
600
601 fValue = newValue;
602 fDirty = true;
603 // have to redo fitted string
604
605 return true;
606 }
607
608
609 float
PreferredWidth(const BPoseView * pose) const610 ScalarAttributeText::PreferredWidth(const BPoseView* pose) const
611 {
612 BString widthString;
613 widthString << fValue;
614 return pose->StringWidth(widthString.String());
615 }
616
617
618 int
Compare(WidgetAttributeText & attr,BPoseView *)619 ScalarAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
620 {
621 ScalarAttributeText* compareTo = dynamic_cast<ScalarAttributeText*>(&attr);
622 ThrowOnAssert(compareTo != NULL);
623
624 if (fValueDirty)
625 fValue = ReadValue();
626
627 return fValue >= compareTo->Value()
628 ? (fValue == compareTo->Value() ? 0 : 1) : -1;
629 }
630
631
632 // #pragma mark - PathAttributeText
633
634
PathAttributeText(const Model * model,const BColumn * column)635 PathAttributeText::PathAttributeText(const Model* model, const BColumn* column)
636 :
637 StringAttributeText(model, column)
638 {
639 }
640
641
642 void
ReadValue(BString * outString)643 PathAttributeText::ReadValue(BString* outString)
644 {
645 // get the path
646 BEntry entry(fModel->EntryRef());
647 BPath path;
648
649 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
650 *outString = path.Path();
651 TruncateLeaf(outString);
652 } else
653 *outString = "-";
654
655 fValueDirty = false;
656 }
657
658
659 // #pragma mark - OriginalPathAttributeText
660
661
OriginalPathAttributeText(const Model * model,const BColumn * column)662 OriginalPathAttributeText::OriginalPathAttributeText(const Model* model,
663 const BColumn* column)
664 :
665 StringAttributeText(model, column)
666 {
667 }
668
669
670 void
ReadValue(BString * outString)671 OriginalPathAttributeText::ReadValue(BString* outString)
672 {
673 BEntry entry(fModel->EntryRef());
674 BPath path;
675
676 // get the original path
677 if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK)
678 *outString = path.Path();
679 else
680 *outString = "-";
681
682 fValueDirty = false;
683 }
684
685
686 // #pragma mark - KindAttributeText
687
688
KindAttributeText(const Model * model,const BColumn * column)689 KindAttributeText::KindAttributeText(const Model* model, const BColumn* column)
690 :
691 StringAttributeText(model, column)
692 {
693 }
694
695
696 void
ReadValue(BString * outString)697 KindAttributeText::ReadValue(BString* outString)
698 {
699 BMimeType mime;
700 char desc[B_MIME_TYPE_LENGTH];
701
702 // get the mime type
703 if (mime.SetType(fModel->MimeType()) != B_OK)
704 *outString = B_TRANSLATE("Unknown");
705 else if (mime.GetShortDescription(desc) == B_OK) {
706 // get the short mime type description
707 *outString = desc;
708 } else
709 *outString = fModel->MimeType();
710
711 fValueDirty = false;
712 }
713
714
715 // #pragma mark - NameAttributeText
716
717
NameAttributeText(const Model * model,const BColumn * column)718 NameAttributeText::NameAttributeText(const Model* model,
719 const BColumn* column)
720 :
721 StringAttributeText(model, column)
722 {
723 }
724
725
726 int
Compare(WidgetAttributeText & attr,BPoseView * view)727 NameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
728 {
729 NameAttributeText* compareTo = dynamic_cast<NameAttributeText*>(&attr);
730 ThrowOnAssert(compareTo != NULL);
731
732 if (fValueDirty)
733 ReadValue(&fFullValueText);
734
735 if (NameAttributeText::sSortFolderNamesFirst)
736 return fModel->CompareFolderNamesFirst(attr.TargetModel());
737
738 return NaturalCompare(fFullValueText.String(),
739 compareTo->ValueAsText(view));
740 }
741
742
743 void
ReadValue(BString * outString)744 NameAttributeText::ReadValue(BString* outString)
745 {
746 *outString = fModel->Name();
747
748 fValueDirty = false;
749 }
750
751
752 void
FitValue(BString * outString,const BPoseView * view)753 NameAttributeText::FitValue(BString* outString, const BPoseView* view)
754 {
755 if (fValueDirty)
756 ReadValue(&fFullValueText);
757
758 fOldWidth = fColumn->Width();
759 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
760 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
761 fDirty = false;
762 }
763
764
765 void
SetupEditing(BTextView * textView)766 NameAttributeText::SetupEditing(BTextView* textView)
767 {
768 DisallowFilenameKeys(textView);
769
770 textView->SetMaxBytes(B_FILE_NAME_LENGTH);
771 textView->SetText(fFullValueText.String(), fFullValueText.Length());
772 }
773
774
775 bool
CommitEditedTextFlavor(BTextView * textView)776 NameAttributeText::CommitEditedTextFlavor(BTextView* textView)
777 {
778 if (textView == NULL)
779 return false;
780
781 const char* name = textView->Text();
782 size_t length = (size_t)textView->TextLength();
783
784 BEntry entry(fModel->EntryRef());
785 status_t result = entry.InitCheck();
786 if (result == B_OK)
787 result = EditModelName(fModel, name, length);
788
789 return result == B_OK;
790 }
791
792
793 void
SetSortFolderNamesFirst(bool enabled)794 NameAttributeText::SetSortFolderNamesFirst(bool enabled)
795 {
796 NameAttributeText::sSortFolderNamesFirst = enabled;
797 }
798
799
800 bool
IsEditable() const801 NameAttributeText::IsEditable() const
802 {
803 return StringAttributeText::IsEditable();
804 }
805
806
807 // #pragma mark - RealNameAttributeText
808
809
RealNameAttributeText(const Model * model,const BColumn * column)810 RealNameAttributeText::RealNameAttributeText(const Model* model,
811 const BColumn* column)
812 :
813 NameAttributeText(model, column)
814 {
815 }
816
817
818 int
Compare(WidgetAttributeText & attr,BPoseView * view)819 RealNameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
820 {
821 RealNameAttributeText* compareTo
822 = dynamic_cast<RealNameAttributeText*>(&attr);
823 ThrowOnAssert(compareTo != NULL);
824
825 if (fValueDirty)
826 ReadValue(&fFullValueText);
827
828 if (RealNameAttributeText::sSortFolderNamesFirst)
829 return fModel->CompareFolderNamesFirst(attr.TargetModel());
830
831 return NaturalCompare(fFullValueText.String(),
832 compareTo->ValueAsText(view));
833 }
834
835
836 void
ReadValue(BString * outString)837 RealNameAttributeText::ReadValue(BString* outString)
838 {
839 *outString = fModel->EntryRef()->name;
840
841 fValueDirty = false;
842 }
843
844
845 void
FitValue(BString * outString,const BPoseView * view)846 RealNameAttributeText::FitValue(BString* outString, const BPoseView* view)
847 {
848 if (fValueDirty)
849 ReadValue(&fFullValueText);
850
851 fOldWidth = fColumn->Width();
852 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
853 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
854 fDirty = false;
855 }
856
857
858 void
SetupEditing(BTextView * textView)859 RealNameAttributeText::SetupEditing(BTextView* textView)
860 {
861 DisallowFilenameKeys(textView);
862
863 textView->SetMaxBytes(B_FILE_NAME_LENGTH);
864 textView->SetText(fFullValueText.String(), fFullValueText.Length());
865 }
866
867
868 void
SetSortFolderNamesFirst(bool enabled)869 RealNameAttributeText::SetSortFolderNamesFirst(bool enabled)
870 {
871 RealNameAttributeText::sSortFolderNamesFirst = enabled;
872 }
873
874
875 // #pragma mark - owner/group
876
877
878 #ifdef OWNER_GROUP_ATTRIBUTES
OwnerAttributeText(const Model * model,const BColumn * column)879 OwnerAttributeText::OwnerAttributeText(const Model* model,
880 const BColumn* column)
881 :
882 StringAttributeText(model, column)
883 {
884 }
885
886
887 void
ReadValue(BString * outString)888 OwnerAttributeText::ReadValue(BString* outString)
889 {
890 uid_t nodeOwner = fModel->StatBuf()->st_uid;
891 BString user;
892
893 if (nodeOwner == 0) {
894 if (getenv("USER") != NULL)
895 user << getenv("USER");
896 else
897 user << "root";
898 } else
899 user << nodeOwner;
900 *outString = user.String();
901
902 fValueDirty = false;
903 }
904
905
GroupAttributeText(const Model * model,const BColumn * column)906 GroupAttributeText::GroupAttributeText(const Model* model,
907 const BColumn* column)
908 :
909 StringAttributeText(model, column)
910 {
911 }
912
913
914 void
ReadValue(BString * outString)915 GroupAttributeText::ReadValue(BString* outString)
916 {
917 gid_t nodeGroup = fModel->StatBuf()->st_gid;
918 BString group;
919
920 if (nodeGroup == 0) {
921 if (getenv("GROUP") != NULL)
922 group << getenv("GROUP");
923 else
924 group << "0";
925 } else
926 group << nodeGroup;
927 *outString = group.String();
928
929 fValueDirty = false;
930 }
931 #endif // OWNER_GROUP_ATTRIBUTES
932
933
934 // #pragma mark - ModeAttributeText
935
936
ModeAttributeText(const Model * model,const BColumn * column)937 ModeAttributeText::ModeAttributeText(const Model* model,
938 const BColumn* column)
939 :
940 StringAttributeText(model, column)
941 {
942 }
943
944
945 void
ReadValue(BString * outString)946 ModeAttributeText::ReadValue(BString* outString)
947 {
948 mode_t mode = fModel->StatBuf()->st_mode;
949 mode_t baseMask = 00400;
950 char buffer[11];
951
952 char* scanner = buffer;
953
954 if (S_ISDIR(mode))
955 *scanner++ = 'd';
956 else if (S_ISLNK(mode))
957 *scanner++ = 'l';
958 else if (S_ISBLK(mode))
959 *scanner++ = 'b';
960 else if (S_ISCHR(mode))
961 *scanner++ = 'c';
962 else
963 *scanner++ = '-';
964
965 for (int32 index = 0; index < 9; index++) {
966 *scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-';
967 baseMask >>= 1;
968 }
969
970 *scanner = 0;
971 *outString = buffer;
972
973 fValueDirty = false;
974 }
975
976
977 // #pragma mark - SizeAttributeText
978
979
SizeAttributeText(const Model * model,const BColumn * column)980 SizeAttributeText::SizeAttributeText(const Model* model,
981 const BColumn* column)
982 :
983 ScalarAttributeText(model, column)
984 {
985 }
986
987
988 int64
ReadValue()989 SizeAttributeText::ReadValue()
990 {
991 fValueDirty = false;
992 // get the size
993
994 if (fModel->IsVolume()) {
995 BVolume volume(fModel->NodeRef()->device);
996 fValueIsDefined = volume.Capacity() != 0;
997 return volume.Capacity();
998 }
999
1000 if (fModel->IsDirectory() || fModel->IsQuery()
1001 || fModel->IsQueryTemplate() || fModel->IsSymLink()
1002 || fModel->IsVirtualDirectory()) {
1003 fValueIsDefined = false;
1004 return kUnknownSize;
1005 }
1006
1007 fValueIsDefined = true;
1008
1009 return fModel->StatBuf()->st_size;
1010 }
1011
1012
1013 void
FitValue(BString * outString,const BPoseView * poseView)1014 SizeAttributeText::FitValue(BString* outString, const BPoseView* poseView)
1015 {
1016 if (fValueDirty)
1017 fValue = ReadValue();
1018
1019 fOldWidth = fColumn->Width();
1020 if (!fValueIsDefined) {
1021 *outString = "-";
1022 fTruncatedWidth = poseView->StringWidth("-");
1023 } else
1024 fTruncatedWidth = TruncFileSize(outString, fValue, poseView, fOldWidth);
1025 fDirty = false;
1026 }
1027
1028
1029 float
PreferredWidth(const BPoseView * poseView) const1030 SizeAttributeText::PreferredWidth(const BPoseView* poseView) const
1031 {
1032 if (!fValueIsDefined)
1033 return poseView->StringWidth("-");
1034
1035 BString widthString;
1036 TruncFileSize(&widthString, fValue, poseView, 100000);
1037
1038 return poseView->StringWidth(widthString.String());
1039 }
1040
1041
1042 // #pragma mark - TimeAttributeText
1043
1044
TimeAttributeText(const Model * model,const BColumn * column)1045 TimeAttributeText::TimeAttributeText(const Model* model,
1046 const BColumn* column)
1047 :
1048 ScalarAttributeText(model, column),
1049 fLastClockIs24(false),
1050 fLastDateOrder(kDateFormatEnd),
1051 fLastTimeFormatSeparator(kSeparatorsEnd)
1052 {
1053 }
1054
1055
1056 float
PreferredWidth(const BPoseView * pose) const1057 TimeAttributeText::PreferredWidth(const BPoseView* pose) const
1058 {
1059 BString widthString;
1060 TruncTimeBase(&widthString, fValue, pose, 100000);
1061 return pose->StringWidth(widthString.String());
1062 }
1063
1064
1065 void
FitValue(BString * outString,const BPoseView * view)1066 TimeAttributeText::FitValue(BString* outString, const BPoseView* view)
1067 {
1068 if (fValueDirty)
1069 fValue = ReadValue();
1070
1071 fOldWidth = fColumn->Width();
1072 fTruncatedWidth = TruncTime(outString, fValue, view, fOldWidth);
1073 fDirty = false;
1074 }
1075
1076
1077 bool
CheckSettingsChanged(void)1078 TimeAttributeText::CheckSettingsChanged(void)
1079 {
1080 // TODO : check against the actual locale settings
1081 return false;
1082 }
1083
1084
1085 // #pragma mark - CreationTimeAttributeText
1086
1087
CreationTimeAttributeText(const Model * model,const BColumn * column)1088 CreationTimeAttributeText::CreationTimeAttributeText(const Model* model,
1089 const BColumn* column)
1090 :
1091 TimeAttributeText(model, column)
1092 {
1093 }
1094
1095
1096 int64
ReadValue()1097 CreationTimeAttributeText::ReadValue()
1098 {
1099 fValueDirty = false;
1100 fValueIsDefined = true;
1101 return fModel->StatBuf()->st_crtime;
1102 }
1103
1104
1105 // #pragma mark - ModificationTimeAttributeText
1106
1107
ModificationTimeAttributeText(const Model * model,const BColumn * column)1108 ModificationTimeAttributeText::ModificationTimeAttributeText(
1109 const Model* model, const BColumn* column)
1110 :
1111 TimeAttributeText(model, column)
1112 {
1113 }
1114
1115
1116 int64
ReadValue()1117 ModificationTimeAttributeText::ReadValue()
1118 {
1119 fValueDirty = false;
1120 fValueIsDefined = true;
1121 return fModel->StatBuf()->st_mtime;
1122 }
1123
1124
1125 // #pragma mark - GenericAttributeText
1126
1127
GenericAttributeText(const Model * model,const BColumn * column)1128 GenericAttributeText::GenericAttributeText(const Model* model,
1129 const BColumn* column)
1130 :
1131 StringAttributeText(model, column)
1132 {
1133 }
1134
1135
1136 bool
CheckAttributeChanged()1137 GenericAttributeText::CheckAttributeChanged()
1138 {
1139 GenericValueStruct tmpValue = fValue;
1140 BString tmpString(fFullValueText);
1141 ReadValue(&fFullValueText);
1142
1143 // fDirty could already be true, in that case we mustn't set it to
1144 // false, even if the attribute text hasn't changed
1145 bool changed = fValue.int64t != tmpValue.int64t
1146 || tmpString != fFullValueText;
1147 if (changed)
1148 fDirty = true;
1149
1150 return fDirty;
1151 }
1152
1153
1154 float
PreferredWidth(const BPoseView * pose) const1155 GenericAttributeText::PreferredWidth(const BPoseView* pose) const
1156 {
1157 return pose->StringWidth(fFullValueText.String());
1158 }
1159
1160
1161 void
ReadValue(BString * outString)1162 GenericAttributeText::ReadValue(BString* outString)
1163 {
1164 BModelOpener opener(const_cast<Model*>(fModel));
1165
1166 ssize_t length = 0;
1167 fFullValueText = "-";
1168 fValue.int64t = 0;
1169 fValueIsDefined = false;
1170 fValueDirty = false;
1171
1172 if (!fModel->Node())
1173 return;
1174
1175 switch (fColumn->AttrType()) {
1176 case B_STRING_TYPE:
1177 {
1178 char buffer[kGenericReadBufferSize];
1179 length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1180 fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
1181
1182 if (length > 0) {
1183 buffer[length] = '\0';
1184 // make sure the buffer is null-terminated even if we
1185 // didn't read the whole attribute in or it wasn't to
1186 // begin with
1187
1188 *outString = buffer;
1189 fValueIsDefined = true;
1190 }
1191 break;
1192 }
1193
1194 case B_SSIZE_T_TYPE:
1195 case B_TIME_TYPE:
1196 case B_OFF_T_TYPE:
1197 case B_FLOAT_TYPE:
1198 case B_BOOL_TYPE:
1199 case B_CHAR_TYPE:
1200 case B_INT8_TYPE:
1201 case B_INT16_TYPE:
1202 case B_INT32_TYPE:
1203 case B_INT64_TYPE:
1204 case B_UINT8_TYPE:
1205 case B_UINT16_TYPE:
1206 case B_UINT32_TYPE:
1207 case B_UINT64_TYPE:
1208 case B_DOUBLE_TYPE:
1209 {
1210 // read in the numerical bit representation and attach it
1211 // with a type, depending on the bytes that could be read
1212 attr_info info;
1213 GenericValueStruct tmp;
1214 if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info)
1215 == B_OK) {
1216 if (info.size && info.size <= (off_t)sizeof(int64)) {
1217 length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1218 fColumn->AttrType(), 0, &tmp, (size_t)info.size);
1219 }
1220
1221 // We used tmp as a block of memory, now set the
1222 // correct fValue:
1223
1224 if (length == info.size) {
1225 if (fColumn->AttrType() == B_FLOAT_TYPE
1226 || fColumn->AttrType() == B_DOUBLE_TYPE) {
1227 // filter out special float/double types
1228 switch (info.size) {
1229 case sizeof(float):
1230 fValueIsDefined = true;
1231 fValue.floatt = tmp.floatt;
1232 break;
1233
1234 case sizeof(double):
1235 fValueIsDefined = true;
1236 fValue.doublet = tmp.doublet;
1237 break;
1238
1239 default:
1240 TRESPASS();
1241 break;
1242 }
1243 } else {
1244 // handle the standard data types
1245 switch (info.size) {
1246 case sizeof(char):
1247 // Takes care of bool too.
1248 fValueIsDefined = true;
1249 fValue.int8t = tmp.int8t;
1250 break;
1251
1252 case sizeof(int16):
1253 fValueIsDefined = true;
1254 fValue.int16t = tmp.int16t;
1255 break;
1256
1257 case sizeof(int32):
1258 // Takes care of time_t too.
1259 fValueIsDefined = true;
1260 fValue.int32t = tmp.int32t;
1261 break;
1262
1263 case sizeof(int64):
1264 // Takes care of off_t too.
1265 fValueIsDefined = true;
1266 fValue.int64t = tmp.int64t;
1267 break;
1268
1269 default:
1270 TRESPASS();
1271 break;
1272 }
1273 }
1274 }
1275 }
1276 break;
1277 }
1278 }
1279 }
1280
1281
1282 void
FitValue(BString * outString,const BPoseView * view)1283 GenericAttributeText::FitValue(BString* outString, const BPoseView* view)
1284 {
1285 if (fValueDirty)
1286 ReadValue(&fFullValueText);
1287
1288 fOldWidth = fColumn->Width();
1289
1290 if (!fValueIsDefined) {
1291 *outString = "-";
1292 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1293 fFullValueText.Length(), view, fOldWidth);
1294 fDirty = false;
1295 return;
1296 }
1297
1298 char buffer[256];
1299
1300 switch (fColumn->AttrType()) {
1301 case B_SIZE_T_TYPE:
1302 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1303 return;
1304
1305 case B_SSIZE_T_TYPE:
1306 if (fValue.int32t > 0) {
1307 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1308 return;
1309 }
1310 sprintf(buffer, "%s", strerror(fValue.int32t));
1311 fFullValueText = buffer;
1312 break;
1313
1314 case B_STRING_TYPE:
1315 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1316 fFullValueText.Length(), view, fOldWidth);
1317 fDirty = false;
1318 return;
1319
1320 case B_OFF_T_TYPE:
1321 // As a side effect update the fFullValueText to the string
1322 // representation of value
1323 TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
1324 fTruncatedWidth = TruncFileSize(outString, fValue.off_tt, view,
1325 fOldWidth);
1326 fDirty = false;
1327 return;
1328
1329 case B_TIME_TYPE:
1330 // As a side effect update the fFullValueText to the string
1331 // representation of value
1332 TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
1333 fTruncatedWidth = TruncTime(outString, fValue.time_tt, view,
1334 fOldWidth);
1335 fDirty = false;
1336 return;
1337
1338 case B_BOOL_TYPE:
1339 // For now use true/false, would be nice to be able to set
1340 // the value text
1341
1342 sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
1343 fFullValueText = buffer;
1344 break;
1345
1346 case B_CHAR_TYPE:
1347 // Make sure no non-printable characters are displayed:
1348 if (!isprint(fValue.uint8t)) {
1349 *outString = "-";
1350 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1351 fFullValueText.Length(), view, fOldWidth);
1352 fDirty = false;
1353 return;
1354 }
1355
1356 sprintf(buffer, "%c", fValue.uint8t);
1357 fFullValueText = buffer;
1358 break;
1359
1360 case B_INT8_TYPE:
1361 sprintf(buffer, "%d", fValue.int8t);
1362 fFullValueText = buffer;
1363 break;
1364
1365 case B_UINT8_TYPE:
1366 sprintf(buffer, "%d", fValue.uint8t);
1367 fFullValueText = buffer;
1368 break;
1369
1370 case B_INT16_TYPE:
1371 sprintf(buffer, "%d", fValue.int16t);
1372 fFullValueText = buffer;
1373 break;
1374
1375 case B_UINT16_TYPE:
1376 sprintf(buffer, "%d", fValue.uint16t);
1377 fFullValueText = buffer;
1378 break;
1379
1380 case B_INT32_TYPE:
1381 sprintf(buffer, "%" B_PRId32, fValue.int32t);
1382 fFullValueText = buffer;
1383 break;
1384
1385 case B_UINT32_TYPE:
1386 sprintf(buffer, "%" B_PRId32, fValue.uint32t);
1387 fFullValueText = buffer;
1388 break;
1389
1390 case B_INT64_TYPE:
1391 sprintf(buffer, "%" B_PRId64, fValue.int64t);
1392 fFullValueText = buffer;
1393 break;
1394
1395 case B_UINT64_TYPE:
1396 sprintf(buffer, "%" B_PRId64, fValue.uint64t);
1397 fFullValueText = buffer;
1398 break;
1399
1400 case B_FLOAT_TYPE:
1401 snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
1402 fFullValueText = buffer;
1403 break;
1404
1405 case B_DOUBLE_TYPE:
1406 snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
1407 fFullValueText = buffer;
1408 break;
1409
1410 default:
1411 *outString = "-";
1412 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1413 fFullValueText.Length(), view, fOldWidth);
1414 fDirty = false;
1415 return;
1416 }
1417 fTruncatedWidth = TruncString(outString, buffer, (ssize_t)strlen(buffer),
1418 view, fOldWidth);
1419 fDirty = false;
1420 }
1421
1422
1423 const char*
ValueAsText(const BPoseView * view)1424 GenericAttributeText::ValueAsText(const BPoseView* view)
1425 {
1426 // TODO: redesign this - this is to make sure the value is valid
1427 bool oldDirty = fDirty;
1428 BString outString;
1429 FitValue(&outString, view);
1430 fDirty = oldDirty;
1431
1432 return fFullValueText.String();
1433 }
1434
1435
1436 int
Compare(WidgetAttributeText & attr,BPoseView *)1437 GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
1438 {
1439 GenericAttributeText* compareTo
1440 = dynamic_cast<GenericAttributeText*>(&attr);
1441 ThrowOnAssert(compareTo != NULL);
1442
1443 if (fValueDirty)
1444 ReadValue(&fFullValueText);
1445
1446 if (compareTo->fValueDirty)
1447 compareTo->ReadValue(&compareTo->fFullValueText);
1448
1449 // sort undefined values last, regardless of the other value
1450 if (!fValueIsDefined)
1451 return compareTo->fValueIsDefined ? 1 : 0;
1452
1453 if (!compareTo->fValueIsDefined)
1454 return -1;
1455
1456 switch (fColumn->AttrType()) {
1457 case B_STRING_TYPE:
1458 return fFullValueText.ICompare(compareTo->fFullValueText);
1459
1460 case B_CHAR_TYPE:
1461 {
1462 char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
1463 char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
1464
1465 BString valueStr(vStr);
1466 BString compareToStr(cStr);
1467
1468 return valueStr.ICompare(compareToStr);
1469 }
1470
1471 case B_FLOAT_TYPE:
1472 return fValue.floatt >= compareTo->fValue.floatt ?
1473 (fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
1474
1475 case B_DOUBLE_TYPE:
1476 return fValue.doublet >= compareTo->fValue.doublet ?
1477 (fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
1478
1479 case B_BOOL_TYPE:
1480 return fValue.boolt >= compareTo->fValue.boolt ?
1481 (fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
1482
1483 case B_UINT8_TYPE:
1484 return fValue.uint8t >= compareTo->fValue.uint8t ?
1485 (fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
1486
1487 case B_INT8_TYPE:
1488 return fValue.int8t >= compareTo->fValue.int8t ?
1489 (fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
1490
1491 case B_UINT16_TYPE:
1492 return fValue.uint16t >= compareTo->fValue.uint16t ?
1493 (fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
1494
1495 case B_INT16_TYPE:
1496 return fValue.int16t >= compareTo->fValue.int16t ?
1497 (fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
1498
1499 case B_UINT32_TYPE:
1500 return fValue.uint32t >= compareTo->fValue.uint32t ?
1501 (fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
1502
1503 case B_TIME_TYPE:
1504 // time_t typedef'd to a long, i.e. a int32
1505 case B_INT32_TYPE:
1506 return fValue.int32t >= compareTo->fValue.int32t ?
1507 (fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
1508
1509 case B_OFF_T_TYPE:
1510 // off_t typedef'd to a long long, i.e. a int64
1511 case B_INT64_TYPE:
1512 return fValue.int64t >= compareTo->fValue.int64t ?
1513 (fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
1514
1515 case B_UINT64_TYPE:
1516 default:
1517 return fValue.uint64t >= compareTo->fValue.uint64t ?
1518 (fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
1519 }
1520
1521 return 0;
1522 }
1523
1524
1525 bool
CommitEditedText(BTextView * textView)1526 GenericAttributeText::CommitEditedText(BTextView* textView)
1527 {
1528 ASSERT(fColumn->Editable());
1529 const char* text = textView->Text();
1530
1531 if (fFullValueText == text)
1532 // no change
1533 return false;
1534
1535 if (!CommitEditedTextFlavor(textView))
1536 return false;
1537
1538 // update text and width in this widget
1539 fFullValueText = text;
1540 // cause re-truncation
1541 fDirty = true;
1542 fValueDirty = true;
1543
1544 return true;
1545 }
1546
1547
1548 void
SetupEditing(BTextView * textView)1549 GenericAttributeText::SetupEditing(BTextView* textView)
1550 {
1551 textView->SetMaxBytes(kGenericReadBufferSize - 1);
1552 textView->SetText(fFullValueText.String(), fFullValueText.Length());
1553 }
1554
1555
1556 bool
CommitEditedTextFlavor(BTextView * textView)1557 GenericAttributeText::CommitEditedTextFlavor(BTextView* textView)
1558 {
1559 BNode node(fModel->EntryRef());
1560
1561 if (node.InitCheck() != B_OK)
1562 return false;
1563
1564 uint32 type = fColumn->AttrType();
1565
1566 if (type != B_STRING_TYPE
1567 && type != B_UINT64_TYPE
1568 && type != B_UINT32_TYPE
1569 && type != B_UINT16_TYPE
1570 && type != B_UINT8_TYPE
1571 && type != B_INT64_TYPE
1572 && type != B_INT32_TYPE
1573 && type != B_INT16_TYPE
1574 && type != B_INT8_TYPE
1575 && type != B_OFF_T_TYPE
1576 && type != B_TIME_TYPE
1577 && type != B_FLOAT_TYPE
1578 && type != B_DOUBLE_TYPE
1579 && type != B_CHAR_TYPE
1580 && type != B_BOOL_TYPE) {
1581 BAlert* alert = new BAlert("",
1582 B_TRANSLATE("Sorry, you cannot edit that attribute."),
1583 B_TRANSLATE("Cancel"),
1584 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1585 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1586 alert->Go();
1587 return false;
1588 }
1589
1590 const char* columnName = fColumn->AttrName();
1591 ssize_t size = 0;
1592
1593 switch (type) {
1594 case B_STRING_TYPE:
1595 size = fModel->WriteAttr(columnName, type, 0, textView->Text(),
1596 (size_t)textView->TextLength() + 1);
1597 break;
1598
1599 case B_BOOL_TYPE:
1600 {
1601 bool value = strncasecmp(textView->Text(), "0", 1) != 0
1602 && strncasecmp(textView->Text(), "off", 2) != 0
1603 && strncasecmp(textView->Text(), "no", 3) != 0
1604 && strncasecmp(textView->Text(), "false", 4) != 0
1605 && strlen(textView->Text()) != 0;
1606
1607 size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
1608 break;
1609 }
1610
1611 case B_CHAR_TYPE:
1612 {
1613 char ch;
1614 sscanf(textView->Text(), "%c", &ch);
1615 //Check if we read the start of a multi-byte glyph:
1616 if (!isprint(ch)) {
1617 BAlert* alert = new BAlert("",
1618 B_TRANSLATE("Sorry, the 'Character' "
1619 "attribute cannot store a multi-byte glyph."),
1620 B_TRANSLATE("Cancel"),
1621 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1622 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1623 alert->Go();
1624 return false;
1625 }
1626
1627 size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
1628 break;
1629 }
1630
1631 case B_FLOAT_TYPE:
1632 {
1633 float floatVal;
1634
1635 if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
1636 fValueIsDefined = true;
1637 fValue.floatt = floatVal;
1638 size = fModel->WriteAttr(columnName, type, 0, &floatVal,
1639 sizeof(float));
1640 } else {
1641 // If the value was already defined, it's on disk.
1642 // Otherwise not.
1643 return fValueIsDefined;
1644 }
1645 break;
1646 }
1647
1648 case B_DOUBLE_TYPE:
1649 {
1650 double doubleVal;
1651
1652 if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
1653 fValueIsDefined = true;
1654 fValue.doublet = doubleVal;
1655 size = fModel->WriteAttr(columnName, type, 0, &doubleVal,
1656 sizeof(double));
1657 } else {
1658 // If the value was already defined, it's on disk.
1659 // Otherwise not.
1660 return fValueIsDefined;
1661 }
1662 break;
1663 }
1664
1665 case B_TIME_TYPE:
1666 case B_OFF_T_TYPE:
1667 case B_UINT64_TYPE:
1668 case B_UINT32_TYPE:
1669 case B_UINT16_TYPE:
1670 case B_UINT8_TYPE:
1671 case B_INT64_TYPE:
1672 case B_INT32_TYPE:
1673 case B_INT16_TYPE:
1674 case B_INT8_TYPE:
1675 {
1676 GenericValueStruct tmp;
1677 size_t scalarSize = 0;
1678
1679 switch (type) {
1680 case B_TIME_TYPE:
1681 tmp.time_tt = parsedate(textView->Text(), time(0));
1682 scalarSize = sizeof(time_t);
1683 break;
1684
1685 // do some size independent conversion on builtin types
1686 case B_OFF_T_TYPE:
1687 tmp.off_tt = StringToScalar(textView->Text());
1688 scalarSize = sizeof(off_t);
1689 break;
1690
1691 case B_UINT64_TYPE:
1692 case B_INT64_TYPE:
1693 tmp.int64t = StringToScalar(textView->Text());
1694 scalarSize = sizeof(int64);
1695 break;
1696
1697 case B_UINT32_TYPE:
1698 case B_INT32_TYPE:
1699 tmp.int32t = (int32)StringToScalar(textView->Text());
1700 scalarSize = sizeof(int32);
1701 break;
1702
1703 case B_UINT16_TYPE:
1704 case B_INT16_TYPE:
1705 tmp.int16t = (int16)StringToScalar(textView->Text());
1706 scalarSize = sizeof(int16);
1707 break;
1708
1709 case B_UINT8_TYPE:
1710 case B_INT8_TYPE:
1711 tmp.int8t = (int8)StringToScalar(textView->Text());
1712 scalarSize = sizeof(int8);
1713 break;
1714
1715 default:
1716 TRESPASS();
1717 }
1718
1719 size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
1720 break;
1721 }
1722 }
1723
1724 if (size < 0) {
1725 BAlert* alert = new BAlert("",
1726 B_TRANSLATE("There was an error writing the attribute."),
1727 B_TRANSLATE("Cancel"),
1728 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1729 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1730 alert->Go();
1731
1732 fValueIsDefined = false;
1733 return false;
1734 }
1735
1736 fValueIsDefined = true;
1737 return true;
1738 }
1739
1740
1741 // #pragma mark - DurationAttributeText (display as: duration)
1742
1743
DurationAttributeText(const Model * model,const BColumn * column)1744 DurationAttributeText::DurationAttributeText(const Model* model,
1745 const BColumn* column)
1746 :
1747 GenericAttributeText(model, column)
1748 {
1749 }
1750
1751
1752 // TODO: support editing!
1753
1754
1755 void
FitValue(BString * outString,const BPoseView * view)1756 DurationAttributeText::FitValue(BString* outString, const BPoseView* view)
1757 {
1758 if (fValueDirty)
1759 ReadValue(&fFullValueText);
1760
1761 fOldWidth = fColumn->Width();
1762 fDirty = false;
1763
1764 if (!fValueIsDefined) {
1765 *outString = "-";
1766 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1767 fFullValueText.Length(), view, fOldWidth);
1768 return;
1769 }
1770
1771 int64 time = 0;
1772
1773 switch (fColumn->AttrType()) {
1774 case B_TIME_TYPE:
1775 time = fValue.time_tt * 1000000LL;
1776 break;
1777
1778 case B_INT8_TYPE:
1779 time = fValue.int8t * 1000000LL;
1780 break;
1781
1782 case B_INT16_TYPE:
1783 time = fValue.int16t * 1000000LL;
1784 break;
1785
1786 case B_INT32_TYPE:
1787 time = fValue.int32t * 1000000LL;
1788 break;
1789
1790 case B_INT64_TYPE:
1791 time = fValue.int64t;
1792 break;
1793 }
1794
1795 // TODO: ignores micro seconds for now
1796 int32 seconds = time / 1000000LL;
1797
1798 bool negative = seconds < 0;
1799 if (negative)
1800 seconds = -seconds;
1801
1802 int32 hours = seconds / 3600;
1803 seconds -= hours * 3600;
1804 int32 minutes = seconds / 60;
1805 seconds = seconds % 60;
1806
1807 char buffer[256];
1808 if (hours > 0) {
1809 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02"
1810 B_PRId32, negative ? "-" : "", hours, minutes, seconds);
1811 } else {
1812 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32,
1813 negative ? "-" : "", minutes, seconds);
1814 }
1815
1816 fFullValueText = buffer;
1817
1818 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1819 fFullValueText.Length(), view, fOldWidth);
1820 }
1821
1822
1823 // #pragma mark - CheckboxAttributeText (display as: checkbox)
1824
1825
CheckboxAttributeText(const Model * model,const BColumn * column)1826 CheckboxAttributeText::CheckboxAttributeText(const Model* model,
1827 const BColumn* column)
1828 :
1829 GenericAttributeText(model, column),
1830 fOnChar("✖"),
1831 fOffChar("-")
1832 {
1833 // TODO: better have common data in the column object!
1834 if (const char* separator = strchr(column->DisplayAs(), ':')) {
1835 BString chars(separator + 1);
1836 int32 length;
1837 const char* c = chars.CharAt(0, &length);
1838 fOnChar.SetTo(c, length);
1839 if (c[length]) {
1840 c = chars.CharAt(1, &length);
1841 fOffChar.SetTo(c, length);
1842 }
1843 }
1844 }
1845
1846
1847 void
SetupEditing(BTextView * view)1848 CheckboxAttributeText::SetupEditing(BTextView* view)
1849 {
1850 // TODO: support editing for real!
1851 BString outString;
1852 GenericAttributeText::FitValue(&outString, NULL);
1853 GenericAttributeText::SetupEditing(view);
1854 }
1855
1856
1857 void
FitValue(BString * outString,const BPoseView * view)1858 CheckboxAttributeText::FitValue(BString* outString, const BPoseView* view)
1859 {
1860 if (fValueDirty)
1861 ReadValue(&fFullValueText);
1862
1863 fOldWidth = fColumn->Width();
1864 fDirty = false;
1865
1866 if (!fValueIsDefined) {
1867 *outString = fOffChar;
1868 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1869 fFullValueText.Length(), view, fOldWidth);
1870 return;
1871 }
1872
1873 bool checked = false;
1874
1875 switch (fColumn->AttrType()) {
1876 case B_BOOL_TYPE:
1877 checked = fValue.boolt;
1878 break;
1879
1880 case B_INT8_TYPE:
1881 case B_UINT8_TYPE:
1882 checked = fValue.int8t != 0;
1883 break;
1884
1885 case B_INT16_TYPE:
1886 case B_UINT16_TYPE:
1887 checked = fValue.int16t != 0;
1888 break;
1889
1890 case B_INT32_TYPE:
1891 case B_UINT32_TYPE:
1892 checked = fValue.int32t != 0;
1893 break;
1894 }
1895
1896 fFullValueText = checked ? fOnChar : fOffChar;
1897
1898 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1899 fFullValueText.Length(), view, fOldWidth);
1900 }
1901
1902
1903 // #pragma mark - RatingAttributeText (display as: rating)
1904
1905
RatingAttributeText(const Model * model,const BColumn * column)1906 RatingAttributeText::RatingAttributeText(const Model* model,
1907 const BColumn* column)
1908 :
1909 GenericAttributeText(model, column),
1910 fCount(5),
1911 fMax(10)
1912 {
1913 // TODO: support different star counts/max via specifier
1914 }
1915
1916
1917 void
SetupEditing(BTextView * view)1918 RatingAttributeText::SetupEditing(BTextView* view)
1919 {
1920 // TODO: support editing for real!
1921 BString outString;
1922 GenericAttributeText::FitValue(&outString, NULL);
1923 GenericAttributeText::SetupEditing(view);
1924 }
1925
1926
1927 void
FitValue(BString * ratingString,const BPoseView * view)1928 RatingAttributeText::FitValue(BString* ratingString, const BPoseView* view)
1929 {
1930 if (fValueDirty)
1931 ReadValue(&fFullValueText);
1932
1933 fOldWidth = fColumn->Width();
1934 fDirty = false;
1935
1936 int64 rating;
1937 if (fValueIsDefined) {
1938 switch (fColumn->AttrType()) {
1939 case B_INT8_TYPE:
1940 rating = fValue.int8t;
1941 break;
1942
1943 case B_INT16_TYPE:
1944 rating = fValue.int16t;
1945 break;
1946
1947 case B_INT32_TYPE:
1948 rating = fValue.int32t;
1949 break;
1950
1951 default:
1952 rating = 0;
1953 break;
1954 }
1955 } else
1956 rating = 0;
1957
1958 if (rating > fMax)
1959 rating = fMax;
1960
1961 if (rating < 0)
1962 rating = 0;
1963
1964 int32 steps = fMax / fCount;
1965 fFullValueText = "";
1966
1967 for (int32 i = 0; i < fCount; i++) {
1968 int64 n = i * steps;
1969 if (rating > n + steps / 2)
1970 fFullValueText += "★";
1971 else if (rating > n)
1972 fFullValueText += "⯪";
1973 else
1974 fFullValueText += "☆";
1975 }
1976
1977 fTruncatedWidth = TruncString(ratingString, fFullValueText.String(),
1978 fFullValueText.Length(), view, fOldWidth);
1979 }
1980
1981
1982 // #pragma mark - OpenWithRelationAttributeText
1983
1984
OpenWithRelationAttributeText(const Model * model,const BColumn * column,const BPoseView * view)1985 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model,
1986 const BColumn* column, const BPoseView* view)
1987 :
1988 ScalarAttributeText(model, column),
1989 fPoseView(view)
1990 {
1991 }
1992
1993
1994 int64
ReadValue()1995 OpenWithRelationAttributeText::ReadValue()
1996 {
1997 fValueDirty = false;
1998
1999 const OpenWithPoseView* view
2000 = dynamic_cast<const OpenWithPoseView*>(fPoseView);
2001 if (view != NULL) {
2002 fValue = view->OpenWithRelation(fModel);
2003 fValueIsDefined = true;
2004 }
2005
2006 return fValue;
2007 }
2008
2009
2010 float
PreferredWidth(const BPoseView * pose) const2011 OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const
2012 {
2013 BString widthString;
2014 TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
2015 pose, 500, B_TRUNCATE_END);
2016 return pose->StringWidth(widthString.String());
2017 }
2018
2019
2020 void
FitValue(BString * outString,const BPoseView * view)2021 OpenWithRelationAttributeText::FitValue(BString* outString,
2022 const BPoseView* view)
2023 {
2024 if (fValueDirty)
2025 ReadValue();
2026
2027 ASSERT(view == fPoseView);
2028 const OpenWithPoseView* launchWithView
2029 = dynamic_cast<const OpenWithPoseView*>(view);
2030 if (launchWithView != NULL)
2031 launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
2032
2033 fOldWidth = fColumn->Width();
2034 fTruncatedWidth = TruncString(outString, fRelationText.String(),
2035 fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
2036 fDirty = false;
2037 }
2038
2039
2040 // #pragma mark - VersionAttributeText
2041
2042
VersionAttributeText(const Model * model,const BColumn * column,bool app)2043 VersionAttributeText::VersionAttributeText(const Model* model,
2044 const BColumn* column, bool app)
2045 :
2046 StringAttributeText(model, column),
2047 fAppVersion(app)
2048 {
2049 }
2050
2051
2052 void
ReadValue(BString * outString)2053 VersionAttributeText::ReadValue(BString* outString)
2054 {
2055 fValueDirty = false;
2056
2057 BModelOpener opener(fModel);
2058 BFile* file = dynamic_cast<BFile*>(fModel->Node());
2059 if (file != NULL) {
2060 BAppFileInfo info(file);
2061 version_info version;
2062 if (info.InitCheck() == B_OK
2063 && info.GetVersionInfo(&version, fAppVersion
2064 ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
2065 *outString = version.short_info;
2066 return;
2067 }
2068 }
2069
2070 *outString = "-";
2071 }
2072