xref: /haiku/src/kits/tracker/CountView.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 <StringFormat.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 static const float kMinFontSize = 8.0f;
60 
61 
62 //	#pragma mark - BCountView
63 
64 
65 BCountView::BCountView(BPoseView* view)
66 	:
67 	BView("CountVw", B_PULSE_NEEDED | B_WILL_DRAW),
68 	fLastCount(-1),
69 	fPoseView(view),
70 	fShowingBarberPole(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::EndBarberPole()
117 {
118 	if (!fShowingBarberPole)
119 		return;
120 
121 	fShowingBarberPole = false;
122 	Invalidate();
123 }
124 
125 
126 void
127 BCountView::StartBarberPole()
128 {
129 	AutoLock<BWindow> lock(Window());
130 	if (fShowingBarberPole)
131 		return;
132 
133 	fShowingBarberPole = true;
134 	fStartSpinningAfter = system_time() + kBarberPoleDelay;
135 		// wait a bit before showing the barber pole
136 }
137 
138 
139 BRect
140 BCountView::BarberPoleInnerRect() const
141 {
142 	BRect result = Bounds();
143 	result.InsetBy(3, 4);
144 	result.left = result.right - 7;
145 	result.bottom = result.top + 7;
146 	return result;
147 }
148 
149 
150 BRect
151 BCountView::BarberPoleOuterRect() const
152 {
153 	BRect result(BarberPoleInnerRect());
154 	result.InsetBy(-1, -1);
155 	return result;
156 }
157 
158 
159 BRect
160 BCountView::TextInvalRect() const
161 {
162 	BRect result = TextAndBarberPoleRect();
163 
164 	// if the barber pole is not present, use its space for text
165 	if (fShowingBarberPole)
166 		result.right -= 10;
167 
168 	return result;
169 }
170 
171 
172 BRect
173 BCountView::TextAndBarberPoleRect() const
174 {
175 	BRect result = Bounds();
176 	result.InsetBy(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 2,
177 		floorf(result.Height() * 0.25f));
178 
179 	return result;
180 }
181 
182 
183 void
184 BCountView::CheckCount()
185 {
186 	// invalidate the count text area if necessary
187 	if (fLastCount != fPoseView->CountItems()) {
188 		fLastCount = fPoseView->CountItems();
189 		Invalidate(TextInvalRect());
190 	}
191 
192 	// invalidate barber pole area if necessary
193 	TrySpinningBarberPole();
194 }
195 
196 
197 void
198 BCountView::Draw(BRect updateRect)
199 {
200 	BRect bounds(Bounds());
201 
202 	rgb_color color = ViewColor();
203 	if (IsTypingAhead())
204 		color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
205 
206 	SetLowColor(color);
207 	be_control_look->DrawBorder(this, bounds, updateRect,
208 		color, B_PLAIN_BORDER, 0,
209 		BControlLook::B_BOTTOM_BORDER | BControlLook::B_LEFT_BORDER);
210 	be_control_look->DrawMenuBarBackground(this, bounds, updateRect, color);
211 
212 	BString itemString;
213 	if (IsTypingAhead())
214 		itemString << TypeAhead();
215 	else if (IsFiltering()) {
216 		itemString << fLastCount << " " << Filter();
217 	} else {
218 		if (fLastCount == 0)
219 			itemString << B_TRANSLATE("no items");
220 		else {
221 			static BStringFormat format(B_TRANSLATE_COMMENT(
222 				"{0, plural, one{# item} other{# items}}",
223 				"Number of selected items: \"1 item\" or \"2 items\""));
224 			format.Format(itemString, fLastCount);
225 		}
226 	}
227 
228 	BRect textRect(TextInvalRect());
229 
230 	TruncateString(&itemString, IsTypingAhead() ? B_TRUNCATE_BEGINNING
231 			: IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END,
232 		textRect.Width());
233 
234 	if (IsTypingAhead()) {
235 		// use a muted gray for the typeahead
236 		SetHighColor(ui_color(B_DOCUMENT_TEXT_COLOR));
237 	} else
238 		SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
239 
240 	MovePenTo(textRect.LeftBottom());
241 	DrawString(itemString.String());
242 
243 	bounds.top++;
244 
245 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
246 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
247 
248 	BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5);
249 
250 	if (!fShowingBarberPole || fStartSpinningAfter) {
251 		EndLineArray();
252 		return;
253 	}
254 
255 	BRect barberPoleRect(BarberPoleOuterRect());
256 
257 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
258 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
259 	AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
260 	AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
261 	EndLineArray();
262 
263 	barberPoleRect.InsetBy(1, 1);
264 
265 	BRect destRect(fBarberPoleMap
266 		? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
267 	destRect.OffsetTo(barberPoleRect.LeftTop()
268 		- BPoint(0, fLastBarberPoleOffset));
269 	fLastBarberPoleOffset -= 1;
270 	if (fLastBarberPoleOffset < 0)
271 		fLastBarberPoleOffset = 5;
272 
273 	BRegion region;
274 	region.Set(BarberPoleInnerRect());
275 	ConstrainClippingRegion(&region);
276 
277 	if (fBarberPoleMap)
278 		DrawBitmap(fBarberPoleMap, destRect);
279 }
280 
281 
282 void
283 BCountView::MouseDown(BPoint)
284 {
285 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
286 	ThrowOnAssert(window != NULL);
287 
288 	window->Activate();
289 	window->UpdateIfNeeded();
290 
291 	if (fPoseView->IsFilePanel() || fPoseView->TargetModel() == NULL)
292 		return;
293 
294 	if (!window->TargetModel()->IsRoot()) {
295 		BDirMenu* menu = new BDirMenu(NULL, be_app, B_REFS_RECEIVED);
296 		BEntry entry;
297 		if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK)
298 			menu->Populate(&entry, Window(), false, false, true, false, true);
299 		else
300 			menu->Populate(NULL, Window(), false, false, true, false, true);
301 
302 		BPoint point = Bounds().LeftBottom();
303 		point.y += 3;
304 		ConvertToScreen(&point);
305 		BRect clickToOpenRect(Bounds());
306 		ConvertToScreen(&clickToOpenRect);
307 		menu->Go(point, true, true, clickToOpenRect);
308 		delete menu;
309 	}
310 }
311 
312 
313 void
314 BCountView::AttachedToWindow()
315 {
316 	SetFont(be_plain_font);
317 	SetFontSize(std::max(kMinFontSize,
318 		ceilf(be_plain_font->Size() * 0.75f)));
319 
320 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
321 	SetLowUIColor(ViewUIColor());
322 
323 	CheckCount();
324 }
325 
326 
327 void
328 BCountView::SetTypeAhead(const char* string)
329 {
330 	fTypeAheadString = string;
331 	Invalidate();
332 }
333 
334 
335 const char*
336 BCountView::TypeAhead() const
337 {
338 	return fTypeAheadString.String();
339 }
340 
341 
342 bool
343 BCountView::IsTypingAhead() const
344 {
345 	return fTypeAheadString.Length() != 0;
346 }
347 
348 
349 void
350 BCountView::AddFilterCharacter(const char* character)
351 {
352 	fFilterString.AppendChars(character, 1);
353 	Invalidate();
354 }
355 
356 
357 void
358 BCountView::RemoveFilterCharacter()
359 {
360 	fFilterString.TruncateChars(fFilterString.CountChars() - 1);
361 	Invalidate();
362 }
363 
364 
365 void
366 BCountView::CancelFilter()
367 {
368 	fFilterString.Truncate(0);
369 	Invalidate();
370 }
371 
372 
373 const char*
374 BCountView::Filter() const
375 {
376 	return fFilterString.String();
377 }
378 
379 
380 bool
381 BCountView::IsFiltering() const
382 {
383 	return fFilterString.Length() > 0;
384 }
385