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