xref: /haiku/src/apps/cortex/RouteApp/StatusView.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 // StatusView.cpp
2 
3 #include "StatusView.h"
4 #include "cortex_ui.h"
5 #include "RouteAppNodeManager.h"
6 #include "TipManager.h"
7 
8 // Application Kit
9 #include <Message.h>
10 #include <MessageRunner.h>
11 // Interface Kit
12 #include <Bitmap.h>
13 #include <Font.h>
14 #include <ScrollBar.h>
15 #include <Window.h>
16 // Support Kit
17 #include <Beep.h>
18 
19 __USE_CORTEX_NAMESPACE
20 
21 #include <Debug.h>
22 #define D_ALLOC(x) //PRINT(x)
23 #define D_HOOK(x) //PRINT(x)
24 #define D_MESSAGE(x) //PRINT(x)
25 #define D_OPERATION(x) //PRINT(x)
26 
27 // -------------------------------------------------------- //
28 // *** constants
29 // -------------------------------------------------------- //
30 
31 const bigtime_t TICK_PERIOD = 50000;
32 const bigtime_t TEXT_DECAY_DELAY = 10 * 1000 * 1000;
33 const bigtime_t TEXT_DECAY_TIME = 3 * 1000 * 1000;
34 
35 // width: 8, height:12, color_space: B_CMAP8
36 const unsigned char ERROR_ICON_BITS [] = {
37 	0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,
38 	0xff,0x00,0xfa,0xfa,0x00,0xff,0xff,0xff,
39 	0x00,0x3f,0x3f,0xfa,0xfa,0x00,0xff,0xff,
40 	0x00,0xf9,0xf9,0x3f,0x5d,0x00,0xff,0xff,
41 	0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
42 	0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
43 	0x00,0x00,0xf9,0x5d,0x00,0x00,0xff,0xff,
44 	0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,
45 	0x00,0xf9,0x00,0x00,0x5d,0x00,0xff,0xff,
46 	0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
47 	0x00,0x00,0xf9,0x5d,0x00,0x00,0x0f,0xff,
48 	0xff,0xff,0x00,0x00,0x00,0x0f,0x0f,0x0f,
49 };
50 
51 // width: 8, height:12, color_space: B_CMAP8
52 const unsigned char INFO_ICON_BITS [] = {
53 	0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,
54 	0xff,0x00,0x21,0x21,0x21,0x00,0xff,0xff,
55 	0xff,0x00,0x21,0x92,0x25,0x00,0xff,0xff,
56 	0xff,0x00,0x21,0x25,0x25,0x00,0xff,0xff,
57 	0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,
58 	0xff,0x00,0x92,0x92,0x25,0x00,0xff,0xff,
59 	0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff,
60 	0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff,
61 	0x00,0x00,0x21,0x21,0x25,0x00,0x00,0xff,
62 	0x00,0xbe,0x21,0x21,0x92,0xbe,0x25,0x00,
63 	0x00,0x00,0x21,0x21,0x21,0x25,0x00,0x0f,
64 	0xff,0xff,0x00,0x00,0x00,0x00,0x0f,0x0f,
65 };
66 
67 // -------------------------------------------------------- //
68 // *** ctor/dtor
69 // -------------------------------------------------------- //
70 
71 StatusView::StatusView(
72 	BRect frame,
73 	RouteAppNodeManager *manager,
74 	BScrollBar *scrollBar)
75 	:	BStringView(frame, "StatusView", "", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM,
76 					B_FRAME_EVENTS | B_WILL_DRAW),
77 		m_scrollBar(scrollBar),
78 		m_icon(0),
79 		m_opacity(1.0),
80 		m_clock(0),
81 		m_dragging(false),
82 		m_backBitmap(0),
83 		m_backView(0),
84 		m_dirty(true),
85 		m_manager(manager) {
86 	D_ALLOC(("StatusView::StatusView()\n"));
87 
88 	SetViewColor(B_TRANSPARENT_COLOR);
89 	SetFont(be_plain_font);
90 
91 	allocBackBitmap(frame.Width(), frame.Height());
92 }
93 
94 StatusView::~StatusView() {
95 	D_ALLOC(("StatusView::~ParameterContainerView()\n"));
96 
97 	// get the tip manager instance and reset
98 	TipManager *manager = TipManager::Instance();
99 	manager->removeAll(this);
100 
101 	delete m_clock;
102 
103 	freeBackBitmap();
104 }
105 
106 // -------------------------------------------------------- //
107 // *** BScrollView impl
108 // -------------------------------------------------------- //
109 
110 void StatusView::AttachedToWindow() {
111 	D_HOOK(("StatusView::AttachedToWindow()\n"));
112 
113 	if (m_manager) {
114 		m_manager->setLogTarget(BMessenger(this, Window()));
115 	}
116 
117 	allocBackBitmap(Bounds().Width(), Bounds().Height());
118 }
119 
120 void StatusView::Draw(
121 	BRect updateRect) {
122 	D_HOOK(("StatusView::Draw()\n"));
123 
124 	if(!m_backView) {
125 		drawInto(this, updateRect);
126 	} else {
127 		if(m_dirty) {
128 			m_backBitmap->Lock();
129 			drawInto(m_backView, updateRect);
130 			m_backView->Sync();
131 			m_backBitmap->Unlock();
132 			m_dirty = false;
133 		}
134 
135 		SetDrawingMode(B_OP_COPY);
136 		DrawBitmap(m_backBitmap, updateRect, updateRect);
137 	}
138 }
139 
140 void StatusView::FrameResized(
141 	float width,
142 	float height) {
143 	D_HOOK(("StatusView::FrameResized()\n"));
144 
145 	allocBackBitmap(width, height);
146 
147 	// get the tip manager instance and reset
148 	TipManager *manager = TipManager::Instance();
149 	manager->removeAll(this);
150 
151 	// re-truncate the string if necessary
152 	BString text = m_fullText;
153 	if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
154 		be_plain_font->TruncateString(&text, B_TRUNCATE_END,
155 									  Bounds().Width() - 25.0);
156 		manager->setTip(m_fullText.String(), this);
157 	}
158 	BStringView::SetText(text.String());
159 
160 	float minWidth, maxWidth, minHeight, maxHeight;
161 	Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
162 	minWidth = width + 6 * B_V_SCROLL_BAR_WIDTH;
163 	Window()->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
164 }
165 
166 void StatusView::MessageReceived(
167 	BMessage *message) {
168 	D_MESSAGE(("StatusView::MessageReceived()\n"));
169 
170 	switch (message->what) {
171 		case 'Tick':
172 			fadeTick();
173 			break;
174 
175 		case RouteAppNodeManager::M_LOG: {
176 			D_MESSAGE((" -> RouteAppNodeManager::M_LOG\n"));
177 
178 			BString title;
179 			if (message->FindString("title", &title) != B_OK) {
180 				return;
181 			}
182 			BString details, line;
183 			for (int32 i = 0; message->FindString("line", i, &line) == B_OK; i++) {
184 				if (details.CountChars() > 0) {
185 					details << "\n";
186 				}
187 				details << line;
188 			}
189 			status_t error = B_OK;
190 			message->FindInt32("error", &error);
191 			setMessage(title, details, error);
192 			break;
193 		}
194 		default: {
195 			BStringView::MessageReceived(message);
196 		}
197 	}
198 }
199 
200 void StatusView::MouseDown(
201 	BPoint point) {
202 	D_HOOK(("StatusView::MouseDown()\n"));
203 
204 	int32 buttons;
205 	if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) {
206 		buttons = B_PRIMARY_MOUSE_BUTTON;
207 	}
208 
209 	if (buttons == B_PRIMARY_MOUSE_BUTTON) {
210 		// drag rect
211 		BRect dragRect(Bounds());
212 		dragRect.left = dragRect.right - 10.0;
213 		if (dragRect.Contains(point)) {
214 			// resize
215 			m_dragging = true;
216 			SetMouseEventMask(B_POINTER_EVENTS,
217 							  B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
218 		}
219 	}
220 }
221 
222 void StatusView::MouseMoved(
223 	BPoint point,
224 	uint32 transit,
225 	const BMessage *message) {
226 	D_HOOK(("StatusView::MouseMoved()\n"));
227 
228 	if (m_dragging) {
229 		float x = point.x - (Bounds().right - 5.0);
230 		if ((Bounds().Width() + x) <= 16.0) {
231 			return;
232 		}
233 		if (m_scrollBar
234 		 && ((m_scrollBar->Bounds().Width() - x) <= (6 * B_V_SCROLL_BAR_WIDTH))) {
235 			return;
236 		}
237 		ResizeBy(x, 0.0);
238 		BRect r(Bounds());
239 		r.left = r.right - 5.0;
240 		if (x > 0)
241 			r.left -= x;
242 		m_dirty = true;
243 		Invalidate(r);
244 		if (m_scrollBar) {
245 			m_scrollBar->ResizeBy(-x, 0.0);
246 			m_scrollBar->MoveBy(x, 0.0);
247 		}
248 	}
249 }
250 
251 void StatusView::MouseUp(
252 	BPoint point) {
253 	D_HOOK(("StatusView::MouseUp()\n"));
254 
255 	m_dragging = false;
256 }
257 
258 // -------------------------------------------------------- //
259 // *** internal operations
260 // -------------------------------------------------------- //
261 
262 void
263 StatusView::drawInto(BView *v, BRect updateRect)
264 {
265 	BRect r(Bounds());
266 	D_OPERATION(("StatusView::drawInto(%.1f, %.1f)\n", r.Width(), r.Height()));
267 
268 	// draw border (minus right edge, which the scrollbar draws)
269 	v->SetDrawingMode(B_OP_COPY);
270 	v->BeginLineArray(8);
271 	v->AddLine(r.LeftTop(), r.RightTop(), M_MED_GRAY_COLOR);
272 	BPoint rtop = r.RightTop();
273 	rtop.y++;
274 	v->AddLine(rtop, r.RightBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
275 	v->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR);
276 	v->AddLine(r.LeftBottom(), r.LeftTop(), M_MED_GRAY_COLOR);
277 	r.InsetBy(1.0, 1.0);
278 	v->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR);
279 	rtop.y++;
280 	rtop.x--;
281 	v->AddLine(rtop, r.RightBottom(), M_GRAY_COLOR);
282 	v->AddLine(r.RightBottom(), r.LeftBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
283 	v->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR);
284 	v->EndLineArray();
285 	r.InsetBy(1.0, 1.0);
286 	v->SetLowColor(M_GRAY_COLOR);
287 	v->FillRect(r, B_SOLID_LOW);
288 
289 	r.InsetBy(2.0, 0.0);
290 	v->SetDrawingMode(B_OP_ALPHA);
291 	v->SetHighColor(0, 0, 0, uchar(255 * m_opacity));
292 
293 	// draw icon
294 	if (m_icon) {
295 		v->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
296 		BPoint p = r.LeftTop();
297 		p.y--;
298 		v->DrawBitmap(m_icon, p);
299 	}
300 
301 	// draw text
302 	r.left += 10.0;
303 	font_height fh;
304 	be_plain_font->GetHeight(&fh);
305 	r.bottom = Bounds().bottom - fh.descent
306 		- (Bounds().Height() - fh.ascent - fh.descent) / 2;
307 	v->MovePenTo(r.LeftBottom());
308 	v->DrawString(Text());
309 
310 	// draw resize dragger
311 	v->SetDrawingMode(B_OP_OVER);
312 	r = Bounds();
313 	r.right -= 2.0;
314 	r.left = r.right - 2.0;
315 	r.InsetBy(0.0, 3.0);
316 	r.top += 1.0;
317 	for (int32 i = 0; i < r.IntegerHeight(); i += 3) {
318 		BPoint p = r.LeftTop() + BPoint(0.0, i);
319 		v->SetHighColor(M_MED_GRAY_COLOR);
320 		v->StrokeLine(p, p, B_SOLID_HIGH);
321 		p += BPoint(1.0, 1.0);
322 		v->SetHighColor(M_WHITE_COLOR);
323 		v->StrokeLine(p, p, B_SOLID_HIGH);
324 	}
325 }
326 
327 
328 
329 void StatusView::setMessage(
330 	BString &title,
331 	BString &details,
332 	status_t error) {
333 	D_OPERATION(("StatusView::setMessage(%s)\n", title.String()));
334 
335 	// get the tip manager instance and reset
336 	TipManager *manager = TipManager::Instance();
337 	manager->removeAll(this);
338 
339 	// append error string
340 	if (error) {
341 		title << " (" << strerror(error) << ")";
342 	}
343 
344 	// truncate if necessary
345 	bool truncated = false;
346 	m_fullText = title;
347 	if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) {
348 		be_plain_font->TruncateString(&title, B_TRUNCATE_END,
349 									  Bounds().Width() - 25.0);
350 		truncated = true;
351 	}
352 	BStringView::SetText(title.String());
353 
354 	if (truncated || details.CountChars() > 0) {
355 		BString tip = m_fullText;
356 		if (details.CountChars() > 0) {
357 			tip << "\n" << details;
358 		}
359 		manager->setTip(tip.String(), this);
360 	}
361 
362 	if (error) {
363 		beep();
364 		// set icon
365 		if (m_icon) {
366 			delete m_icon;
367 			m_icon = 0;
368 		}
369 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
370 		m_icon = new BBitmap(iconRect, B_CMAP8);
371 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
372 	}
373 	else {
374 		// set icon
375 		if (m_icon) {
376 			delete m_icon;
377 			m_icon = 0;
378 		}
379 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
380 		m_icon = new BBitmap(iconRect, B_CMAP8);
381 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
382 	}
383 	m_dirty = true;
384 	startFade();
385 	Invalidate();
386 }
387 
388 void StatusView::setErrorMessage(
389 	BString text,
390 	bool error) {
391 	D_OPERATION(("StatusView::setErrorMessage(%s)\n",
392 				 text.String()));
393 
394 	// get the tip manager instance and reset
395 	TipManager *manager = TipManager::Instance();
396 	manager->removeAll(this);
397 
398 	// truncate if necessary
399 	m_fullText = text;
400 	if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
401 		be_plain_font->TruncateString(&text, B_TRUNCATE_END,
402 									  Bounds().Width() - 25.0);
403 		manager->setTip(m_fullText.String(), this);
404 	}
405 	BStringView::SetText(text.String());
406 
407 	if (error) {
408 		beep();
409 		// set icon
410 		if (m_icon) {
411 			delete m_icon;
412 			m_icon = 0;
413 		}
414 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
415 		m_icon = new BBitmap(iconRect, B_CMAP8);
416 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
417 	}
418 	else {
419 		// set icon
420 		if (m_icon) {
421 			delete m_icon;
422 			m_icon = 0;
423 		}
424 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
425 		m_icon = new BBitmap(iconRect, B_CMAP8);
426 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
427 	}
428 	m_dirty = true;
429 	startFade();
430 	Invalidate();
431 }
432 
433 void StatusView::startFade() {
434 	D_OPERATION(("StatusView::startFade()\n"));
435 
436 	m_opacity = 1.0;
437 	m_decayDelay = TEXT_DECAY_DELAY;
438 	if(!m_clock) {
439 		m_clock = new BMessageRunner(
440 			BMessenger(this),
441 			new BMessage('Tick'),
442 			TICK_PERIOD);
443 	}
444 }
445 
446 void StatusView::fadeTick() {
447 	D_HOOK(("StatusView::fadeTick()\n"));
448 
449 	if (m_opacity > 0.0) {
450 		if(m_decayDelay > 0) {
451 			m_decayDelay -= TICK_PERIOD;
452 			return;
453 		}
454 
455 		float steps = static_cast<float>(TEXT_DECAY_TIME)
456 					  / static_cast<float>(TICK_PERIOD);
457 		m_opacity -= (1.0 / steps);
458 		if (m_opacity < 0.001) {
459 			m_opacity = 0.0;
460 		}
461 		m_dirty = true;
462 		Invalidate();
463 	}
464 	else if (m_clock) {
465 		delete m_clock;
466 		m_clock = 0;
467 
468 		// get the tip manager instance and reset
469 		TipManager *manager = TipManager::Instance();
470 		manager->removeAll(this);
471 	}
472 }
473 
474 void StatusView::allocBackBitmap(float width, float height) {
475 	D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height));
476 
477 	// sanity check
478 	if(width <= 0.0 || height <= 0.0)
479 		return;
480 
481 	if(m_backBitmap) {
482 		// see if the bitmap needs to be expanded
483 		BRect b = m_backBitmap->Bounds();
484 		if(b.Width() >= width && b.Height() >= height)
485 			return;
486 
487 		// it does; clean up:
488 		freeBackBitmap();
489 	}
490 
491 	BRect b(0.0, 0.0, width, height);
492 	m_backBitmap = new BBitmap(b, B_RGB32, true);
493 	if(!m_backBitmap) {
494 		D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n"));
495 		return;
496 	}
497 
498 	m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
499 	m_backBitmap->AddChild(m_backView);
500 	m_dirty = true;
501 }
502 
503 void StatusView::freeBackBitmap() {
504 	if(m_backBitmap) {
505 		delete m_backBitmap;
506 		m_backBitmap = 0;
507 		m_backView = 0;
508 	}
509 }
510 
511 
512 // END -- ParameterContainerView.cpp --
513