1/*
2 * Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Vlad Slepukhin
7 *		Siarzhuk Zharski
8 */
9
10
11#include "StatusView.h"
12#include <StatusView.h>
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <Catalog.h>
19#include <CharacterSet.h>
20#include <CharacterSetRoster.h>
21#include <ControlLook.h>
22#include <MenuItem.h>
23#include <Message.h>
24#include <PopUpMenu.h>
25#include <ScrollView.h>
26#include <StringView.h>
27#include <Window.h>
28
29#include <tracker_private.h>
30#include "DirMenu.h"
31
32#include "Constants.h"
33
34
35const float kHorzSpacing = 5.f;
36#define UTF8_EXPAND_ARROW "\xe2\x96\xbe"
37
38using namespace BPrivate;
39
40
41#undef B_TRANSLATION_CONTEXT
42#define B_TRANSLATION_CONTEXT "StatusView"
43
44
45StatusView::StatusView(BScrollView* scrollView)
46			:
47			BView(BRect(), "statusview",
48				B_FOLLOW_BOTTOM | B_FOLLOW_LEFT, B_WILL_DRAW),
49			fScrollView(scrollView),
50			fPreferredSize(0., 0.),
51			fReadOnly(false),
52			fCanUnlock(false)
53{
54	memset(fCellWidth, 0, sizeof(fCellWidth));
55}
56
57
58StatusView::~StatusView()
59{
60}
61
62
63void
64StatusView::AttachedToWindow()
65{
66	SetFont(be_plain_font);
67	BPrivate::AdoptScrollBarFontSize(this);
68
69	BMessage message(UPDATE_STATUS);
70	message.AddInt32("line", 1);
71	message.AddInt32("column", 1);
72	message.AddString("encoding", "");
73	SetStatus(&message);
74
75	BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
76	MoveTo(0., scrollBar->Frame().top);
77
78	rgb_color color = B_TRANSPARENT_COLOR;
79	BView* parent = Parent();
80	if (parent != NULL)
81		color = parent->ViewColor();
82
83	if (color == B_TRANSPARENT_COLOR)
84		color = ui_color(B_PANEL_BACKGROUND_COLOR);
85
86	SetViewColor(color);
87
88	ResizeToPreferred();
89}
90
91
92void
93StatusView::GetPreferredSize(float* _width, float* _height)
94{
95	_ValidatePreferredSize();
96
97	if (_width)
98		*_width = fPreferredSize.width;
99
100	if (_height)
101		*_height = fPreferredSize.height;
102}
103
104
105void
106StatusView::ResizeToPreferred()
107{
108	float width, height;
109	GetPreferredSize(&width, &height);
110
111	if (Bounds().Width() > width)
112		width = Bounds().Width();
113
114	BView::ResizeTo(width, height);
115}
116
117
118void
119StatusView::Draw(BRect updateRect)
120{
121	if (fPreferredSize.width <= 0)
122		return;
123
124	if (be_control_look != NULL) {
125		BRect bounds(Bounds());
126		be_control_look->DrawMenuBarBackground(this,
127			bounds, updateRect,	ViewColor());
128	}
129
130	BRect bounds(Bounds());
131	SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT));
132	StrokeLine(bounds.LeftTop(), bounds.RightTop());
133
134	float x = bounds.left;
135	for (size_t i = 0; i < kStatusCellCount - 1; i++) {
136		x += fCellWidth[i];
137		StrokeLine(BPoint(x, bounds.top + 3), BPoint(x, bounds.bottom - 3));
138	}
139
140	SetLowColor(ViewColor());
141	SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
142
143	font_height fontHeight;
144	GetFontHeight(&fontHeight);
145
146	x = bounds.left;
147	float y = (bounds.bottom + bounds.top
148		+ ceilf(fontHeight.ascent) - ceilf(fontHeight.descent)) / 2;
149
150	for (size_t i = 0; i < kStatusCellCount; i++) {
151		if (fCellText[i].Length() == 0)
152			continue;
153		DrawString(fCellText[i], BPoint(x + kHorzSpacing, y));
154		x += fCellWidth[i];
155	}
156}
157
158
159void
160StatusView::MouseDown(BPoint where)
161{
162	if (where.x < fCellWidth[kPositionCell]) {
163		_ShowDirMenu();
164		return;
165	}
166
167	if (!fReadOnly || !fCanUnlock)
168		return;
169
170	float left = fCellWidth[kPositionCell] + fCellWidth[kEncodingCell];
171	if (where.x < left)
172		return;
173
174	int32 clicks = 0;
175	BMessage* message = Window()->CurrentMessage();
176	if (message != NULL
177		&& message->FindInt32("clicks", &clicks) == B_OK && clicks > 1)
178			return;
179
180	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
181	menu->AddItem(new BMenuItem(B_TRANSLATE("Unlock file"),
182					new BMessage(UNLOCK_FILE)));
183	where.x = left;
184	where.y = Bounds().bottom;
185
186	ConvertToScreen(&where);
187	menu->SetTargetForItems(this);
188	menu->Go(where, true, true,	true);
189}
190
191
192void
193StatusView::SetStatus(BMessage* message)
194{
195	int32 line = 0, column = 0;
196	if (B_OK == message->FindInt32("line", &line)
197		&& B_OK == message->FindInt32("column", &column))
198	{
199		char info[256];
200		snprintf(info, sizeof(info), B_TRANSLATE("line %d, column %d"), (int)line, (int)column);
201		fCellText[kPositionCell].SetTo(info);
202	}
203
204	if (B_OK == message->FindString("encoding", &fEncoding)) {
205		// sometime corresponding Int-32 "encoding" attrib is read as string :(
206		if (fEncoding.Length() == 0
207			|| fEncoding.Compare("\xff\xff") == 0
208			|| fEncoding.Compare("UTF-8") == 0)
209		{
210			// do not display default UTF-8 encoding
211			fCellText[kEncodingCell].Truncate(0);
212			fEncoding.Truncate(0);
213		} else {
214			const BCharacterSet* charset
215				= BCharacterSetRoster::FindCharacterSetByName(fEncoding);
216			fCellText[kEncodingCell]
217				= charset != NULL ? charset->GetPrintName() : "";
218		}
219	}
220
221	bool modified = false;
222	fReadOnly = false;
223	fCanUnlock = false;
224	if (B_OK == message->FindBool("modified", &modified) && modified) {
225		fCellText[kFileStateCell] = B_TRANSLATE("Modified");
226	} else if (B_OK == message->FindBool("readOnly", &fReadOnly) && fReadOnly) {
227		fCellText[kFileStateCell] = B_TRANSLATE("Read-only");
228	    if (B_OK == message->FindBool("canUnlock", &fCanUnlock) && fCanUnlock)
229			fCellText[kFileStateCell] << " " UTF8_EXPAND_ARROW;
230	} else
231		fCellText[kFileStateCell].Truncate(0);
232
233	_ValidatePreferredSize();
234	Invalidate();
235}
236
237
238void
239StatusView::SetRef(const entry_ref& ref)
240{
241	fRef = ref;
242}
243
244
245void
246StatusView::_ValidatePreferredSize()
247{
248	float orgWidth = fPreferredSize.width;
249	// width
250	fPreferredSize.width = 0.f;
251	for (size_t i = 0; i < kStatusCellCount; i++) {
252		if (fCellText[i].Length() == 0) {
253			fCellWidth[i] = 0;
254			continue;
255		}
256		float width = ceilf(StringWidth(fCellText[i]));
257		if (width > 0)
258			width += kHorzSpacing * 2;
259		if (width > fCellWidth[i] || i != kPositionCell)
260			fCellWidth[i] = width;
261		fPreferredSize.width += fCellWidth[i];
262	}
263
264	// height
265	font_height fontHeight;
266	GetFontHeight(&fontHeight);
267
268	fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent
269		+ fontHeight.leading);
270
271	if (fPreferredSize.height < B_H_SCROLL_BAR_HEIGHT)
272		fPreferredSize.height = B_H_SCROLL_BAR_HEIGHT;
273
274	float delta = fPreferredSize.width - orgWidth;
275	ResizeBy(delta, 0);
276	BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
277	scrollBar->ResizeBy(-delta, 0);
278	scrollBar->MoveBy(delta, 0);
279}
280
281
282void
283StatusView::_ShowDirMenu()
284{
285	BEntry entry;
286	status_t status = entry.SetTo(&fRef);
287
288	if (status != B_OK || !entry.Exists())
289		return;
290
291	BPrivate::BDirMenu* menu = new BDirMenu(NULL,
292		BMessenger(kTrackerSignature), B_REFS_RECEIVED);
293
294	menu->Populate(&entry, Window(), false, false, true, false, true);
295
296	BPoint point = Bounds().LeftBottom();
297	point.y += 3;
298	ConvertToScreen(&point);
299	BRect clickToOpenRect(Bounds());
300	ConvertToScreen(&clickToOpenRect);
301	menu->Go(point, true, true, clickToOpenRect);
302	delete menu;
303}
304
305