xref: /haiku/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp (revision be9a70562e3c6552efb0caa53bd26965e7e1bed7)
1 /*
2  * Copyright 2016-2017 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT license
4  *
5  * Authors:
6  *		Alexander von Gluck IV <kallisti5@unixzen.com>
7  *		Brian Hill <supernova@warpmail.net>
8  */
9 
10 
11 #include "SoftwareUpdaterWindow.h"
12 
13 #include <Alert.h>
14 #include <AppDefs.h>
15 #include <Application.h>
16 #include <Catalog.h>
17 #include <ControlLook.h>
18 #include <LayoutBuilder.h>
19 #include <LayoutUtils.h>
20 #include <Message.h>
21 #include <NodeInfo.h>
22 #include <Roster.h>
23 #include <String.h>
24 
25 #include "constants.h"
26 
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "SoftwareUpdaterWindow"
29 
30 
31 SoftwareUpdaterWindow::SoftwareUpdaterWindow()
32 	:
33 	BWindow(BRect(0, 0, 300, 100),
34 		B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW,
35 		B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE | B_NOT_CLOSABLE),
36 	fStripeView(NULL),
37 	fHeaderView(NULL),
38 	fDetailView(NULL),
39 	fUpdateButton(NULL),
40 	fCancelButton(NULL),
41 	fStatusBar(NULL),
42 	fCurrentState(STATE_HEAD),
43 	fWaitingSem(-1),
44 	fWaitingForButton(false),
45 	fUserCancelRequested(false),
46 	fWarningAlertCount(0)
47 {
48 	int32 iconSize = int(be_plain_font->Size() * 32.0 / 12.0);
49 		// At 12 point font, icon size is 32, at 24 point it is 64
50 	BBitmap icon = GetIcon(iconSize);
51 	fStripeView = new StripeView(icon);
52 
53 	fUpdateButton = new BButton(B_TRANSLATE("Update now"),
54 		new BMessage(kMsgUpdateConfirmed));
55 	fUpdateButton->MakeDefault(true);
56 	fCancelButton = new BButton(B_TRANSLATE("Cancel"),
57 		new BMessage(kMsgCancel));
58 
59 	fHeaderView = new BStringView("header",
60 		B_TRANSLATE("Checking for updates"), B_WILL_DRAW);
61 	fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
62 	fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
63 	fDetailView = new BStringView("detail", B_TRANSLATE("Contacting software "
64 		"repositories to check for package updates."), B_WILL_DRAW);
65 	fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
66 	fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
67 	fStatusBar = new BStatusBar("progress");
68 	fStatusBar->SetMaxValue(1.0);
69 
70 	fListView = new PackageListView();
71 	fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW,
72 		false, true);
73 
74 	BFont font;
75 	fHeaderView->GetFont(&font);
76 	font.SetFace(B_BOLD_FACE);
77 	font.SetSize(font.Size() * 1.5);
78 	fHeaderView->SetFont(&font,
79 		B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE | B_FONT_FLAGS);
80 
81 	BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0)
82 		.Add(fStripeView)
83 		.AddGroup(B_VERTICAL, 0)
84 			.SetInsets(0, B_USE_WINDOW_SPACING,
85 				B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
86 			.AddGroup(new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING))
87 				.Add(fHeaderView)
88 				.Add(fDetailView)
89 				.Add(fStatusBar)
90 				.Add(fScrollView)
91 			.End()
92 			.AddStrut(B_USE_SMALL_SPACING)
93 			.AddGroup(new BGroupView(B_HORIZONTAL))
94 				.AddGlue()
95 				.Add(fCancelButton)
96 				.Add(fUpdateButton)
97 			.End()
98 		.End()
99 	.End();
100 
101 	fDetailsLayoutItem = layout_item_for(fDetailView);
102 	fProgressLayoutItem = layout_item_for(fStatusBar);
103 	fPackagesLayoutItem = layout_item_for(fScrollView);
104 	fUpdateButtonLayoutItem = layout_item_for(fUpdateButton);
105 
106 	_SetState(STATE_DISPLAY_STATUS);
107 	CenterOnScreen();
108 	Show();
109 	SetFlags(Flags() ^ B_AUTO_UPDATE_SIZE_LIMITS);
110 
111 	// Prevent resizing for now
112 	fDefaultRect = Bounds();
113 	SetSizeLimits(fDefaultRect.Width(), fDefaultRect.Width(),
114 		fDefaultRect.Height(), fDefaultRect.Height());
115 
116 	BMessage registerMessage(kMsgRegister);
117 	registerMessage.AddMessenger(kKeyMessenger, BMessenger(this));
118 	be_app->PostMessage(&registerMessage);
119 
120 	fCancelAlertResponse.SetMessage(new BMessage(kMsgCancelResponse));
121 	fCancelAlertResponse.SetTarget(this);
122 	fWarningAlertDismissed.SetMessage(new BMessage(kMsgWarningDismissed));
123 	fWarningAlertDismissed.SetTarget(this);
124 }
125 
126 
127 void
128 SoftwareUpdaterWindow::MessageReceived(BMessage* message)
129 {
130 	switch (message->what) {
131 
132 		case kMsgTextUpdate:
133 		{
134 			if (fCurrentState == STATE_DISPLAY_PROGRESS)
135 				_SetState(STATE_DISPLAY_STATUS);
136 			else if (fCurrentState != STATE_DISPLAY_STATUS)
137 				break;
138 
139 			BString header;
140 			BString detail;
141 			Lock();
142 			status_t result = message->FindString(kKeyHeader, &header);
143 			if (result == B_OK && header != fHeaderView->Text())
144 				fHeaderView->SetText(header.String());
145 			result = message->FindString(kKeyDetail, &detail);
146 			if (result == B_OK)
147 				fDetailView->SetText(detail.String());
148 			Unlock();
149 			break;
150 		}
151 
152 		case kMsgProgressUpdate:
153 		{
154 			if (fCurrentState == STATE_DISPLAY_STATUS)
155 				_SetState(STATE_DISPLAY_PROGRESS);
156 			else if (fCurrentState != STATE_DISPLAY_PROGRESS)
157 				break;
158 
159 			BString packageName;
160 			status_t result = message->FindString(kKeyPackageName, &packageName);
161 			if (result != B_OK)
162 				break;
163 			BString packageCount;
164 			result = message->FindString(kKeyPackageCount, &packageCount);
165 			if (result != B_OK)
166 				break;
167 			float percent;
168 			result = message->FindFloat(kKeyPercentage, &percent);
169 			if (result != B_OK)
170 				break;
171 
172 			BString header;
173 			Lock();
174 			result = message->FindString(kKeyHeader, &header);
175 			if (result == B_OK && header != fHeaderView->Text())
176 				fHeaderView->SetText(header.String());
177 			fStatusBar->SetTo(percent, packageName.String(),
178 				packageCount.String());
179 			Unlock();
180 			break;
181 		}
182 
183 		case kMsgCancel:
184 		{
185 			if (_GetState() == STATE_FINAL_MESSAGE) {
186 				PostMessage(B_QUIT_REQUESTED);
187 				be_app->PostMessage(kMsgFinalQuit);
188 				break;
189 			}
190 
191 			BAlert* alert = new BAlert("cancel request", B_TRANSLATE("Updates"
192 				" have not been completed, are you sure you want to quit?"),
193 				B_TRANSLATE("Quit"), B_TRANSLATE("Don't quit"), NULL,
194 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
195 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
196 			alert->Go(&fCancelAlertResponse);
197 			break;
198 		}
199 
200 		case kMsgCancelResponse:
201 		{
202 			// Verify whether the cancel request was confirmed
203 			int32 selection = -1;
204 			message->FindInt32("which", &selection);
205 			if (selection != 0)
206 				break;
207 
208 			Lock();
209 			fHeaderView->SetText(B_TRANSLATE("Cancelling updates"));
210 			fDetailView->SetText(
211 				B_TRANSLATE("Attempting to cancel the updates..."));
212 			fCancelButton->SetEnabled(false);
213 			Unlock();
214 			fUserCancelRequested = true;
215 
216 			if (fWaitingForButton) {
217 				fButtonResult = message->what;
218 				delete_sem(fWaitingSem);
219 				fWaitingSem = -1;
220 			}
221 			break;
222 		}
223 
224 		case kMsgUpdateConfirmed:
225 		{
226 			if (fWaitingForButton) {
227 				fButtonResult = message->what;
228 				delete_sem(fWaitingSem);
229 				fWaitingSem = -1;
230 			}
231 			break;
232 		}
233 
234 		case kMsgWarningDismissed:
235 			fWarningAlertCount--;
236 			break;
237 
238 		case kMsgNetworkAlert:
239 		{
240 			BAlert* alert = new BAlert("network_connection",
241 				B_TRANSLATE_COMMENT("No active network connection was found",
242 					"Alert message"),
243 				B_TRANSLATE_COMMENT("Continue anyway", "Alert button label"),
244 				B_TRANSLATE_COMMENT("Quit","Alert button label"),
245 				NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
246 			int32 result = alert->Go();
247 			BMessage reply;
248 			reply.AddInt32(kKeyAlertResult, result);
249 			message->SendReply(&reply);
250 			break;
251 		}
252 
253 		default:
254 			BWindow::MessageReceived(message);
255 	}
256 }
257 
258 
259 bool
260 SoftwareUpdaterWindow::ConfirmUpdates(const char* text)
261 {
262 	Lock();
263 	fHeaderView->SetText(B_TRANSLATE("Updates found"));
264 	fDetailView->SetText(text);
265 	fListView->SortItems();
266 	Unlock();
267 
268 	uint32 priorState = _GetState();
269 	_SetState(STATE_GET_CONFIRMATION);
270 
271 	_WaitForButtonClick();
272 	_SetState(priorState);
273 	return fButtonResult == kMsgUpdateConfirmed;
274 }
275 
276 
277 void
278 SoftwareUpdaterWindow::UpdatesApplying(const char* header, const char* detail)
279 {
280 	Lock();
281 	fHeaderView->SetText(header);
282 	fDetailView->SetText(detail);
283 	Unlock();
284 	_SetState(STATE_APPLY_UPDATES);
285 }
286 
287 
288 bool
289 SoftwareUpdaterWindow::UserCancelRequested()
290 {
291 	if (_GetState() > STATE_GET_CONFIRMATION)
292 		return false;
293 
294 	return fUserCancelRequested;
295 }
296 
297 
298 void
299 SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type,
300 	const char* package_name, const char* cur_ver, const char* new_ver,
301 	const char* summary, const char* repository)
302 {
303 	Lock();
304 	fListView->AddPackage(install_type, package_name, cur_ver, new_ver,
305 		summary, repository);
306 	Unlock();
307 }
308 
309 
310 void
311 SoftwareUpdaterWindow::ShowWarningAlert(const char* text)
312 {
313 	BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL, NULL,
314 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
315 	alert->Go(&fWarningAlertDismissed);
316 	alert->CenterIn(Frame());
317 	// Offset multiple alerts
318 	alert->MoveBy(fWarningAlertCount * 15, fWarningAlertCount * 15);
319 	fWarningAlertCount++;
320 }
321 
322 
323 BBitmap
324 SoftwareUpdaterWindow::GetIcon(int32 iconSize)
325 {
326 	BBitmap icon(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
327 	team_info teamInfo;
328 	get_team_info(B_CURRENT_TEAM, &teamInfo);
329 	app_info appInfo;
330 	be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
331 	BNodeInfo::GetTrackerIcon(&appInfo.ref, &icon, icon_size(iconSize));
332 	return icon;
333 }
334 
335 
336 BBitmap
337 SoftwareUpdaterWindow::GetNotificationIcon()
338 {
339 	return GetIcon(B_LARGE_ICON);
340 }
341 
342 
343 void
344 SoftwareUpdaterWindow::FinalUpdate(const char* header, const char* detail)
345 {
346 	if (_GetState() == STATE_FINAL_MESSAGE)
347 		return;
348 
349 	_SetState(STATE_FINAL_MESSAGE);
350 	Lock();
351 	fHeaderView->SetText(header);
352 	fDetailView->SetText(detail);
353 	Unlock();
354 }
355 
356 
357 BLayoutItem*
358 SoftwareUpdaterWindow::layout_item_for(BView* view)
359 {
360 	BLayout* layout = view->Parent()->GetLayout();
361 	int32 index = layout->IndexOfView(view);
362 	return layout->ItemAt(index);
363 }
364 
365 
366 uint32
367 SoftwareUpdaterWindow::_WaitForButtonClick()
368 {
369 	fButtonResult = 0;
370 	fWaitingForButton = true;
371 	fWaitingSem = create_sem(0, "WaitingSem");
372 	while (acquire_sem(fWaitingSem) == B_INTERRUPTED) {
373 	}
374 	fWaitingForButton = false;
375 	return fButtonResult;
376 }
377 
378 
379 void
380 SoftwareUpdaterWindow::_SetState(uint32 state)
381 {
382 	if (state <= STATE_HEAD || state >= STATE_MAX)
383 		return;
384 
385 	Lock();
386 
387 	// Initial settings
388 	if (fCurrentState == STATE_HEAD) {
389 		fProgressLayoutItem->SetVisible(false);
390 		fPackagesLayoutItem->SetVisible(false);
391 	}
392 	fCurrentState = state;
393 
394 	// Update confirmation button
395 	// Show only when asking for confirmation to update
396 	if (fCurrentState == STATE_GET_CONFIRMATION)
397 		fUpdateButtonLayoutItem->SetVisible(true);
398 	else
399 		fUpdateButtonLayoutItem->SetVisible(false);
400 
401 	// View package info view
402 	// Show at confirmation prompt, hide at final update
403 	if (fCurrentState == STATE_GET_CONFIRMATION) {
404 		fPackagesLayoutItem->SetVisible(true);
405 		// Re-enable resizing
406 		float defaultWidth = fDefaultRect.Width();
407 		SetSizeLimits(defaultWidth, 9999,
408 			fDefaultRect.Height() + 4 * fListView->ItemHeight(), 9999);
409 		ResizeTo(defaultWidth, .75 * defaultWidth);
410 	} else if (fCurrentState == STATE_FINAL_MESSAGE) {
411 		fPackagesLayoutItem->SetVisible(false);
412 		float defaultWidth = fDefaultRect.Width();
413 		float defaultHeight = fDefaultRect.Height();
414 		SetSizeLimits(defaultWidth, defaultWidth, defaultHeight,
415 			defaultHeight);
416 		ResizeTo(defaultWidth, defaultHeight);
417 	}
418 
419 	// Progress bar and string view
420 	// Hide detail text while showing status bar
421 	if (fCurrentState == STATE_DISPLAY_PROGRESS) {
422 		fDetailsLayoutItem->SetVisible(false);
423 		fProgressLayoutItem->SetVisible(true);
424 	} else {
425 		fProgressLayoutItem->SetVisible(false);
426 		fDetailsLayoutItem->SetVisible(true);
427 	}
428 
429 	// Cancel button
430 	if (fCurrentState == STATE_FINAL_MESSAGE)
431  		fCancelButton->SetLabel(B_TRANSLATE("Quit"));
432 	fCancelButton->SetEnabled(fCurrentState != STATE_APPLY_UPDATES);
433 
434 	Unlock();
435 }
436 
437 
438 uint32
439 SoftwareUpdaterWindow::_GetState()
440 {
441 	return fCurrentState;
442 }
443 
444 
445 SuperItem::SuperItem(const char* label)
446 	:
447 	BListItem(),
448 	fLabel(label),
449 	fPackageIcon(NULL)
450 {
451 }
452 
453 
454 SuperItem::~SuperItem()
455 {
456 	delete fPackageIcon;
457 }
458 
459 
460 void
461 SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete)
462 {
463 	float width;
464     owner->GetPreferredSize(&width, NULL);
465     BString label(fLabel);
466     owner->TruncateString(&label, B_TRUNCATE_END, width);
467     owner->SetFont(&fBoldFont);
468     owner->DrawString(label.String(), BPoint(item_rect.left,
469 		item_rect.bottom - fFontHeight.descent - 1));
470 	owner->SetFont(&fRegularFont);
471 }
472 
473 
474 void
475 SuperItem::Update(BView *owner, const BFont *font)
476 {
477 	fRegularFont = *font;
478 	fBoldFont = *font;
479 	fBoldFont.SetFace(B_BOLD_FACE);
480 	BListItem::Update(owner, &fBoldFont);
481 
482 	// Calculate height for PackageItem
483 	fRegularFont.GetHeight(&fFontHeight);
484 	fPackageItemHeight = 2 * (fFontHeight.ascent + fFontHeight.descent
485 		+ fFontHeight.leading);
486 
487 	// Calculate height for this item
488 	fBoldFont.GetHeight(&fFontHeight);
489 	SetHeight(fFontHeight.ascent + fFontHeight.descent
490 		+ fFontHeight.leading + 4);
491 
492 	_GetPackageIcon();
493 }
494 
495 
496 void
497 SuperItem::_GetPackageIcon()
498 {
499 	delete fPackageIcon;
500 	fIconSize = int(fPackageItemHeight * .7);
501 
502 	status_t result = B_ERROR;
503 	BRect iconRect(0, 0, fIconSize - 1, fIconSize - 1);
504 	fPackageIcon = new BBitmap(iconRect, 0, B_RGBA32);
505 	BMimeType nodeType;
506 	nodeType.SetTo("application/x-vnd.haiku-package");
507 	result = nodeType.GetIcon(fPackageIcon, icon_size(fIconSize));
508 	// Get super type icon
509 	if (result != B_OK) {
510 		BMimeType superType;
511 		if (nodeType.GetSupertype(&superType) == B_OK)
512 			result = superType.GetIcon(fPackageIcon, icon_size(fIconSize));
513 	}
514 	if (result != B_OK) {
515 		delete fPackageIcon;
516 		fPackageIcon = NULL;
517 	}
518 }
519 
520 
521 PackageItem::PackageItem(const char* name, const char* version,
522 	const char* summary, const char* tooltip, SuperItem* super)
523 	:
524 	BListItem(),
525 	fName(name),
526 	fVersion(version),
527 	fSummary(summary),
528 	fToolTip(NULL),
529 	fSuperItem(super)
530 {
531 	fLabelOffset = be_control_look->DefaultLabelSpacing();
532 	if (tooltip != NULL)
533 		fToolTip = new BTextToolTip(tooltip);
534 }
535 
536 
537 PackageItem::~PackageItem()
538 {
539 	if (fToolTip != NULL)
540 		fToolTip->ReleaseReference();
541 }
542 
543 
544 void
545 PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete)
546 {
547 	float width;
548     owner->GetPreferredSize(&width, NULL);
549     float nameWidth = width / 2.0;
550     float offset_width = 0;
551 
552 	BBitmap* icon = fSuperItem->GetIcon();
553 	if (icon != NULL && icon->IsValid()) {
554 		int16 iconSize = fSuperItem->GetIconSize();
555 		float offsetMarginHeight = floor((Height() - iconSize) / 2);
556 
557 		//owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
558 		owner->SetDrawingMode(B_OP_ALPHA);
559 		owner->DrawBitmap(icon, BPoint(item_rect.left,
560 			item_rect.top + offsetMarginHeight));
561 		owner->SetDrawingMode(B_OP_COPY);
562 		offset_width += iconSize + fLabelOffset;
563 	}
564 
565 	// Package name
566 	font_height fontHeight = fSuperItem->GetFontHeight();
567     BString name(fName);
568     owner->TruncateString(&name, B_TRUNCATE_END, nameWidth);
569 	BPoint cursor(item_rect.left + offset_width,
570 		item_rect.bottom - fSmallTotalHeight - fontHeight.descent - 1);
571 	owner->DrawString(name.String(), cursor);
572 	cursor.x += owner->StringWidth(name.String()) + fLabelOffset;
573 
574 	// Change font and color
575 	owner->SetFont(&fSmallFont);
576 	owner->SetHighColor(tint_color(ui_color(B_LIST_ITEM_TEXT_COLOR), 0.7));
577 
578 	// Version
579 	BString version(fVersion);
580 	owner->TruncateString(&version, B_TRUNCATE_END, width - cursor.x);
581 	owner->DrawString(version.String(), cursor);
582 
583 	// Summary
584 	BString summary(fSummary);
585 	cursor.x = item_rect.left + offset_width;
586 	cursor.y = item_rect.bottom - fontHeight.descent;
587 	owner->TruncateString(&summary, B_TRUNCATE_END, width - cursor.x);
588 	owner->DrawString(summary.String(), cursor);
589 
590 	owner->SetFont(&fRegularFont);
591 }
592 
593 
594 void
595 PackageItem::Update(BView *owner, const BFont *font)
596 {
597 	BListItem::Update(owner, font);
598 	SetItemHeight(font);
599 }
600 
601 
602 void
603 PackageItem::SetItemHeight(const BFont* font)
604 {
605 	SetHeight(fSuperItem->GetPackageItemHeight());
606 
607 	fRegularFont = *font;
608 	fSmallFont = *font;
609 	fSmallFont.SetSize(font->Size() - 2);
610 	fSmallFont.GetHeight(&fSmallFontHeight);
611 	fSmallTotalHeight = fSmallFontHeight.ascent + fSmallFontHeight.descent
612 		+ fSmallFontHeight.leading;
613 }
614 
615 
616 int
617 PackageItem::NameCompare(PackageItem* item)
618 {
619 	// sort by package name
620 	return fName.ICompare(item->fName);
621 }
622 
623 
624 int
625 SortPackageItems(const BListItem* item1, const BListItem* item2)
626 {
627 	PackageItem* first = (PackageItem*)item1;
628 	PackageItem* second = (PackageItem*)item2;
629 	return first->NameCompare(second);
630 }
631 
632 
633 PackageListView::PackageListView()
634 	:
635 	BOutlineListView("Package list"),
636 	fSuperUpdateItem(NULL),
637 	fSuperInstallItem(NULL),
638 	fSuperUninstallItem(NULL)
639 {
640 	SetExplicitMinSize(BSize(B_SIZE_UNSET, 40));
641 	SetExplicitPreferredSize(BSize(B_SIZE_UNSET, 400));
642 }
643 
644 
645 void
646 PackageListView::FrameResized(float newWidth, float newHeight)
647 {
648 	BListView::FrameResized(newWidth, newHeight);
649 
650 	float count = CountItems();
651 	for (int32 i = 0; i < count; i++) {
652 		BListItem *item = ItemAt(i);
653 		item->Update(this, be_plain_font);
654 	}
655 	Invalidate();
656 }
657 
658 
659 void
660 PackageListView::MouseDown(BPoint where)
661 {
662 	BOutlineListView::MouseDown(where);
663 	BToolTip* tooltip = NULL;
664 	bool found = GetToolTipAt(where, &tooltip);
665 	if (found)
666 		ShowToolTip(tooltip);
667 }
668 
669 
670 bool
671 PackageListView::GetToolTipAt(BPoint point, BToolTip** _tip)
672 {
673 	BListItem* item = ItemAt(IndexOf(point));
674 	if (item == NULL)
675 		return false;
676 	PackageItem* pItem = dynamic_cast<PackageItem*>(item);
677 	if (pItem != NULL) {
678 		*_tip = pItem->ToolTip();
679 		return true;
680 	}
681 	return false;
682 }
683 
684 
685 void
686 PackageListView::AddPackage(uint32 install_type, const char* name,
687 	const char* cur_ver, const char* new_ver, const char* summary,
688 	const char* repository)
689 {
690 	SuperItem* super;
691 	BString version;
692 	BString tooltip(B_TRANSLATE_COMMENT("Package:", "Tooltip text"));
693 	tooltip.Append(" ").Append(name).Append("\n")
694 		.Append(B_TRANSLATE_COMMENT("Repository:", "Tooltip text"))
695 		.Append(" ").Append(repository).Append("\n");
696 	switch (install_type) {
697 		case PACKAGE_UPDATE:
698 		{
699 			if (fSuperUpdateItem == NULL) {
700 				fSuperUpdateItem = new SuperItem(B_TRANSLATE_COMMENT(
701 					"Packages to be updated", "List super item label"));
702 				AddItem(fSuperUpdateItem);
703 			}
704 			super = fSuperUpdateItem;
705 
706 			version.SetTo(new_ver);
707 			tooltip.Append(B_TRANSLATE_COMMENT("Updating version",
708 					"Tooltip text"))
709 				.Append(" ").Append(cur_ver)
710 				.Append(" ").Append(B_TRANSLATE_COMMENT("to", "Tooltip text"))
711 				.Append(" ").Append(new_ver);
712 			break;
713 		}
714 
715 		case PACKAGE_INSTALL:
716 		{
717 			if (fSuperInstallItem == NULL) {
718 				fSuperInstallItem = new SuperItem(B_TRANSLATE_COMMENT(
719 					"New packages to be installed", "List super item label"));
720 				AddItem(fSuperInstallItem);
721 			}
722 			super = fSuperInstallItem;
723 
724 			version.SetTo(new_ver);
725 			tooltip.Append(B_TRANSLATE_COMMENT("Installing version",
726 					"Tooltip text"))
727 				.Append(" ").Append(new_ver);
728 			break;
729 		}
730 
731 		case PACKAGE_UNINSTALL:
732 		{
733 			if (fSuperUninstallItem == NULL) {
734 				fSuperUninstallItem = new SuperItem(B_TRANSLATE_COMMENT(
735 					"Packages to be uninstalled", "List super item label"));
736 				AddItem(fSuperUninstallItem);
737 			}
738 			super = fSuperUninstallItem;
739 
740 			version.SetTo("");
741 			tooltip.Append(B_TRANSLATE_COMMENT("Uninstalling version",
742 					"Tooltip text"))
743 				.Append(" ").Append(cur_ver);
744 			break;
745 		}
746 
747 		default:
748 			return;
749 
750 	}
751 	PackageItem* item = new PackageItem(name, version.String(), summary,
752 		tooltip.String(), super);
753 	AddUnder(item, super);
754 }
755 
756 
757 void
758 PackageListView::SortItems()
759 {
760 	if (fSuperUpdateItem != NULL)
761 		SortItemsUnder(fSuperUpdateItem, true, SortPackageItems);
762 	if (fSuperInstallItem != NULL)
763 		SortItemsUnder(fSuperInstallItem, true, SortPackageItems);
764 	if (fSuperUninstallItem != NULL)
765 		SortItemsUnder(fSuperUninstallItem, true, SortPackageItems);
766 }
767 
768 
769 float
770 PackageListView::ItemHeight()
771 {
772 	if (fSuperUpdateItem != NULL)
773 		return fSuperUpdateItem->GetPackageItemHeight();
774 	if (fSuperInstallItem != NULL)
775 		return fSuperInstallItem->GetPackageItemHeight();
776 	if (fSuperUninstallItem != NULL)
777 		return fSuperUninstallItem->GetPackageItemHeight();
778 	return 0;
779 }
780