xref: /haiku/src/kits/tracker/CountView.cpp (revision bd7645a12ab594d5eb1c6d6a70c3a82a429410dd)
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 <ControlLook.h>
41 
42 #include "AutoLock.h"
43 #include "Bitmaps.h"
44 #include "ContainerWindow.h"
45 #include "DirMenu.h"
46 #include "PoseView.h"
47 
48 
49 const bigtime_t kBarberPoleDelay = 500000;
50 
51 
52 BCountView::BCountView(BRect bounds, BPoseView* view)
53 	: BView(bounds, "CountVw", B_FOLLOW_LEFT + B_FOLLOW_BOTTOM,
54 			B_PULSE_NEEDED | B_WILL_DRAW),
55 	fLastCount(-1),
56 	fPoseView(view),
57 	fShowingBarberPole(false),
58 	fBorderHighlighted(false),
59 	fBarberPoleMap(NULL),
60 	fLastBarberPoleOffset(5),
61 	fStartSpinningAfter(0),
62 	fTypeAheadString(""),
63 	fFilterString("")
64 {
65  	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE,
66  		R_BarberPoleBitmap, &fBarberPoleMap);
67 }
68 
69 
70 BCountView::~BCountView()
71 {
72 	delete fBarberPoleMap;
73 }
74 
75 
76 void
77 BCountView::TrySpinningBarberPole()
78 {
79 	if (!fShowingBarberPole)
80 		return;
81 
82 	if (fStartSpinningAfter && system_time() < fStartSpinningAfter)
83 		return;
84 
85 	// When the barber pole just starts spinning we need to invalidate
86 	// the whole rectangle of text and barber pole.
87 	// After this the text needs no updating since only the pole changes.
88 	if (fStartSpinningAfter) {
89 		fStartSpinningAfter = 0;
90 		Invalidate(TextAndBarberPoleRect());
91 	} else
92 		Invalidate(BarberPoleInnerRect());
93 }
94 
95 
96 void
97 BCountView::Pulse()
98 {
99 	TrySpinningBarberPole();
100 }
101 
102 
103 void
104 BCountView::WindowActivated(bool active)
105 {
106 	if (fBorderHighlighted) {
107 		BRect dirty(Bounds());
108 		dirty.bottom = dirty.top;
109 		Invalidate(dirty);
110 	}
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, 2);
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, 2);
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 	if (be_control_look != NULL) {
202 		rgb_color base = ViewColor();
203 		if (fBorderHighlighted && Window()->IsActive())
204 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
205 		else
206 			SetHighColor(tint_color(base, B_DARKEN_2_TINT));
207 		StrokeLine(bounds.LeftTop(), bounds.RightTop());
208 		bounds.top++;
209 		be_control_look->DrawMenuBarBackground(this, bounds, updateRect,
210 			ViewColor());
211 	}
212 
213 	BString itemString;
214 	if (IsTypingAhead())
215 		itemString << TypeAhead();
216 	else if (IsFiltering()) {
217 		itemString << fLastCount << " " << Filter();
218 	} else {
219 		if (fLastCount == 0)
220 			itemString << "no items";
221 		else if (fLastCount == 1)
222 			itemString << "1 item";
223 		else
224 			itemString << fLastCount << " items";
225 	}
226 
227 	BString string(itemString);
228 	BRect textRect(TextInvalRect());
229 
230 	TruncateString(&string, 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(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT));
237 	} else
238 		SetHighColor(0, 0, 0);
239 
240 	MovePenTo(textRect.LeftBottom());
241 	DrawString(string.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 	rgb_color lightShadow = tint_color(ViewColor(), B_DARKEN_1_TINT);
248 
249 	BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5);
250 
251 	if (be_control_look == NULL) {
252 		AddLine(bounds.LeftTop(), bounds.RightTop(), light);
253 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), light);
254 		bounds.top--;
255 
256 		AddLine(bounds.LeftTop(), bounds.RightTop(), shadow);
257 		AddLine(BPoint(bounds.right, bounds.top + 2), bounds.RightBottom(), lightShadow);
258 		AddLine(bounds.LeftBottom(), bounds.RightBottom(), lightShadow);
259 	}
260 
261 	if (!fShowingBarberPole || fStartSpinningAfter) {
262 		EndLineArray();
263 		return;
264 	}
265 
266 	BRect barberPoleRect(BarberPoleOuterRect());
267 
268 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
269 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
270 	AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
271 	AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
272 	EndLineArray();
273 
274 	barberPoleRect.InsetBy(1, 1);
275 
276 	BRect destRect(fBarberPoleMap ? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
277 	destRect.OffsetTo(barberPoleRect.LeftTop() - BPoint(0, fLastBarberPoleOffset));
278 	fLastBarberPoleOffset -= 1;
279 	if (fLastBarberPoleOffset < 0)
280 		fLastBarberPoleOffset = 5;
281 
282 	BRegion region;
283 	region.Set(BarberPoleInnerRect());
284 	ConstrainClippingRegion(&region);
285 
286 	if (fBarberPoleMap)
287 		DrawBitmap(fBarberPoleMap, destRect);
288 }
289 
290 
291 void
292 BCountView::MouseDown(BPoint)
293 {
294 	BContainerWindow *window = dynamic_cast<BContainerWindow *>(Window());
295 	window->Activate();
296 	window->UpdateIfNeeded();
297 
298 	if (fPoseView->IsFilePanel() || !fPoseView->TargetModel())
299 		return;
300 
301 	if (!window->TargetModel()->IsRoot()) {
302 		BDirMenu *menu = new BDirMenu(NULL, B_REFS_RECEIVED);
303 		BEntry entry;
304 		if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK)
305 			menu->Populate(&entry, Window(), false, false, true, false, true);
306 		else
307 			menu->Populate(NULL, Window(), false, false, true, false, true);
308 
309 		menu->SetTargetForItems(be_app);
310 		BPoint pop_pt = Bounds().LeftBottom();
311 		pop_pt.y += 3;
312 		ConvertToScreen(&pop_pt);
313 		BRect mouse_rect(Bounds());
314 		ConvertToScreen(&mouse_rect);
315 		menu->Go(pop_pt, true, true, mouse_rect);
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 
393 
394 void
395 BCountView::SetBorderHighlighted(bool highlighted)
396 {
397 	if (fBorderHighlighted == highlighted)
398 		return;
399 
400 	fBorderHighlighted = highlighted;
401 	BRect dirty(Bounds());
402 	dirty.bottom = dirty.top;
403 	Invalidate(dirty);
404 }
405