1/*
2 * Copyright (C) 2019-2020 Adrien Destugues <pulkomandy@pulkomandy.tk>
3 *
4 * Distributed under terms of the MIT license.
5 */
6
7#include "AttributesView.h"
8
9#include <Catalog.h>
10#include <ColumnListView.h>
11#include <ColumnTypes.h>
12#include <DateTimeFormat.h>
13#include <FormattingConventions.h>
14#include <fs_attr.h>
15#include <Node.h>
16#include <StringFormat.h>
17
18
19#undef B_TRANSLATION_CONTEXT
20#define B_TRANSLATION_CONTEXT "AttributesView"
21
22int kValueColumn = 1;
23int kTypeColumn = 2;
24
25
26AttributesView::AttributesView(Model* model)
27	:
28	BGroupView(B_VERTICAL, 0),
29	fListView(new BColumnListView("attrs", 0, B_PLAIN_BORDER, false))
30{
31	SetName(B_TRANSLATE("Attributes"));
32	AddChild(fListView);
33	GroupLayout()->SetInsets(-1, -1, -1, -1);
34
35	float nameWidth = StringWidth("SYS:PACKAGE_FILE") + 16;
36	float typeMaxWidth = StringWidth(B_TRANSLATE(
37		"Double-precision floating point number")) + 16;
38	float typeWidth = StringWidth(B_TRANSLATE("64-bit unsigned integer")) + 16;
39	float valueMaxWidth = StringWidth("W") * 64 + 16;
40	float valueWidth = StringWidth("(94.00, 95.00) (1920, 1080)") + 16;
41	BStringColumn* nameColumn = new BStringColumn(B_TRANSLATE("Name"),
42		nameWidth, nameWidth, nameWidth, 0);
43	BStringColumn* typeColumn = new BStringColumn(B_TRANSLATE("Type"),
44		typeWidth, typeWidth, typeMaxWidth, 0);
45	BStringColumn* valueColumn = new BStringColumn(B_TRANSLATE("Value"),
46		valueWidth, valueWidth, valueMaxWidth, 0);
47
48	fListView->AddColumn(nameColumn, 0);
49	fListView->AddColumn(valueColumn, 1);
50	fListView->AddColumn(typeColumn, 2);
51
52	SetExplicitMinSize(BSize(typeWidth + valueWidth + nameWidth + 40,
53		B_SIZE_UNSET));
54
55	BNode* node = model->Node();
56
57	node->RewindAttrs();
58	char name[B_ATTR_NAME_LENGTH];
59
60	// Initialize formatters only once for all attributes
61	BDateTimeFormat dateTimeFormatter;
62	BStringFormat multiValueFormat(B_TRANSLATE(
63		"{0, plural, other{<# values>}}"));
64
65	while (node->GetNextAttrName(name) == B_OK) {
66		// Skip well-known attributes already shown elsewhere in the window
67		if (strcmp(name, "BEOS:TYPE") == 0)
68			continue;
69
70		attr_info info;
71		node->GetAttrInfo(name, &info);
72		BRow* row = new BRow;
73		row->SetField(new BStringField(name), 0);
74
75		BString representation;
76		switch(info.type) {
77			case B_STRING_TYPE:
78			case B_MIME_STRING_TYPE:
79			case 'MSIG':
80			case 'MSDC':
81			case 'MPTH':
82			{
83				// Use a small buffer, long strings will be truncated
84				char buffer[64];
85				ssize_t size = node->ReadAttr(name, info.type, 0, buffer,
86					sizeof(buffer));
87				if (size > 0)
88					representation.SetTo(buffer, size);
89				break;
90			}
91			case B_BOOL_TYPE:
92			{
93				if (info.size == sizeof(bool)) {
94					bool value;
95					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
96					representation = value ? B_TRANSLATE("yes")
97						: B_TRANSLATE("no");
98				} else {
99					multiValueFormat.Format(representation,
100						info.size / sizeof(bool));
101				}
102				break;
103			}
104			case B_INT16_TYPE:
105			{
106				if (info.size == sizeof(int16)) {
107					int16 value;
108					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
109					representation.SetToFormat("%" B_PRId16, value);
110				} else {
111					multiValueFormat.Format(representation,
112						info.size / sizeof(int16));
113				}
114				break;
115			}
116			case B_INT32_TYPE:
117			{
118				if (info.size == sizeof(int32)) {
119					int32 value;
120					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
121					representation.SetToFormat("%" B_PRId32, value);
122				} else {
123					multiValueFormat.Format(representation,
124						info.size / sizeof(int32));
125				}
126				break;
127			}
128			case B_INT64_TYPE:
129			{
130				if (info.size == sizeof(int64)) {
131					int64 value;
132					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
133					representation.SetToFormat("%" B_PRId64, value);
134				} else {
135					multiValueFormat.Format(representation,
136						info.size / sizeof(int64));
137				}
138				break;
139			}
140			case B_INT8_TYPE:
141			{
142				if (info.size == sizeof(int8)) {
143					int8 value;
144					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
145					representation.SetToFormat("%" B_PRId8, value);
146				} else {
147					multiValueFormat.Format(representation,
148						info.size / sizeof(int8));
149				}
150				break;
151			}
152			case B_RECT_TYPE:
153			{
154				if (info.size == sizeof(BRect)) {
155					BRect value;
156					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
157					representation.SetToFormat("(%g,%g) (%g,%g)", value.left,
158						value.top, value.right, value.bottom);
159				} else {
160					BStringFormat multiRectFormat(B_TRANSLATE(
161						"{0, plural, other{<# rectangles>}}"));
162					multiRectFormat.Format(representation,
163						info.size / sizeof(BRect));
164				}
165				break;
166			}
167			case B_DOUBLE_TYPE:
168			{
169				if (info.size == sizeof(double)) {
170					double value;
171					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
172					representation.SetToFormat("%f", value);
173				} else {
174					multiValueFormat.Format(representation,
175						info.size / sizeof(double));
176				}
177				break;
178			}
179			case B_FLOAT_TYPE:
180			{
181				if (info.size == sizeof(float)) {
182					float value;
183					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
184					representation.SetToFormat("%f", value);
185				} else {
186					multiValueFormat.Format(representation,
187						info.size / sizeof(float));
188				}
189				break;
190			}
191			case B_TIME_TYPE:
192			{
193				// Try to handle attributes written on both 32 and 64bit systems
194				if (info.size == sizeof(int32)) {
195					int32 value;
196					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
197					dateTimeFormatter.Format(representation, value,
198						B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
199				} else if (info.size == sizeof(int64)) {
200					int64 value;
201					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
202					dateTimeFormatter.Format(representation, value,
203						B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
204				} else {
205					BStringFormat multiDateFormat(B_TRANSLATE(
206						"{0, plural, other{<# dates>}}"));
207					multiDateFormat.Format(representation,
208						info.size / sizeof(time_t));
209				}
210				break;
211			}
212			case B_UINT16_TYPE:
213			{
214				if (info.size == sizeof(uint16)) {
215					uint16 value;
216					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
217					representation.SetToFormat("%" B_PRIu16, value);
218				} else {
219					multiValueFormat.Format(representation,
220						info.size / sizeof(uint16));
221				}
222				break;
223			}
224			case B_UINT32_TYPE:
225			{
226				if (info.size == sizeof(uint32)) {
227					uint32 value;
228					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
229					representation.SetToFormat("%" B_PRIu32, value);
230				} else {
231					multiValueFormat.Format(representation,
232						info.size / sizeof(uint32));
233				}
234				break;
235			}
236			case B_UINT64_TYPE:
237			{
238				if (info.size == sizeof(uint64)) {
239					uint64 value;
240					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
241					representation.SetToFormat("%" B_PRIu64, value);
242				} else {
243					multiValueFormat.Format(representation,
244						info.size / sizeof(uint64));
245				}
246				break;
247			}
248			case B_UINT8_TYPE:
249			{
250				if (info.size == sizeof(uint8)) {
251					uint8 value;
252					node->ReadAttr(name, info.type, 0, &value, sizeof(value));
253					representation.SetToFormat("%" B_PRIu8, value);
254				} else {
255					multiValueFormat.Format(representation,
256						info.size / sizeof(uint8));
257				}
258				break;
259			}
260			default:
261			{
262				BStringFormat sizeFormat(B_TRANSLATE(
263					"{0, plural, one{<# data byte>} other{<# bytes of data>}}"));
264				sizeFormat.Format(representation, info.size);
265				break;
266			}
267		}
268		row->SetField(new BStringField(representation), kValueColumn);
269
270		switch(info.type) {
271			case B_AFFINE_TRANSFORM_TYPE:
272				row->SetField(new BStringField(B_TRANSLATE("Affine transform")),
273					kTypeColumn);
274				break;
275			case B_ALIGNMENT_TYPE:
276				row->SetField(new BStringField(B_TRANSLATE("Alignment")),
277					kTypeColumn);
278				break;
279			case B_ANY_TYPE:
280				row->SetField(new BStringField(B_TRANSLATE("Any")),
281					kTypeColumn);
282				break;
283			case B_ATOM_TYPE:
284				row->SetField(new BStringField(B_TRANSLATE("Atom")),
285					kTypeColumn);
286				break;
287			case B_ATOMREF_TYPE:
288				row->SetField(new BStringField(B_TRANSLATE("Atom reference")),
289					kTypeColumn);
290				break;
291			case B_BOOL_TYPE:
292				row->SetField(new BStringField(B_TRANSLATE("Boolean")),
293					kTypeColumn);
294				break;
295			case B_CHAR_TYPE:
296				row->SetField(new BStringField(B_TRANSLATE("Character")),
297					kTypeColumn);
298				break;
299			case B_COLOR_8_BIT_TYPE:
300				row->SetField(new BStringField(B_TRANSLATE(
301					"Palette-indexed picture")), kTypeColumn);
302				break;
303			case B_DOUBLE_TYPE:
304				row->SetField(new BStringField(B_TRANSLATE(
305					"Double-precision floating point number")), kTypeColumn);
306				break;
307			case B_FLOAT_TYPE:
308				row->SetField(new BStringField(B_TRANSLATE(
309					"Floating point number")), kTypeColumn);
310				break;
311			case B_GRAYSCALE_8_BIT_TYPE:
312				row->SetField(new BStringField(B_TRANSLATE(
313					"Grayscale picture")), kTypeColumn);
314				break;
315			case B_INT16_TYPE:
316				row->SetField(new BStringField(B_TRANSLATE("16-bit integer")),
317					kTypeColumn);
318				break;
319			case B_INT32_TYPE:
320				row->SetField(new BStringField(B_TRANSLATE("32-bit integer")),
321					kTypeColumn);
322				break;
323			case B_INT64_TYPE:
324				row->SetField(new BStringField(B_TRANSLATE("64-bit integer")),
325					kTypeColumn);
326				break;
327			case B_INT8_TYPE:
328				row->SetField(new BStringField(B_TRANSLATE("8-bit integer")),
329					kTypeColumn);
330				break;
331			case B_LARGE_ICON_TYPE:
332				row->SetField(new BStringField(B_TRANSLATE("Bitmap icon")),
333					kTypeColumn);
334				break;
335			case B_MEDIA_PARAMETER_GROUP_TYPE:
336				row->SetField(new BStringField(B_TRANSLATE(
337					"Media parameter group")), kTypeColumn);
338				break;
339			case B_MEDIA_PARAMETER_TYPE:
340				row->SetField(new BStringField(B_TRANSLATE("Media parameter")),
341					kTypeColumn);
342				break;
343			case B_MEDIA_PARAMETER_WEB_TYPE:
344				row->SetField(new BStringField(B_TRANSLATE(
345					"Media parameter web")), kTypeColumn);
346				break;
347			case B_MESSAGE_TYPE:
348				row->SetField(new BStringField(B_TRANSLATE("Message")),
349					kTypeColumn);
350				break;
351			case B_MESSENGER_TYPE:
352				row->SetField(new BStringField(B_TRANSLATE("Messenger")),
353					kTypeColumn);
354				break;
355			case B_MIME_TYPE:
356				row->SetField(new BStringField(B_TRANSLATE("MIME Type")),
357					kTypeColumn);
358				break;
359			case B_MINI_ICON_TYPE:
360				row->SetField(new BStringField(B_TRANSLATE(
361					"Small bitmap icon")), kTypeColumn);
362				break;
363			case B_MONOCHROME_1_BIT_TYPE:
364				row->SetField(new BStringField(B_TRANSLATE(
365					"Monochrome picture")), kTypeColumn);
366				break;
367			case B_OBJECT_TYPE:
368				row->SetField(new BStringField(B_TRANSLATE("Object")),
369					kTypeColumn);
370				break;
371			case B_OFF_T_TYPE:
372				row->SetField(new BStringField(B_TRANSLATE("File offset")),
373					kTypeColumn);
374				break;
375			case B_PATTERN_TYPE:
376				row->SetField(new BStringField(B_TRANSLATE("Drawing pattern")),
377					kTypeColumn);
378				break;
379			case B_POINTER_TYPE:
380				row->SetField(new BStringField(B_TRANSLATE("Memory pointer")),
381					kTypeColumn);
382				break;
383			case B_POINT_TYPE:
384				row->SetField(new BStringField(B_TRANSLATE("Point")),
385					kTypeColumn);
386				break;
387			case B_PROPERTY_INFO_TYPE:
388				row->SetField(new BStringField(B_TRANSLATE("Property info")),
389					kTypeColumn);
390				break;
391			case B_RAW_TYPE:
392				row->SetField(new BStringField(B_TRANSLATE("Raw data")),
393					kTypeColumn);
394				break;
395			case B_RECT_TYPE:
396				row->SetField(new BStringField(B_TRANSLATE("Rectangle")),
397					kTypeColumn);
398				break;
399			case B_REF_TYPE:
400				row->SetField(new BStringField(B_TRANSLATE("Entry ref")),
401					kTypeColumn);
402				break;
403			case B_NODE_REF_TYPE:
404				row->SetField(new BStringField(B_TRANSLATE("Node ref")),
405					kTypeColumn);
406				break;
407			case B_RGB_32_BIT_TYPE:
408				row->SetField(new BStringField(B_TRANSLATE(
409					"True-color picture")), kTypeColumn);
410				break;
411			case B_RGB_COLOR_TYPE:
412				row->SetField(new BStringField(B_TRANSLATE("Color")),
413					kTypeColumn);
414				break;
415			case B_SIZE_TYPE:
416				row->SetField(new BStringField(B_TRANSLATE("Geometric size")),
417					kTypeColumn);
418				break;
419			case B_SIZE_T_TYPE:
420				row->SetField(new BStringField(B_TRANSLATE("Memory size")),
421					kTypeColumn);
422				break;
423			case B_SSIZE_T_TYPE:
424				row->SetField(new BStringField(B_TRANSLATE(
425					"Signed memory size")), kTypeColumn);
426				break;
427			case B_STRING_TYPE:
428				row->SetField(new BStringField(B_TRANSLATE("Plain text")),
429					kTypeColumn);
430				break;
431			case B_STRING_LIST_TYPE:
432				row->SetField(new BStringField(B_TRANSLATE("Text list")),
433					kTypeColumn);
434				break;
435			case B_TIME_TYPE:
436				row->SetField(new BStringField(B_TRANSLATE("Time")),
437					kTypeColumn);
438				break;
439			case B_UINT16_TYPE:
440				row->SetField(new BStringField(B_TRANSLATE(
441					"16-bit unsigned integer")), kTypeColumn);
442				break;
443			case B_UINT32_TYPE:
444				row->SetField(new BStringField(B_TRANSLATE(
445					"32-bit unsigned integer")), kTypeColumn);
446				break;
447			case B_UINT64_TYPE:
448				row->SetField(new BStringField(B_TRANSLATE(
449					"64-bit unsigned integer")), kTypeColumn);
450				break;
451			case B_UINT8_TYPE:
452				row->SetField(new BStringField(B_TRANSLATE(
453					"8-bit unsigned integer")), kTypeColumn);
454				break;
455			case B_VECTOR_ICON_TYPE:
456				row->SetField(new BStringField(B_TRANSLATE("Icon")),
457					kTypeColumn);
458				break;
459			case B_XATTR_TYPE:
460				row->SetField(new BStringField(B_TRANSLATE(
461					"Extended attribute")), kTypeColumn);
462				break;
463			case B_NETWORK_ADDRESS_TYPE:
464				row->SetField(new BStringField(B_TRANSLATE("Network address")),
465					kTypeColumn);
466				break;
467			case B_MIME_STRING_TYPE:
468				row->SetField(new BStringField(B_TRANSLATE("MIME String")),
469					kTypeColumn);
470				break;
471			case 'MSIG':
472				row->SetField(new BStringField(B_TRANSLATE("MIME Signature")),
473					kTypeColumn);
474				break;
475			case 'MSDC':
476				row->SetField(new BStringField(B_TRANSLATE("MIME Description")),
477					kTypeColumn);
478				break;
479			case 'MPTH':
480				row->SetField(new BStringField(B_TRANSLATE("MIME Path")),
481					kTypeColumn);
482				break;
483			case B_ASCII_TYPE:
484				row->SetField(new BStringField(B_TRANSLATE("ASCII Text")),
485					kTypeColumn);
486				break;
487			default:
488				row->SetField(new BStringField(B_TRANSLATE("(unknown)")),
489					kTypeColumn);
490				break;
491		}
492		fListView->AddRow(row);
493	}
494
495	int32 rows = fListView->CountRows(NULL);
496	if (rows < 5)
497		rows = 5;
498	BRow* first = fListView->RowAt(0, NULL);
499	if (first != NULL) {
500		float height = first->Height() * (rows + 2);
501		SetExplicitMaxSize(BSize(B_SIZE_UNSET, height));
502	}
503
504}
505