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