xref: /haiku/src/apps/cortex/RouteApp/StatusView.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
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 - 1.0;
306 	v->MovePenTo(r.LeftBottom());
307 	v->DrawString(Text());
308 
309 	// draw resize dragger
310 	v->SetDrawingMode(B_OP_OVER);
311 	r = Bounds();
312 	r.right -= 2.0;
313 	r.left = r.right - 2.0;
314 	r.InsetBy(0.0, 3.0);
315 	r.top += 1.0;
316 	for (int32 i = 0; i < r.IntegerHeight(); i += 3) {
317 		BPoint p = r.LeftTop() + BPoint(0.0, i);
318 		v->SetHighColor(M_MED_GRAY_COLOR);
319 		v->StrokeLine(p, p, B_SOLID_HIGH);
320 		p += BPoint(1.0, 1.0);
321 		v->SetHighColor(M_WHITE_COLOR);
322 		v->StrokeLine(p, p, B_SOLID_HIGH);
323 	}
324 }
325 
326 
327 
328 void StatusView::setMessage(
329 	BString &title,
330 	BString &details,
331 	status_t error) {
332 	D_OPERATION(("StatusView::setMessage(%s)\n", title.String()));
333 
334 	// get the tip manager instance and reset
335 	TipManager *manager = TipManager::Instance();
336 	manager->removeAll(this);
337 
338 	// append error string
339 	if (error) {
340 		title << " (" << strerror(error) << ")";
341 	}
342 
343 	// truncate if necessary
344 	bool truncated = false;
345 	m_fullText = title;
346 	if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) {
347 		be_plain_font->TruncateString(&title, B_TRUNCATE_END,
348 									  Bounds().Width() - 25.0);
349 		truncated = true;
350 	}
351 	BStringView::SetText(title.String());
352 
353 	if (truncated || details.CountChars() > 0) {
354 		BString tip = m_fullText;
355 		if (details.CountChars() > 0) {
356 			tip << "\n" << details;
357 		}
358 		manager->setTip(tip.String(), this);
359 	}
360 
361 	if (error) {
362 		beep();
363 		// set icon
364 		if (m_icon) {
365 			delete m_icon;
366 			m_icon = 0;
367 		}
368 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
369 		m_icon = new BBitmap(iconRect, B_CMAP8);
370 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
371 	}
372 	else {
373 		// set icon
374 		if (m_icon) {
375 			delete m_icon;
376 			m_icon = 0;
377 		}
378 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
379 		m_icon = new BBitmap(iconRect, B_CMAP8);
380 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
381 	}
382 	m_dirty = true;
383 	startFade();
384 	Invalidate();
385 }
386 
387 void StatusView::setErrorMessage(
388 	BString text,
389 	bool error) {
390 	D_OPERATION(("StatusView::setErrorMessage(%s)\n",
391 				 text.String()));
392 
393 	// get the tip manager instance and reset
394 	TipManager *manager = TipManager::Instance();
395 	manager->removeAll(this);
396 
397 	// truncate if necessary
398 	m_fullText = text;
399 	if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
400 		be_plain_font->TruncateString(&text, B_TRUNCATE_END,
401 									  Bounds().Width() - 25.0);
402 		manager->setTip(m_fullText.String(), this);
403 	}
404 	BStringView::SetText(text.String());
405 
406 	if (error) {
407 		beep();
408 		// set icon
409 		if (m_icon) {
410 			delete m_icon;
411 			m_icon = 0;
412 		}
413 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
414 		m_icon = new BBitmap(iconRect, B_CMAP8);
415 		m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
416 	}
417 	else {
418 		// set icon
419 		if (m_icon) {
420 			delete m_icon;
421 			m_icon = 0;
422 		}
423 		BRect iconRect(0.0, 0.0, 7.0, 11.0);
424 		m_icon = new BBitmap(iconRect, B_CMAP8);
425 		m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
426 	}
427 	m_dirty = true;
428 	startFade();
429 	Invalidate();
430 }
431 
432 void StatusView::startFade() {
433 	D_OPERATION(("StatusView::startFade()\n"));
434 
435 	m_opacity = 1.0;
436 	m_decayDelay = TEXT_DECAY_DELAY;
437 	if(!m_clock) {
438 		m_clock = new BMessageRunner(
439 			BMessenger(this),
440 			new BMessage('Tick'),
441 			TICK_PERIOD);
442 	}
443 }
444 
445 void StatusView::fadeTick() {
446 	D_HOOK(("StatusView::fadeTick()\n"));
447 
448 	if (m_opacity > 0.0) {
449 		if(m_decayDelay > 0) {
450 			m_decayDelay -= TICK_PERIOD;
451 			return;
452 		}
453 
454 		float steps = static_cast<float>(TEXT_DECAY_TIME)
455 					  / static_cast<float>(TICK_PERIOD);
456 		m_opacity -= (1.0 / steps);
457 		if (m_opacity < 0.001) {
458 			m_opacity = 0.0;
459 		}
460 		m_dirty = true;
461 		Invalidate();
462 	}
463 	else if (m_clock) {
464 		delete m_clock;
465 		m_clock = 0;
466 
467 		// get the tip manager instance and reset
468 		TipManager *manager = TipManager::Instance();
469 		manager->removeAll(this);
470 	}
471 }
472 
473 void StatusView::allocBackBitmap(float width, float height) {
474 	D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height));
475 
476 	// sanity check
477 	if(width <= 0.0 || height <= 0.0)
478 		return;
479 
480 	if(m_backBitmap) {
481 		// see if the bitmap needs to be expanded
482 		BRect b = m_backBitmap->Bounds();
483 		if(b.Width() >= width && b.Height() >= height)
484 			return;
485 
486 		// it does; clean up:
487 		freeBackBitmap();
488 	}
489 
490 	BRect b(0.0, 0.0, width, height);
491 	m_backBitmap = new BBitmap(b, B_RGB32, true);
492 	if(!m_backBitmap) {
493 		D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n"));
494 		return;
495 	}
496 
497 	m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
498 	m_backBitmap->AddChild(m_backView);
499 	m_dirty = true;
500 }
501 
502 void StatusView::freeBackBitmap() {
503 	if(m_backBitmap) {
504 		delete m_backBitmap;
505 		m_backBitmap = 0;
506 		m_backView = 0;
507 	}
508 }
509 
510 
511 // END -- ParameterContainerView.cpp --
512