xref: /haiku/src/kits/tracker/CountView.cpp (revision 32832cbe47f991cc6d2b29824903181d8baaaa63)
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 // defines the status area drawn in the bottom left corner of a Tracker window
36 
37 
38 #include "CountView.h"
39 
40 #include <Application.h>
41 #include <Catalog.h>
42 #include <ControlLook.h>
43 #include <Locale.h>
44 
45 #include "AutoLock.h"
46 #include "Bitmaps.h"
47 #include "ContainerWindow.h"
48 #include "DirMenu.h"
49 #include "PoseView.h"
50 #include "Utilities.h"
51 
52 
53 #undef B_TRANSLATION_CONTEXT
54 #define B_TRANSLATION_CONTEXT "CountView"
55 
56 
57 const bigtime_t kBarberPoleDelay = 500000;
58 
59 
60 //	#pragma mark - BCountView
61 
62 
63 BCountView::BCountView(BRect bounds, BPoseView* view)
64 	:
65 	BView(bounds, "CountVw", B_FOLLOW_LEFT + B_FOLLOW_BOTTOM,
66 		B_PULSE_NEEDED | B_WILL_DRAW),
67 	fLastCount(-1),
68 	fPoseView(view),
69 	fShowingBarberPole(false),
70 	fBorderHighlighted(false),
71 	fBarberPoleMap(NULL),
72 	fLastBarberPoleOffset(5),
73 	fStartSpinningAfter(0),
74 	fTypeAheadString(""),
75 	fFilterString("")
76 {
77  	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE,
78  		R_BarberPoleBitmap, &fBarberPoleMap);
79 }
80 
81 
82 BCountView::~BCountView()
83 {
84 	delete fBarberPoleMap;
85 }
86 
87 
88 void
89 BCountView::TrySpinningBarberPole()
90 {
91 	if (!fShowingBarberPole)
92 		return;
93 
94 	if (fStartSpinningAfter && system_time() < fStartSpinningAfter)
95 		return;
96 
97 	// When the barber pole just starts spinning we need to invalidate
98 	// the whole rectangle of text and barber pole.
99 	// After this the text needs no updating since only the pole changes.
100 	if (fStartSpinningAfter) {
101 		fStartSpinningAfter = 0;
102 		Invalidate(TextAndBarberPoleRect());
103 	} else
104 		Invalidate(BarberPoleInnerRect());
105 }
106 
107 
108 void
109 BCountView::Pulse()
110 {
111 	TrySpinningBarberPole();
112 }
113 
114 
115 void
116 BCountView::WindowActivated(bool active)
117 {
118 	if (fBorderHighlighted) {
119 		BRect dirty(Bounds());
120 		dirty.bottom = dirty.top;
121 		Invalidate(dirty);
122 	}
123 }
124 
125 
126 void
127 BCountView::EndBarberPole()
128 {
129 	if (!fShowingBarberPole)
130 		return;
131 
132 	fShowingBarberPole = false;
133 	Invalidate();
134 }
135 
136 
137 void
138 BCountView::StartBarberPole()
139 {
140 	AutoLock<BWindow> lock(Window());
141 	if (fShowingBarberPole)
142 		return;
143 
144 	fShowingBarberPole = true;
145 	fStartSpinningAfter = system_time() + kBarberPoleDelay;
146 		// wait a bit before showing the barber pole
147 }
148 
149 
150 BRect
151 BCountView::BarberPoleInnerRect() const
152 {
153 	BRect result = Bounds();
154 	result.InsetBy(3, 4);
155 	result.left = result.right - 7;
156 	result.bottom = result.top + 7;
157 	return result;
158 }
159 
160 
161 BRect
162 BCountView::BarberPoleOuterRect() const
163 {
164 	BRect result(BarberPoleInnerRect());
165 	result.InsetBy(-1, -1);
166 	return result;
167 }
168 
169 
170 BRect
171 BCountView::TextInvalRect() const
172 {
173 	BRect result = Bounds();
174 	result.InsetBy(4, 2);
175 
176 	// if the barber pole is not present, use its space for text
177 	if (fShowingBarberPole)
178 		result.right -= 10;
179 
180 	return result;
181 }
182 
183 
184 BRect
185 BCountView::TextAndBarberPoleRect() const
186 {
187 	BRect result = Bounds();
188 	result.InsetBy(4, 2);
189 
190 	return result;
191 }
192 
193 
194 void
195 BCountView::CheckCount()
196 {
197 	// invalidate the count text area if necessary
198 	if (fLastCount != fPoseView->CountItems()) {
199 		fLastCount = fPoseView->CountItems();
200 		Invalidate(TextInvalRect());
201 	}
202 
203 	// invalidate barber pole area if necessary
204 	TrySpinningBarberPole();
205 }
206 
207 
208 void
209 BCountView::Draw(BRect updateRect)
210 {
211 	BRect bounds(Bounds());
212 
213 	if (be_control_look != NULL) {
214 		rgb_color base = ViewColor();
215 		if (fBorderHighlighted && Window()->IsActive())
216 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
217 		else
218 			SetHighColor(tint_color(base, B_DARKEN_2_TINT));
219 		StrokeLine(bounds.LeftTop(), bounds.RightTop());
220 		bounds.top++;
221 		be_control_look->DrawMenuBarBackground(this, bounds, updateRect,
222 			ViewColor());
223 	}
224 
225 	BString itemString;
226 	if (IsTypingAhead())
227 		itemString << TypeAhead();
228 	else if (IsFiltering()) {
229 		itemString << fLastCount << " " << Filter();
230 	} else {
231 		if (fLastCount == 0)
232 			itemString << B_TRANSLATE("no items");
233 		else if (fLastCount == 1)
234 			itemString << B_TRANSLATE("1 item");
235 		else {
236 			itemString.SetTo(B_TRANSLATE("%num items"));
237 			char numString[256];
238 			snprintf(numString, sizeof(numString), "%" B_PRId32, fLastCount);
239 			itemString.ReplaceFirst("%num", numString);
240 		}
241 	}
242 
243 	BRect textRect(TextInvalRect());
244 
245 	TruncateString(&itemString, IsTypingAhead() ? B_TRUNCATE_BEGINNING
246 			: IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END,
247 		textRect.Width());
248 
249 	if (IsTypingAhead()) {
250 		// use a muted gray for the typeahead
251 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
252 			B_DARKEN_4_TINT));
253 	} else
254 		SetHighColor(0, 0, 0);
255 
256 	MovePenTo(textRect.LeftBottom());
257 	DrawString(itemString.String());
258 
259 	bounds.top++;
260 
261 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
262 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
263 	rgb_color lightShadow = tint_color(ViewColor(), B_DARKEN_1_TINT);
264 
265 	BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5);
266 
267 	if (be_control_look == NULL) {
268 		AddLine(bounds.LeftTop(), bounds.RightTop(), light);
269 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), light);
270 		bounds.top--;
271 
272 		AddLine(bounds.LeftTop(), bounds.RightTop(), shadow);
273 		AddLine(BPoint(bounds.right, bounds.top + 2), bounds.RightBottom(),
274 			lightShadow);
275 		AddLine(bounds.LeftBottom(), bounds.RightBottom(), lightShadow);
276 	}
277 
278 	if (!fShowingBarberPole || fStartSpinningAfter) {
279 		EndLineArray();
280 		return;
281 	}
282 
283 	BRect barberPoleRect(BarberPoleOuterRect());
284 
285 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
286 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
287 	AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
288 	AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
289 	EndLineArray();
290 
291 	barberPoleRect.InsetBy(1, 1);
292 
293 	BRect destRect(fBarberPoleMap
294 		? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
295 	destRect.OffsetTo(barberPoleRect.LeftTop()
296 		- BPoint(0, fLastBarberPoleOffset));
297 	fLastBarberPoleOffset -= 1;
298 	if (fLastBarberPoleOffset < 0)
299 		fLastBarberPoleOffset = 5;
300 
301 	BRegion region;
302 	region.Set(BarberPoleInnerRect());
303 	ConstrainClippingRegion(&region);
304 
305 	if (fBarberPoleMap)
306 		DrawBitmap(fBarberPoleMap, destRect);
307 }
308 
309 
310 void
311 BCountView::MouseDown(BPoint)
312 {
313 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
314 	window->Activate();
315 	window->UpdateIfNeeded();
316 
317 	if (fPoseView->IsFilePanel() || fPoseView->TargetModel() == NULL)
318 		return;
319 
320 	if (!window->TargetModel()->IsRoot()) {
321 		BDirMenu* menu = new BDirMenu(NULL, be_app, B_REFS_RECEIVED);
322 		BEntry entry;
323 		if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK)
324 			menu->Populate(&entry, Window(), false, false, true, false, true);
325 		else
326 			menu->Populate(NULL, Window(), false, false, true, false, true);
327 
328 		BPoint point = Bounds().LeftBottom();
329 		point.y += 3;
330 		ConvertToScreen(&point);
331 		BRect clickToOpenRect(Bounds());
332 		ConvertToScreen(&clickToOpenRect);
333 		menu->Go(point, true, true, clickToOpenRect);
334 		delete menu;
335 	}
336 }
337 
338 
339 void
340 BCountView::AttachedToWindow()
341 {
342 	SetFont(be_plain_font);
343 	SetFontSize(9);
344 
345 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
346 	SetLowColor(ViewColor());
347 
348 	CheckCount();
349 }
350 
351 
352 void
353 BCountView::SetTypeAhead(const char* string)
354 {
355 	fTypeAheadString = string;
356 	Invalidate();
357 }
358 
359 
360 const char*
361 BCountView::TypeAhead() const
362 {
363 	return fTypeAheadString.String();
364 }
365 
366 
367 bool
368 BCountView::IsTypingAhead() const
369 {
370 	return fTypeAheadString.Length() != 0;
371 }
372 
373 
374 void
375 BCountView::AddFilterCharacter(const char* character)
376 {
377 	fFilterString.AppendChars(character, 1);
378 	Invalidate();
379 }
380 
381 
382 void
383 BCountView::RemoveFilterCharacter()
384 {
385 	fFilterString.TruncateChars(fFilterString.CountChars() - 1);
386 	Invalidate();
387 }
388 
389 
390 void
391 BCountView::CancelFilter()
392 {
393 	fFilterString.Truncate(0);
394 	Invalidate();
395 }
396 
397 
398 const char*
399 BCountView::Filter() const
400 {
401 	return fFilterString.String();
402 }
403 
404 
405 bool
406 BCountView::IsFiltering() const
407 {
408 	return fFilterString.Length() > 0;
409 }
410 
411 
412 void
413 BCountView::SetBorderHighlighted(bool highlighted)
414 {
415 	if (fBorderHighlighted == highlighted)
416 		return;
417 
418 	fBorderHighlighted = highlighted;
419 	BRect dirty(Bounds());
420 	dirty.bottom = dirty.top;
421 	Invalidate(dirty);
422 }
423