xref: /haiku/src/kits/tracker/CountView.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 (be_control_look == NULL) {
248 		AddLine(bounds.LeftTop(), bounds.RightTop(), light);
249 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), light);
250 		bounds.top--;
251 
252 		AddLine(bounds.LeftTop(), bounds.RightTop(), shadow);
253 		AddLine(BPoint(bounds.right, bounds.top + 2), bounds.RightBottom(),
254 			lightShadow);
255 		AddLine(bounds.LeftBottom(), bounds.RightBottom(), lightShadow);
256 	}
257 
258 	if (!fShowingBarberPole || fStartSpinningAfter) {
259 		EndLineArray();
260 		return;
261 	}
262 
263 	BRect barberPoleRect(BarberPoleOuterRect());
264 
265 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
266 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
267 	AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
268 	AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
269 	EndLineArray();
270 
271 	barberPoleRect.InsetBy(1, 1);
272 
273 	BRect destRect(fBarberPoleMap
274 		? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
275 	destRect.OffsetTo(barberPoleRect.LeftTop()
276 		- BPoint(0, fLastBarberPoleOffset));
277 	fLastBarberPoleOffset -= 1;
278 	if (fLastBarberPoleOffset < 0)
279 		fLastBarberPoleOffset = 5;
280 
281 	BRegion region;
282 	region.Set(BarberPoleInnerRect());
283 	ConstrainClippingRegion(&region);
284 
285 	if (fBarberPoleMap)
286 		DrawBitmap(fBarberPoleMap, destRect);
287 }
288 
289 
290 void
291 BCountView::MouseDown(BPoint)
292 {
293 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
294 	ThrowOnAssert(window != NULL);
295 
296 	window->Activate();
297 	window->UpdateIfNeeded();
298 
299 	if (fPoseView->IsFilePanel() || fPoseView->TargetModel() == NULL)
300 		return;
301 
302 	if (!window->TargetModel()->IsRoot()) {
303 		BDirMenu* menu = new BDirMenu(NULL, be_app, B_REFS_RECEIVED);
304 		BEntry entry;
305 		if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK)
306 			menu->Populate(&entry, Window(), false, false, true, false, true);
307 		else
308 			menu->Populate(NULL, Window(), false, false, true, false, true);
309 
310 		BPoint point = Bounds().LeftBottom();
311 		point.y += 3;
312 		ConvertToScreen(&point);
313 		BRect clickToOpenRect(Bounds());
314 		ConvertToScreen(&clickToOpenRect);
315 		menu->Go(point, true, true, clickToOpenRect);
316 		delete menu;
317 	}
318 }
319 
320 
321 void
322 BCountView::AttachedToWindow()
323 {
324 	SetFont(be_plain_font);
325 	SetFontSize(9);
326 
327 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
328 	SetLowColor(ViewColor());
329 
330 	CheckCount();
331 }
332 
333 
334 void
335 BCountView::SetTypeAhead(const char* string)
336 {
337 	fTypeAheadString = string;
338 	Invalidate();
339 }
340 
341 
342 const char*
343 BCountView::TypeAhead() const
344 {
345 	return fTypeAheadString.String();
346 }
347 
348 
349 bool
350 BCountView::IsTypingAhead() const
351 {
352 	return fTypeAheadString.Length() != 0;
353 }
354 
355 
356 void
357 BCountView::AddFilterCharacter(const char* character)
358 {
359 	fFilterString.AppendChars(character, 1);
360 	Invalidate();
361 }
362 
363 
364 void
365 BCountView::RemoveFilterCharacter()
366 {
367 	fFilterString.TruncateChars(fFilterString.CountChars() - 1);
368 	Invalidate();
369 }
370 
371 
372 void
373 BCountView::CancelFilter()
374 {
375 	fFilterString.Truncate(0);
376 	Invalidate();
377 }
378 
379 
380 const char*
381 BCountView::Filter() const
382 {
383 	return fFilterString.String();
384 }
385 
386 
387 bool
388 BCountView::IsFiltering() const
389 {
390 	return fFilterString.Length() > 0;
391 }
392