xref: /haiku/src/apps/cortex/RouteApp/StatusView.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
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 - 10.0;
240 		m_dirty = true;
241 		Invalidate(r);
242 		if (m_scrollBar) {
243 			m_scrollBar->ResizeBy(-x, 0.0);
244 			m_scrollBar->MoveBy(x, 0.0);
245 		}
246 	}
247 }
248 
249 void StatusView::MouseUp(
250 	BPoint point) {
251 	D_HOOK(("StatusView::MouseUp()\n"));
252 
253 	m_dragging = false;
254 }
255 
256 // -------------------------------------------------------- //
257 // *** internal operations
258 // -------------------------------------------------------- //
259 
260 void
261 StatusView::drawInto(BView *v, BRect updateRect)
262 {
263 	BRect r(Bounds());
264 	D_OPERATION(("StatusView::drawInto(%.1f, %.1f)\n", r.Width(), r.Height()));
265 
266 	// draw border (minus right edge, which the scrollbar draws)
267 	v->SetDrawingMode(B_OP_COPY);
268 	v->BeginLineArray(8);
269 	v->AddLine(r.LeftTop(), r.RightTop(), M_MED_GRAY_COLOR);
270 	BPoint rtop = r.RightTop();
271 	rtop.y++;
272 	v->AddLine(rtop, r.RightBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
273 	v->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR);
274 	v->AddLine(r.LeftBottom(), r.LeftTop(), M_MED_GRAY_COLOR);
275 	r.InsetBy(1.0, 1.0);
276 	v->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR);
277 	rtop.y++;
278 	rtop.x--;
279 	v->AddLine(rtop, r.RightBottom(), M_GRAY_COLOR);
280 	v->AddLine(r.RightBottom(), r.LeftBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
281 	v->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR);
282 	v->EndLineArray();
283 	r.InsetBy(1.0, 1.0);
284 	v->SetLowColor(M_GRAY_COLOR);
285 	v->FillRect(r, B_SOLID_LOW);
286 
287 	r.InsetBy(2.0, 0.0);
288 	v->SetDrawingMode(B_OP_ALPHA);
289 	v->SetHighColor(0, 0, 0, 255 * m_opacity);
290 
291 	// draw icon
292 	if (m_icon) {
293 		v->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
294 		BPoint p = r.LeftTop();
295 		p.y--;
296 		v->DrawBitmap(m_icon, p);
297 	}
298 
299 	// draw text
300 	r.left += 10.0;
301 	font_height fh;
302 	be_plain_font->GetHeight(&fh);
303 	r.bottom = Bounds().bottom - fh.descent - 1.0;
304 	v->MovePenTo(r.LeftBottom());
305 	v->DrawString(Text());
306 
307 	// draw resize dragger
308 	v->SetDrawingMode(B_OP_OVER);
309 	r = Bounds();
310 	r.right -= 2.0;
311 	r.left = r.right - 2.0;
312 	r.InsetBy(0.0, 3.0);
313 	r.top += 1.0;
314 	for (int32 i = 0; i < r.IntegerHeight(); i += 3) {
315 		BPoint p = r.LeftTop() + BPoint(0.0, i);
316 		v->SetHighColor(M_MED_GRAY_COLOR);
317 		v->StrokeLine(p, p, B_SOLID_HIGH);
318 		p += BPoint(1.0, 1.0);
319 		v->SetHighColor(M_WHITE_COLOR);
320 		v->StrokeLine(p, p, B_SOLID_HIGH);
321 	}
322 }
323 
324 
325 
326 void StatusView::setMessage(
327 	BString &title,
328 	BString &details,
329 	status_t error) {
330 	D_OPERATION(("StatusView::setMessage(%s)\n", title.String()));
331 
332 	// get the tip manager instance and reset
333 	TipManager *manager = TipManager::Instance();
334 	manager->removeAll(this);
335 
336 	// append error string
337 	if (error) {
338 		title << " (" << strerror(error) << ")";
339 	}
340 
341 	// truncate if necessary
342 	bool truncated = false;
343 	m_fullText = title;
344 	if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) {
345 		be_plain_font->TruncateString(&title, B_TRUNCATE_END,
346 									  Bounds().Width() - 25.0);
347 		truncated = true;
348 	}
349 	BStringView::SetText(title.String());
350 
351 	if (truncated || details.CountChars() > 0) {
352 		BString tip = m_fullText;
353 		if (details.CountChars() > 0) {
354 			tip << "\n" << details;
355 		}
356 		manager->setTip(tip.String(), this);
357 	}
358 
359 	if (error) {
360 		beep();
361 		// set icon
362 		if (m_icon) {
363 			delete m_icon;
364 			m_icon = 0;
365 		}
366 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
367 		m_icon = new BBitmap(iconRect, B_CMAP8);
368 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
369 	}
370 	else {
371 		// set icon
372 		if (m_icon) {
373 			delete m_icon;
374 			m_icon = 0;
375 		}
376 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
377 		m_icon = new BBitmap(iconRect, B_CMAP8);
378 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
379 	}
380 	m_dirty = true;
381 	startFade();
382 	Invalidate();
383 }
384 
385 void StatusView::setErrorMessage(
386 	BString text,
387 	bool error) {
388 	D_OPERATION(("StatusView::setErrorMessage(%s)\n",
389 				 text.String()));
390 
391 	// get the tip manager instance and reset
392 	TipManager *manager = TipManager::Instance();
393 	manager->removeAll(this);
394 
395 	// truncate if necessary
396 	m_fullText = text;
397 	if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
398 		be_plain_font->TruncateString(&text, B_TRUNCATE_END,
399 									  Bounds().Width() - 25.0);
400 		manager->setTip(m_fullText.String(), this);
401 	}
402 	BStringView::SetText(text.String());
403 
404 	if (error) {
405 		beep();
406 		// set icon
407 		if (m_icon) {
408 			delete m_icon;
409 			m_icon = 0;
410 		}
411 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
412 		m_icon = new BBitmap(iconRect, B_CMAP8);
413 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
414 	}
415 	else {
416 		// set icon
417 		if (m_icon) {
418 			delete m_icon;
419 			m_icon = 0;
420 		}
421 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
422 		m_icon = new BBitmap(iconRect, B_CMAP8);
423 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
424 	}
425 	m_dirty = true;
426 	startFade();
427 	Invalidate();
428 }
429 
430 void StatusView::startFade() {
431 	D_OPERATION(("StatusView::startFade()\n"));
432 
433 	m_opacity = 1.0;
434 	m_decayDelay = TEXT_DECAY_DELAY;
435 	if(!m_clock) {
436 		m_clock = new BMessageRunner(
437 			BMessenger(this),
438 			new BMessage('Tick'),
439 			TICK_PERIOD);
440 	}
441 }
442 
443 void StatusView::fadeTick() {
444 	D_HOOK(("StatusView::fadeTick()\n"));
445 
446 	if (m_opacity > 0.0) {
447 		if(m_decayDelay > 0) {
448 			m_decayDelay -= TICK_PERIOD;
449 			return;
450 		}
451 
452 		float steps = static_cast<float>(TEXT_DECAY_TIME)
453 					  / static_cast<float>(TICK_PERIOD);
454 		m_opacity -= (1.0 / steps);
455 		if (m_opacity < 0.001) {
456 			m_opacity = 0.0;
457 		}
458 		m_dirty = true;
459 		Invalidate();
460 	}
461 	else if (m_clock) {
462 		delete m_clock;
463 		m_clock = 0;
464 
465 		// get the tip manager instance and reset
466 		TipManager *manager = TipManager::Instance();
467 		manager->removeAll(this);
468 	}
469 }
470 
471 void StatusView::allocBackBitmap(float width, float height) {
472 	D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height));
473 
474 	// sanity check
475 	if(width <= 0.0 || height <= 0.0)
476 		return;
477 
478 	if(m_backBitmap) {
479 		// see if the bitmap needs to be expanded
480 		BRect b = m_backBitmap->Bounds();
481 		if(b.Width() >= width && b.Height() >= height)
482 			return;
483 
484 		// it does; clean up:
485 		freeBackBitmap();
486 	}
487 
488 	BRect b(0.0, 0.0, width, height);
489 	m_backBitmap = new BBitmap(b, B_RGB32, true);
490 	if(!m_backBitmap) {
491 		D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n"));
492 		return;
493 	}
494 
495 	m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
496 	m_backBitmap->AddChild(m_backView);
497 	m_dirty = true;
498 }
499 
500 void StatusView::freeBackBitmap() {
501 	if(m_backBitmap) {
502 		delete m_backBitmap;
503 		m_backBitmap = 0;
504 		m_backView = 0;
505 	}
506 }
507 
508 
509 // END -- ParameterContainerView.cpp --
510