xref: /haiku/src/kits/tracker/CountView.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 {
64  	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE,
65  		kResBarberPoleBitmap, &fBarberPoleMap);
66 }
67 
68 
69 BCountView::~BCountView()
70 {
71 	delete fBarberPoleMap;
72 }
73 
74 
75 void
76 BCountView::TrySpinningBarberPole()
77 {
78 	if (!fShowingBarberPole)
79 		return;
80 
81 	if (fStartSpinningAfter && system_time() < fStartSpinningAfter)
82 		return;
83 
84 	// When the barber pole just starts spinning we need to invalidate
85 	// the whole rectangle of text and barber pole.
86 	// After this the text needs no updating since only the pole changes.
87 	if (fStartSpinningAfter) {
88 		fStartSpinningAfter = 0;
89 		Invalidate(TextAndBarberPoleRect());
90 	} else
91 		Invalidate(BarberPoleInnerRect());
92 }
93 
94 
95 void
96 BCountView::Pulse()
97 {
98 	TrySpinningBarberPole();
99 }
100 
101 
102 void
103 BCountView::WindowActivated(bool active)
104 {
105 	if (fBorderHighlighted) {
106 		BRect dirty(Bounds());
107 		dirty.bottom = dirty.top;
108 		Invalidate(dirty);
109 	}
110 }
111 
112 
113 void
114 BCountView::EndBarberPole()
115 {
116 	if (!fShowingBarberPole)
117 		return;
118 
119 	fShowingBarberPole = false;
120 	Invalidate();
121 }
122 
123 
124 void
125 BCountView::StartBarberPole()
126 {
127 	AutoLock<BWindow> lock(Window());
128 	if (fShowingBarberPole)
129 		return;
130 
131 	fShowingBarberPole = true;
132 	fStartSpinningAfter = system_time() + kBarberPoleDelay;
133 		// wait a bit before showing the barber pole
134 }
135 
136 
137 BRect
138 BCountView::BarberPoleInnerRect() const
139 {
140 	BRect result = Bounds();
141 	result.InsetBy(3, 4);
142 	result.left = result.right - 7;
143 	result.bottom = result.top + 7;
144 	return result;
145 }
146 
147 
148 BRect
149 BCountView::BarberPoleOuterRect() const
150 {
151 	BRect result(BarberPoleInnerRect());
152 	result.InsetBy(-1, -1);
153 	return result;
154 }
155 
156 
157 BRect
158 BCountView::TextInvalRect() const
159 {
160 	BRect result = Bounds();
161 	result.InsetBy(4, 2);
162 
163 	// if the barber pole is not present, use its space for text
164 	if (fShowingBarberPole)
165 		result.right -= 10;
166 
167 	return result;
168 }
169 
170 
171 BRect
172 BCountView::TextAndBarberPoleRect() const
173 {
174 	BRect result = Bounds();
175 	result.InsetBy(4, 2);
176 
177 	return result;
178 }
179 
180 
181 void
182 BCountView::CheckCount()
183 {
184 	// invalidate the count text area if necessary
185 	if (fLastCount != fPoseView->CountItems()) {
186 		fLastCount = fPoseView->CountItems();
187 		Invalidate(TextInvalRect());
188 	}
189 	// invalidate barber pole area if necessary
190 	TrySpinningBarberPole();
191 }
192 
193 
194 void
195 BCountView::Draw(BRect updateRect)
196 {
197 	BRect bounds(Bounds());
198 
199 	if (be_control_look != NULL) {
200 		rgb_color base = ViewColor();
201 		if (fBorderHighlighted && Window()->IsActive())
202 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
203 		else
204 			SetHighColor(tint_color(base, B_DARKEN_2_TINT));
205 		StrokeLine(bounds.LeftTop(), bounds.RightTop());
206 		bounds.top++;
207 		be_control_look->DrawMenuBarBackground(this, bounds, updateRect,
208 			ViewColor());
209 	}
210 
211 	BString itemString;
212 	if (!IsTypingAhead()) {
213 		if (fLastCount == 0)
214 			itemString << "no items";
215 		else if (fLastCount == 1)
216 			itemString << "1 item";
217 		else
218 			itemString << fLastCount << " items";
219 	} else
220 		itemString << TypeAhead();
221 
222 	BString string(itemString);
223 	BRect textRect(TextInvalRect());
224 
225 	TruncateString(&string, B_TRUNCATE_END, textRect.Width());
226 
227 	if (IsTypingAhead()) {
228 		// use a muted gray for the typeahead
229 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT));
230 	} else
231 		SetHighColor(0, 0, 0);
232 
233 	MovePenTo(textRect.LeftBottom());
234 	DrawString(string.String());
235 
236 	bounds.top++;
237 
238 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
239 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
240 	rgb_color lightShadow = tint_color(ViewColor(), B_DARKEN_1_TINT);
241 
242 	BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5);
243 
244 	if (be_control_look == NULL) {
245 		AddLine(bounds.LeftTop(), bounds.RightTop(), light);
246 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), light);
247 		bounds.top--;
248 
249 		AddLine(bounds.LeftTop(), bounds.RightTop(), shadow);
250 		AddLine(BPoint(bounds.right, bounds.top + 2), bounds.RightBottom(), lightShadow);
251 		AddLine(bounds.LeftBottom(), bounds.RightBottom(), lightShadow);
252 	}
253 
254 	if (!fShowingBarberPole || fStartSpinningAfter) {
255 		EndLineArray();
256 		return;
257 	}
258 
259 	BRect barberPoleRect(BarberPoleOuterRect());
260 
261 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
262 	AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
263 	AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
264 	AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
265 	EndLineArray();
266 
267 	barberPoleRect.InsetBy(1, 1);
268 
269 	BRect destRect(fBarberPoleMap ? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
270 	destRect.OffsetTo(barberPoleRect.LeftTop() - BPoint(0, fLastBarberPoleOffset));
271 	fLastBarberPoleOffset -= 1;
272 	if (fLastBarberPoleOffset < 0)
273 		fLastBarberPoleOffset = 5;
274 
275 	BRegion region;
276 	region.Set(BarberPoleInnerRect());
277 	ConstrainClippingRegion(&region);
278 
279 	if (fBarberPoleMap)
280 		DrawBitmap(fBarberPoleMap, destRect);
281 }
282 
283 
284 void
285 BCountView::MouseDown(BPoint)
286 {
287 	BContainerWindow *window = dynamic_cast<BContainerWindow *>(Window());
288 	window->Activate();
289 	window->UpdateIfNeeded();
290 
291 	if (fPoseView->IsFilePanel() || !fPoseView->TargetModel())
292 		return;
293 
294 	if (!window->TargetModel()->IsRoot()) {
295 		BDirMenu *menu = new BDirMenu(NULL, 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 		menu->SetTargetForItems(be_app);
303 		BPoint pop_pt = Bounds().LeftBottom();
304 		pop_pt.y += 3;
305 		ConvertToScreen(&pop_pt);
306 		BRect mouse_rect(Bounds());
307 		ConvertToScreen(&mouse_rect);
308 		menu->Go(pop_pt, true, true, mouse_rect);
309 		delete menu;
310 	}
311 }
312 
313 
314 void
315 BCountView::AttachedToWindow()
316 {
317 	SetFont(be_plain_font);
318 	SetFontSize(9);
319 
320 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
321 	SetLowColor(ViewColor());
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::SetBorderHighlighted(bool highlighted)
351 {
352 	if (fBorderHighlighted == highlighted)
353 		return;
354 
355 	fBorderHighlighted = highlighted;
356 	BRect dirty(Bounds());
357 	dirty.bottom = dirty.top;
358 	Invalidate(dirty);
359 }
360