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