xref: /haiku/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp (revision fc1ace1563a98648db1cec501a10d9b559ef831b)
1 /*
2  * Copyright 2016-2019 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@tycho.email>
8  *		Jacob Secunda
9  */
10 
11 
12 #include "SoftwareUpdaterWindow.h"
13 
14 #include <Alert.h>
15 #include <AppDefs.h>
16 #include <Application.h>
17 #include <Catalog.h>
18 #include <ControlLook.h>
19 #include <FindDirectory.h>
20 #include <LayoutBuilder.h>
21 #include <LayoutUtils.h>
22 #include <Message.h>
23 #include <Roster.h>
24 #include <RosterPrivate.h>
25 #include <Screen.h>
26 #include <String.h>
27 
28 #include "constants.h"
29 
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "SoftwareUpdaterWindow"
32 
33 
SoftwareUpdaterWindow()34 SoftwareUpdaterWindow::SoftwareUpdaterWindow()
35 	:
36 	BWindow(BRect(0, 0, 300, 10),
37 		B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW,
38 		B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE | B_NOT_RESIZABLE),
39 	fStripeView(NULL),
40 	fHeaderView(NULL),
41 	fDetailView(NULL),
42 	fUpdateButton(NULL),
43 	fCancelButton(NULL),
44 	fStatusBar(NULL),
45 	fCurrentState(STATE_HEAD),
46 	fWaitingSem(-1),
47 	fWaitingForButton(false),
48 	fUpdateConfirmed(false),
49 	fUserCancelRequested(false),
50 	fWarningAlertCount(0),
51 	fSettingsReadStatus(B_ERROR),
52 	fSaveFrameChanges(false),
53 	fMessageRunner(NULL),
54 	fFrameChangeMessage(kMsgWindowFrameChanged)
55 {
56 	// Layout
57 	BBitmap icon = GetIcon(32 * icon_layout_scale());
58 	fStripeView = new BStripeView(icon);
59 
60 	fUpdateButton = new BButton(B_TRANSLATE("Update now"),
61 		new BMessage(kMsgUpdateConfirmed));
62 	fUpdateButton->MakeDefault(true);
63 	fCancelButton = new BButton(B_TRANSLATE("Cancel"),
64 		new BMessage(kMsgCancel));
65 	fRebootButton = new BButton(B_TRANSLATE("Reboot"),
66 		new BMessage(kMsgReboot));
67 
68 	fHeaderView = new BStringView("header",
69 		B_TRANSLATE("Checking for updates"), B_WILL_DRAW);
70 	fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
71 	fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
72 	fDetailView = new BStringView("detail", B_TRANSLATE("Contacting software "
73 		"repositories to check for package updates."), B_WILL_DRAW);
74 	fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
75 	fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
76 	fStatusBar = new BStatusBar("progress");
77 	fStatusBar->SetMaxValue(100);
78 
79 	fListView = new PackageListView();
80 	fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW,
81 		false, true);
82 
83 	fDetailsCheckbox = new BCheckBox("detailscheckbox",
84 		B_TRANSLATE("Show more details"),
85 		new BMessage(kMsgMoreDetailsToggle));
86 
87 	BFont font;
88 	fHeaderView->GetFont(&font);
89 	font.SetFace(B_BOLD_FACE);
90 	font.SetSize(font.Size() * 1.5);
91 	fHeaderView->SetFont(&font,
92 		B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE | B_FONT_FLAGS);
93 
94 	BLayoutBuilder::Group<>(this, B_HORIZONTAL, B_USE_ITEM_SPACING)
95 		.Add(fStripeView)
96 		.AddGroup(B_VERTICAL, 0)
97 			.SetInsets(0, B_USE_WINDOW_SPACING,
98 				B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
99 			.AddGroup(new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING))
100 				.Add(fHeaderView)
101 				.Add(fDetailView)
102 				.Add(fStatusBar)
103 				.Add(fScrollView)
104 			.End()
105 			.AddStrut(B_USE_SMALL_SPACING)
106 			.AddGroup(new BGroupView(B_HORIZONTAL))
107 				.Add(fDetailsCheckbox)
108 				.AddGlue()
109 				.Add(fCancelButton)
110 				.Add(fUpdateButton)
111 				.Add(fRebootButton)
112 			.End()
113 		.End()
114 	.End();
115 
116 	fDetailsLayoutItem = layout_item_for(fDetailView);
117 	fProgressLayoutItem = layout_item_for(fStatusBar);
118 	fPackagesLayoutItem = layout_item_for(fScrollView);
119 	fCancelButtonLayoutItem = layout_item_for(fCancelButton);
120 	fUpdateButtonLayoutItem = layout_item_for(fUpdateButton);
121 	fRebootButtonLayoutItem = layout_item_for(fRebootButton);
122 	fDetailsCheckboxLayoutItem = layout_item_for(fDetailsCheckbox);
123 
124 	_SetState(STATE_DISPLAY_STATUS);
125 	CenterOnScreen();
126 	SetFlags(Flags() ^ B_AUTO_UPDATE_SIZE_LIMITS);
127 
128 	// Prevent resizing for now
129 	fDefaultRect = Bounds();
130 	SetSizeLimits(fDefaultRect.Width(), fDefaultRect.Width(),
131 		fDefaultRect.Height(), fDefaultRect.Height());
132 
133 	// Read settings file
134 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &fSettingsPath);
135 	if (status == B_OK) {
136 		fSettingsPath.Append(kSettingsFilename);
137 		fSettingsReadStatus = _ReadSettings(fInitialSettings);
138 	}
139 	// Move to saved setting position
140 	if (fSettingsReadStatus == B_OK) {
141 		BRect windowFrame;
142 		status = fInitialSettings.FindRect(kKeyWindowFrame, &windowFrame);
143 		if (status == B_OK) {
144 			BScreen screen(this);
145 			if (screen.Frame().Contains(windowFrame.LeftTop()))
146 				MoveTo(windowFrame.LeftTop());
147 		}
148 	}
149 	Show();
150 
151 	BMessage registerMessage(kMsgRegister);
152 	registerMessage.AddMessenger(kKeyMessenger, BMessenger(this));
153 	be_app->PostMessage(&registerMessage);
154 
155 	fCancelAlertResponse.SetMessage(new BMessage(kMsgCancelResponse));
156 	fCancelAlertResponse.SetTarget(this);
157 	fWarningAlertDismissed.SetMessage(new BMessage(kMsgWarningDismissed));
158 	fWarningAlertDismissed.SetTarget(this);
159 
160 	// Common elements used for the zoom height and width calculations
161 	fZoomHeightBaseline = 6
162 		+ be_control_look->ComposeSpacing(B_USE_SMALL_SPACING)
163 		+ 2 * be_control_look->ComposeSpacing(B_USE_WINDOW_SPACING);
164 	fZoomWidthBaseline = fStripeView->PreferredSize().Width()
165 		+ be_control_look->ComposeSpacing(B_USE_ITEM_SPACING)
166 		+ fScrollView->ScrollBar(B_VERTICAL)->PreferredSize().Width()
167 		+ be_control_look->ComposeSpacing(B_USE_WINDOW_SPACING);
168 }
169 
170 
171 bool
QuitRequested()172 SoftwareUpdaterWindow::QuitRequested()
173 {
174 	PostMessage(kMsgCancel);
175 	return false;
176 }
177 
178 
179 void
FrameMoved(BPoint newPosition)180 SoftwareUpdaterWindow::FrameMoved(BPoint newPosition)
181 {
182 	BWindow::FrameMoved(newPosition);
183 
184 	// Create a message runner to consolidate all function calls from a
185 	// move into one message post after moving has ceased for .5 seconds
186 	if (fSaveFrameChanges) {
187 		if (fMessageRunner == NULL) {
188 			fMessageRunner = new BMessageRunner(this, &fFrameChangeMessage,
189 				500000, 1);
190 		} else
191 			fMessageRunner->SetInterval(500000);
192 	}
193 }
194 
195 
196 void
FrameResized(float newWidth,float newHeight)197 SoftwareUpdaterWindow::FrameResized(float newWidth, float newHeight)
198 {
199 	BWindow::FrameResized(newWidth, newHeight);
200 
201 	// Create a message runner to consolidate all function calls from a
202 	// resize into one message post after resizing has ceased for .5 seconds
203 	if (fSaveFrameChanges) {
204 		if (fMessageRunner == NULL) {
205 			fMessageRunner = new BMessageRunner(this, &fFrameChangeMessage,
206 				500000, 1);
207 		} else
208 			fMessageRunner->SetInterval(500000);
209 	}
210 }
211 
212 
213 void
Zoom(BPoint origin,float width,float height)214 SoftwareUpdaterWindow::Zoom(BPoint origin, float width, float height)
215 {
216 	// Override default zoom behavior and keep window at same position instead
217 	// of centering on screen
218 	BWindow::Zoom(Frame().LeftTop(), width, height);
219 }
220 
221 
222 void
MessageReceived(BMessage * message)223 SoftwareUpdaterWindow::MessageReceived(BMessage* message)
224 {
225 	switch (message->what) {
226 
227 		case kMsgTextUpdate:
228 		{
229 			if (fCurrentState == STATE_DISPLAY_PROGRESS)
230 				_SetState(STATE_DISPLAY_STATUS);
231 			else if (fCurrentState != STATE_DISPLAY_STATUS)
232 				break;
233 
234 			BString header;
235 			BString detail;
236 			Lock();
237 			status_t result = message->FindString(kKeyHeader, &header);
238 			if (result == B_OK && header != fHeaderView->Text())
239 				fHeaderView->SetText(header.String());
240 			result = message->FindString(kKeyDetail, &detail);
241 			if (result == B_OK)
242 				fDetailView->SetText(detail.String());
243 			Unlock();
244 			break;
245 		}
246 
247 		case kMsgProgressUpdate:
248 		{
249 			if (fCurrentState == STATE_DISPLAY_STATUS)
250 				_SetState(STATE_DISPLAY_PROGRESS);
251 			else if (fCurrentState != STATE_DISPLAY_PROGRESS)
252 				break;
253 
254 			BString packageName;
255 			status_t result = message->FindString(kKeyPackageName,
256 				&packageName);
257 			if (result != B_OK)
258 				break;
259 			BString packageCount;
260 			result = message->FindString(kKeyPackageCount, &packageCount);
261 			if (result != B_OK)
262 				break;
263 			float percent;
264 			result = message->FindFloat(kKeyPercentage, &percent);
265 			if (result != B_OK)
266 				break;
267 
268 			BString header;
269 			Lock();
270 			result = message->FindString(kKeyHeader, &header);
271 			if (result == B_OK && header != fHeaderView->Text())
272 				fHeaderView->SetText(header.String());
273 			fStatusBar->SetTo(percent, packageName.String(),
274 				packageCount.String());
275 			Unlock();
276 
277 			fListView->UpdatePackageProgress(packageName.String(), percent);
278 			break;
279 		}
280 
281 		case kMsgCancel:
282 		{
283 			if (_GetState() == STATE_FINAL_MESSAGE) {
284 				be_app->PostMessage(kMsgFinalQuit);
285 				break;
286 			}
287 			if (!fUpdateConfirmed) {
288 				// Downloads have not started yet, we will request to cancel
289 				// without confirming
290 				Lock();
291 				fHeaderView->SetText(B_TRANSLATE("Cancelling updates"));
292 				fDetailView->SetText(
293 					B_TRANSLATE("Attempting to cancel the updates"
294 						B_UTF8_ELLIPSIS));
295 				Unlock();
296 				fUserCancelRequested = true;
297 
298 				if (fWaitingForButton) {
299 					fButtonResult = message->what;
300 					delete_sem(fWaitingSem);
301 					fWaitingSem = -1;
302 				}
303 				break;
304 			}
305 
306 			// Confirm with the user to cancel
307 			BAlert* alert = new BAlert("cancel request", B_TRANSLATE("Updates"
308 				" have not been completed, are you sure you want to quit?"),
309 				B_TRANSLATE("Quit"), B_TRANSLATE("Don't quit"), NULL,
310 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
311 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
312 			alert->Go(&fCancelAlertResponse);
313 			break;
314 		}
315 
316 		case kMsgShowReboot:
317 		{
318 			fRebootButtonLayoutItem->SetVisible(true);
319 			fRebootButton->SetLabel(B_TRANSLATE_COMMENT("Reboot",
320 				"Button label"));
321 			fRebootButton->MakeDefault(true);
322 			break;
323 		}
324 
325 		case kMsgReboot:
326 		{
327 			if (_GetState() != STATE_FINAL_MESSAGE)
328 				break;
329 
330 			BRoster roster;
331 			BRoster::Private rosterPrivate(roster);
332 			status_t error = rosterPrivate.ShutDown(true, false, false);
333 			if (error != B_OK) {
334 				BAlert* alert = new BAlert("reboot request", B_TRANSLATE(
335 					"For some reason, we could not reboot your computer."),
336 					B_TRANSLATE("OK"), NULL, NULL,
337 					B_WIDTH_AS_USUAL, B_STOP_ALERT);
338 				alert->Go();
339 			}
340 			break;
341 		}
342 
343 		case kMsgCancelResponse:
344 		{
345 			// Verify whether the cancel alert was confirmed
346 			int32 selection = -1;
347 			message->FindInt32("which", &selection);
348 			if (selection != 0)
349 				break;
350 
351 			Lock();
352 			fHeaderView->SetText(B_TRANSLATE("Cancelling updates"));
353 			fDetailView->SetText(
354 				B_TRANSLATE("Attempting to cancel the updates"
355 					B_UTF8_ELLIPSIS));
356 			Unlock();
357 			fUserCancelRequested = true;
358 
359 			if (fWaitingForButton) {
360 				fButtonResult = message->what;
361 				delete_sem(fWaitingSem);
362 				fWaitingSem = -1;
363 			}
364 			break;
365 		}
366 
367 		case kMsgUpdateConfirmed:
368 		{
369 			if (fWaitingForButton) {
370 				fButtonResult = message->what;
371 				delete_sem(fWaitingSem);
372 				fWaitingSem = -1;
373 				fUpdateConfirmed = true;
374 			}
375 			break;
376 		}
377 
378 		case kMsgMoreDetailsToggle:
379 			fListView->SetMoreDetails(fDetailsCheckbox->Value() != 0);
380 			PostMessage(kMsgSetZoomLimits);
381 			_WriteSettings();
382 			break;
383 
384 		case kMsgSetZoomLimits:
385 		{
386 			int32 count = fListView->CountItems();
387 			if (count < 1)
388 				break;
389 			// Convert last item's bottom point to its layout group coordinates
390 			BPoint zoomPoint = fListView->ZoomPoint();
391 			fScrollView->ConvertToParent(&zoomPoint);
392 			// Determine which BControl object height to use
393 			float controlHeight;
394 			if (fUpdateButtonLayoutItem->IsVisible())
395 				fUpdateButton->GetPreferredSize(NULL, &controlHeight);
396 			else
397 				fDetailsCheckbox->GetPreferredSize(NULL, &controlHeight);
398 			// Calculate height and width values
399 			float zoomHeight = fZoomHeightBaseline + zoomPoint.y
400 				+ controlHeight;
401 			float zoomWidth = fZoomWidthBaseline + zoomPoint.x;
402 			SetZoomLimits(zoomWidth, zoomHeight);
403 			break;
404 		}
405 
406 		case kMsgWarningDismissed:
407 			fWarningAlertCount--;
408 			break;
409 
410 		case kMsgWindowFrameChanged:
411 			delete fMessageRunner;
412 			fMessageRunner = NULL;
413 			_WriteSettings();
414 			break;
415 
416 		case kMsgGetUpdateType:
417 		{
418 			BString text(
419 				B_TRANSLATE("Please choose from these update options:\n\n"
420 				"Update:\n"
421 				"	Updates all installed packages.\n"
422 				"Full sync:\n"
423 				"	Synchronizes the installed packages with the repositories."
424 				));
425 			BAlert* alert = new BAlert("update_type",
426 				text,
427 				B_TRANSLATE_COMMENT("Cancel", "Alert button label"),
428 				B_TRANSLATE_COMMENT("Full sync","Alert button label"),
429 				B_TRANSLATE_COMMENT("Update","Alert button label"),
430 				B_WIDTH_AS_USUAL, B_INFO_ALERT);
431 			int32 result = alert->Go();
432 			int32 action = INVALID_SELECTION;
433 			switch(result) {
434 				case 0:
435 					action = CANCEL_UPDATE;
436 					break;
437 
438 				case 1:
439 					action = FULLSYNC;
440 					break;
441 
442 				case 2:
443 					action = UPDATE;
444 					break;
445 			}
446 			BMessage reply;
447 			reply.AddInt32(kKeyAlertResult, action);
448 			message->SendReply(&reply);
449 			break;
450 		}
451 
452 		case kMsgNoRepositories:
453 		{
454 			BString text(
455 				B_TRANSLATE_COMMENT(
456 				"No remote repositories are available. Please verify that some"
457 				" repositories are enabled using the Repositories preflet or"
458 				" the \'pkgman\' command.", "Error message"));
459 			BAlert* alert = new BAlert("repositories", text,
460 				B_TRANSLATE_COMMENT("Quit", "Alert button label"),
461 				B_TRANSLATE_COMMENT("Open Repositories","Alert button label"),
462 				NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
463 			int32 result = alert->Go();
464 			BMessage reply;
465 			reply.AddInt32(kKeyAlertResult, result);
466 			message->SendReply(&reply);
467 			break;
468 		}
469 
470 		default:
471 			BWindow::MessageReceived(message);
472 	}
473 }
474 
475 
476 bool
ConfirmUpdates()477 SoftwareUpdaterWindow::ConfirmUpdates()
478 {
479 	Lock();
480 	fHeaderView->SetText(B_TRANSLATE("Updates found"));
481 	fDetailView->SetText(B_TRANSLATE("The following changes will be made:"));
482 	fListView->SortItems();
483 	Unlock();
484 
485 	uint32 priorState = _GetState();
486 	_SetState(STATE_GET_CONFIRMATION);
487 
488 	_WaitForButtonClick();
489 	_SetState(priorState);
490 	return fButtonResult == kMsgUpdateConfirmed;
491 }
492 
493 
494 void
UpdatesApplying(const char * header,const char * detail)495 SoftwareUpdaterWindow::UpdatesApplying(const char* header, const char* detail)
496 {
497 	Lock();
498 	fHeaderView->SetText(header);
499 	fDetailView->SetText(detail);
500 	Unlock();
501 	_SetState(STATE_APPLY_UPDATES);
502 }
503 
504 
505 bool
UserCancelRequested()506 SoftwareUpdaterWindow::UserCancelRequested()
507 {
508 	if (_GetState() > STATE_GET_CONFIRMATION)
509 		return false;
510 
511 	return fUserCancelRequested;
512 }
513 
514 
515 void
AddPackageInfo(uint32 install_type,const char * package_name,const char * cur_ver,const char * new_ver,const char * summary,const char * repository,const char * file_name)516 SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type,
517 	const char* package_name, const char* cur_ver, const char* new_ver,
518 	const char* summary, const char* repository, const char* file_name)
519 {
520 	Lock();
521 	fListView->AddPackage(install_type, package_name, cur_ver, new_ver,
522 		summary, repository, file_name);
523 	Unlock();
524 }
525 
526 
527 void
ShowWarningAlert(const char * text)528 SoftwareUpdaterWindow::ShowWarningAlert(const char* text)
529 {
530 	BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL, NULL,
531 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
532 	alert->Go(&fWarningAlertDismissed);
533 	alert->CenterIn(Frame());
534 	// Offset multiple alerts
535 	alert->MoveBy(fWarningAlertCount * 15, fWarningAlertCount * 15);
536 	fWarningAlertCount++;
537 }
538 
539 
540 BBitmap
GetIcon(int32 iconSize)541 SoftwareUpdaterWindow::GetIcon(int32 iconSize)
542 {
543 	BBitmap icon(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
544 	team_info teamInfo;
545 	get_team_info(B_CURRENT_TEAM, &teamInfo);
546 	app_info appInfo;
547 	be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
548 	BNodeInfo::GetTrackerIcon(&appInfo.ref, &icon, icon_size(iconSize));
549 	return icon;
550 }
551 
552 
553 void
FinalUpdate(const char * header,const char * detail)554 SoftwareUpdaterWindow::FinalUpdate(const char* header, const char* detail)
555 {
556 	if (_GetState() == STATE_FINAL_MESSAGE)
557 		return;
558 
559 	_SetState(STATE_FINAL_MESSAGE);
560 	Lock();
561 	fHeaderView->SetText(header);
562 	fDetailView->SetText(detail);
563 	Unlock();
564 }
565 
566 
567 BLayoutItem*
layout_item_for(BView * view)568 SoftwareUpdaterWindow::layout_item_for(BView* view)
569 {
570 	BLayout* layout = view->Parent()->GetLayout();
571 	int32 index = layout->IndexOfView(view);
572 	return layout->ItemAt(index);
573 }
574 
575 
576 uint32
_WaitForButtonClick()577 SoftwareUpdaterWindow::_WaitForButtonClick()
578 {
579 	fButtonResult = 0;
580 	fWaitingForButton = true;
581 	fWaitingSem = create_sem(0, "WaitingSem");
582 	while (acquire_sem(fWaitingSem) == B_INTERRUPTED) {
583 	}
584 	fWaitingForButton = false;
585 	return fButtonResult;
586 }
587 
588 
589 void
_SetState(uint32 state)590 SoftwareUpdaterWindow::_SetState(uint32 state)
591 {
592 	if (state <= STATE_HEAD || state >= STATE_MAX)
593 		return;
594 
595 	Lock();
596 
597 	// Initial settings
598 	if (fCurrentState == STATE_HEAD) {
599 		fProgressLayoutItem->SetVisible(false);
600 		fPackagesLayoutItem->SetVisible(false);
601 		fDetailsCheckboxLayoutItem->SetVisible(false);
602 		fCancelButtonLayoutItem->SetVisible(false);
603 		fRebootButtonLayoutItem->SetVisible(false);
604 	}
605 	fCurrentState = state;
606 
607 	// Update confirmation button
608 	// Show only when asking for confirmation to update
609 	if (fCurrentState == STATE_GET_CONFIRMATION)
610 		fUpdateButtonLayoutItem->SetVisible(true);
611 	else
612 		fUpdateButtonLayoutItem->SetVisible(false);
613 
614 	// View package info view and checkbox
615 	// Show at confirmation prompt, hide at final update
616 	if (fCurrentState == STATE_GET_CONFIRMATION) {
617 		fPackagesLayoutItem->SetVisible(true);
618 		fDetailsCheckboxLayoutItem->SetVisible(true);
619 		if (fSettingsReadStatus == B_OK) {
620 			bool showMoreDetails;
621 			status_t result = fInitialSettings.FindBool(kKeyShowDetails,
622 				&showMoreDetails);
623 			if (result == B_OK) {
624 				fDetailsCheckbox->SetValue(showMoreDetails ? 1 : 0);
625 				fListView->SetMoreDetails(showMoreDetails);
626 			}
627 		}
628 	} else if (fCurrentState == STATE_FINAL_MESSAGE) {
629 		fPackagesLayoutItem->SetVisible(false);
630 		fDetailsCheckboxLayoutItem->SetVisible(false);
631 	}
632 
633 	// Progress bar and string view
634 	// Hide detail text while showing status bar
635 	if (fCurrentState == STATE_DISPLAY_PROGRESS) {
636 		fDetailsLayoutItem->SetVisible(false);
637 		fProgressLayoutItem->SetVisible(true);
638 	} else {
639 		fProgressLayoutItem->SetVisible(false);
640 		fDetailsLayoutItem->SetVisible(true);
641 	}
642 
643 	// Resizing and zooming
644 	if (fCurrentState == STATE_GET_CONFIRMATION) {
645 		// Enable resizing and zooming
646 		float defaultWidth = fDefaultRect.Width();
647 		SetSizeLimits(defaultWidth, B_SIZE_UNLIMITED,
648 			fDefaultRect.Height() + 4 * fListView->ItemHeight(),
649 			B_SIZE_UNLIMITED);
650 		SetFlags(Flags() ^ (B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
651 		PostMessage(kMsgSetZoomLimits);
652 		// Recall saved settings
653 		BScreen screen(this);
654 		BRect screenFrame = screen.Frame();
655 		bool windowResized = false;
656 		if (fSettingsReadStatus == B_OK) {
657 			BRect windowFrame;
658 			status_t result = fInitialSettings.FindRect(kKeyWindowFrame,
659 				&windowFrame);
660 			if (result == B_OK) {
661 				if (screenFrame.Contains(windowFrame)) {
662 					ResizeTo(windowFrame.Width(), windowFrame.Height());
663 					windowResized = true;
664 				}
665 			}
666 		}
667 		if (!windowResized)
668 			ResizeTo(defaultWidth, .75 * defaultWidth);
669 		// Check that the bottom of window is on screen
670 		float screenBottom = screenFrame.bottom;
671 		float windowBottom = DecoratorFrame().bottom;
672 		if (windowBottom > screenBottom)
673 			MoveBy(0, screenBottom - windowBottom);
674 		fSaveFrameChanges = true;
675 	} else if (fUpdateConfirmed && (fCurrentState == STATE_DISPLAY_PROGRESS
676 			|| fCurrentState == STATE_DISPLAY_STATUS)) {
677 		PostMessage(kMsgSetZoomLimits);
678 	} else if (fCurrentState == STATE_APPLY_UPDATES)
679 		fSaveFrameChanges = false;
680 	else if (fCurrentState == STATE_FINAL_MESSAGE) {
681 		// Disable resizing and zooming
682 		fSaveFrameChanges = false;
683 		ResizeTo(fDefaultRect.Width(), fDefaultRect.Height());
684 		SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_RESIZABLE
685 			| B_NOT_ZOOMABLE);
686 	}
687 
688 	// Quit button
689 	if (fCurrentState == STATE_FINAL_MESSAGE) {
690 		fCancelButtonLayoutItem->SetVisible(true);
691  		fCancelButton->SetLabel(B_TRANSLATE_COMMENT("Quit", "Button label"));
692 		fCancelButton->MakeDefault(true);
693 	}
694 
695 	Unlock();
696 }
697 
698 
699 uint32
_GetState()700 SoftwareUpdaterWindow::_GetState()
701 {
702 	return fCurrentState;
703 }
704 
705 
706 status_t
_WriteSettings()707 SoftwareUpdaterWindow::_WriteSettings()
708 {
709 	BFile file;
710 	status_t status = file.SetTo(fSettingsPath.Path(),
711 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
712 	if (status == B_OK) {
713 		BMessage settings;
714 		settings.AddBool(kKeyShowDetails, fDetailsCheckbox->Value() != 0);
715 		settings.AddRect(kKeyWindowFrame, Frame());
716 		status = settings.Flatten(&file);
717 	}
718 	file.Unset();
719 	return status;
720 }
721 
722 
723 status_t
_ReadSettings(BMessage & settings)724 SoftwareUpdaterWindow::_ReadSettings(BMessage& settings)
725 {
726 	BFile file;
727 	status_t status = file.SetTo(fSettingsPath.Path(), B_READ_ONLY);
728 	if (status == B_OK)
729 		status = settings.Unflatten(&file);
730 	file.Unset();
731 	return status;
732 }
733 
734 
SuperItem(const char * label)735 SuperItem::SuperItem(const char* label)
736 	:
737 	BListItem(),
738 	fLabel(label),
739 	fRegularFont(be_plain_font),
740 	fBoldFont(be_plain_font),
741 	fShowMoreDetails(false),
742 	fPackageLessIcon(NULL),
743 	fPackageMoreIcon(NULL),
744 	fItemCount(0)
745 {
746 	fBoldFont.SetFace(B_BOLD_FACE);
747 	fBoldFont.GetHeight(&fBoldFontHeight);
748 	font_height fontHeight;
749 	fRegularFont.GetHeight(&fontHeight);
750 	fPackageItemLineHeight = fontHeight.ascent + fontHeight.descent
751 		+ fontHeight.leading;
752 	fPackageLessIcon = _GetPackageIcon(GetPackageItemHeight(false));
753 	fPackageMoreIcon = _GetPackageIcon(GetPackageItemHeight(true));
754 }
755 
756 
~SuperItem()757 SuperItem::~SuperItem()
758 {
759 	delete fPackageLessIcon;
760 	delete fPackageMoreIcon;
761 }
762 
763 
764 void
DrawItem(BView * owner,BRect item_rect,bool complete)765 SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete)
766 {
767 	owner->PushState();
768 
769 	float width;
770 	owner->GetPreferredSize(&width, NULL);
771 	BString text(fItemText);
772 	owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
773 	owner->SetFont(&fBoldFont);
774 	owner->TruncateString(&text, B_TRUNCATE_END, width);
775 	owner->DrawString(text.String(), BPoint(item_rect.left,
776 		item_rect.bottom - fBoldFontHeight.descent));
777 
778 	owner->PopState();
779 }
780 
781 
782 float
GetPackageItemHeight()783 SuperItem::GetPackageItemHeight()
784 {
785 	return GetPackageItemHeight(fShowMoreDetails);
786 }
787 
788 
789 float
GetPackageItemHeight(bool showMoreDetails)790 SuperItem::GetPackageItemHeight(bool showMoreDetails)
791 {
792 	int lineCount = showMoreDetails ? 3 : 2;
793 	return lineCount * fPackageItemLineHeight;
794 }
795 
796 
797 BBitmap*
GetIcon(bool showMoreDetails)798 SuperItem::GetIcon(bool showMoreDetails)
799 {
800 	if (showMoreDetails)
801 		return fPackageMoreIcon;
802 	else
803 		return fPackageLessIcon;
804 }
805 
806 
807 float
GetIconSize(bool showMoreDetails)808 SuperItem::GetIconSize(bool showMoreDetails)
809 {
810 	if (showMoreDetails)
811 		return fPackageMoreIcon->Bounds().Height();
812 	else
813 		return fPackageLessIcon->Bounds().Height();
814 }
815 
816 
817 void
SetDetailLevel(bool showMoreDetails)818 SuperItem::SetDetailLevel(bool showMoreDetails)
819 {
820 	fShowMoreDetails = showMoreDetails;
821 }
822 
823 
824 void
SetItemCount(int32 count)825 SuperItem::SetItemCount(int32 count)
826 {
827 	fItemCount = count;
828 	fItemText = fLabel;
829 	fItemText.Append(" (");
830 	fItemText << fItemCount;
831 	fItemText.Append(")");
832 }
833 
834 
835 float
ZoomWidth(BView * owner)836 SuperItem::ZoomWidth(BView *owner)
837 {
838 	owner->PushState();
839 	owner->SetFont(&fBoldFont);
840 	float width = owner->StringWidth(fItemText.String());
841 	owner->PopState();
842 	return width;
843 }
844 
845 
846 BBitmap*
_GetPackageIcon(float listItemHeight)847 SuperItem::_GetPackageIcon(float listItemHeight)
848 {
849 	int32 iconSize = int(listItemHeight * .8);
850 	status_t result = B_ERROR;
851 	BRect iconRect(0, 0, iconSize - 1, iconSize - 1);
852 	BBitmap* packageIcon = new BBitmap(iconRect, 0, B_RGBA32);
853 	BMimeType nodeType;
854 	nodeType.SetTo("application/x-vnd.haiku-package");
855 	result = nodeType.GetIcon(packageIcon, icon_size(iconSize));
856 	// Get super type icon
857 	if (result != B_OK) {
858 		BMimeType superType;
859 		if (nodeType.GetSupertype(&superType) == B_OK)
860 			result = superType.GetIcon(packageIcon, icon_size(iconSize));
861 	}
862 	if (result != B_OK) {
863 		delete packageIcon;
864 		return NULL;
865 	}
866 	return packageIcon;
867 }
868 
869 
PackageItem(const char * name,const char * simple_version,const char * detailed_version,const char * repository,const char * summary,const char * file_name,SuperItem * super)870 PackageItem::PackageItem(const char* name, const char* simple_version,
871 	const char* detailed_version, const char* repository, const char* summary,
872 	const char* file_name, SuperItem* super)
873 	:
874 	BListItem(),
875 	fName(name),
876 	fSimpleVersion(simple_version),
877 	fDetailedVersion(detailed_version),
878 	fRepository(repository),
879 	fSummary(summary),
880 	fSmallFont(be_plain_font),
881 	fSuperItem(super),
882 	fFileName(file_name),
883 	fDownloadProgress(0),
884 	fDrawBarFlag(false),
885 	fMoreDetailsWidth(0),
886 	fLessDetailsWidth(0)
887 {
888 	fLabelOffset = be_control_look->DefaultLabelSpacing();
889 	fSmallFont.SetSize(be_plain_font->Size() - 2);
890 	fSmallFont.GetHeight(&fSmallFontHeight);
891 	fSmallTotalHeight = fSmallFontHeight.ascent + fSmallFontHeight.descent
892 		+ fSmallFontHeight.leading;
893 }
894 
895 
896 void
DrawItem(BView * owner,BRect item_rect,bool complete)897 PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete)
898 {
899 	owner->PushState();
900 
901 	float width = owner->Frame().Width();
902 	float nameWidth = width / 2.0;
903 	float offsetWidth = 0;
904 	bool showMoreDetails = fSuperItem->GetDetailLevel();
905 
906 	BBitmap* icon = fSuperItem->GetIcon(showMoreDetails);
907 	if (icon != NULL && icon->IsValid()) {
908 		float iconSize = icon->Bounds().Height();
909 		float offsetMarginHeight = floor((Height() - iconSize) / 2);
910 		owner->SetDrawingMode(B_OP_ALPHA);
911 		BPoint location = BPoint(item_rect.left,
912 			item_rect.top + offsetMarginHeight);
913 		owner->DrawBitmap(icon, location);
914 		owner->SetDrawingMode(B_OP_COPY);
915 		offsetWidth = iconSize + fLabelOffset;
916 
917 		if (fDrawBarFlag)
918 			_DrawBar(location, owner, icon_size(iconSize));
919 	}
920 
921 	owner->SetFont(be_plain_font);
922 	owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
923 
924 	// Package name
925 	BString name(fName);
926 	owner->TruncateString(&name, B_TRUNCATE_END, nameWidth);
927 	BPoint cursor(item_rect.left + offsetWidth,
928 		item_rect.bottom - fSmallTotalHeight - fSmallFontHeight.descent - 2);
929 	if (showMoreDetails)
930 		cursor.y -= fSmallTotalHeight + 1;
931 	owner->DrawString(name.String(), cursor);
932 	cursor.x += owner->StringWidth(name.String()) + fLabelOffset;
933 
934 	// Change font and color
935 	owner->SetFont(&fSmallFont);
936 	owner->SetHighColor(tint_color(ui_color(B_LIST_ITEM_TEXT_COLOR), 0.7));
937 
938 	// Simple version or repository
939 	BString versionOrRepo;
940 	if (showMoreDetails)
941 		versionOrRepo.SetTo(fRepository);
942 	else
943 		versionOrRepo.SetTo(fSimpleVersion);
944 	owner->TruncateString(&versionOrRepo, B_TRUNCATE_END, width - cursor.x);
945 	owner->DrawString(versionOrRepo.String(), cursor);
946 
947 	// Summary
948 	BString summary(fSummary);
949 	cursor.x = item_rect.left + offsetWidth;
950 	cursor.y += fSmallTotalHeight;
951 	owner->TruncateString(&summary, B_TRUNCATE_END, width - cursor.x);
952 	owner->DrawString(summary.String(), cursor);
953 
954 	// Detailed version
955 	if (showMoreDetails) {
956 		BString version(fDetailedVersion);
957 		cursor.y += fSmallTotalHeight;
958 		owner->TruncateString(&version, B_TRUNCATE_END, width - cursor.x);
959 		owner->DrawString(version.String(), cursor);
960 	}
961 
962 	owner->PopState();
963 }
964 
965 
966 // Modified slightly from Tracker's BPose::DrawBar
967 void
_DrawBar(BPoint where,BView * view,icon_size which)968 PackageItem::_DrawBar(BPoint where, BView* view, icon_size which)
969 {
970 	int32 yOffset;
971 	int32 size = which - 1;
972 	int32 barWidth = (int32)(7.0f / 32.0f * (float)which);
973 	if (barWidth < 4) {
974 		barWidth = 4;
975 		yOffset = 0;
976 	} else
977 		yOffset = 2;
978 	int32 barHeight = size - 3 - 2 * yOffset;
979 
980 
981 	// the black shadowed line
982 	view->SetHighColor(32, 32, 32, 92);
983 	view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
984 	view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
985 	view->StrokeLine(BPoint(where.x + size - barWidth + 1,
986 		where.y + size - yOffset));
987 
988 	view->SetDrawingMode(B_OP_ALPHA);
989 
990 	// the gray frame
991 	view->SetHighColor(76, 76, 76, 192);
992 	BRect rect(where.x + size - barWidth,where.y + yOffset,
993 		where.x + size - 1,where.y + size - 1 - yOffset);
994 	view->StrokeRect(rect);
995 
996 	// calculate bar height
997 	int32 barPos = barHeight - int32(barHeight * fDownloadProgress / 100.0);
998 	if (barPos < 0)
999 		barPos = 0;
1000 	else if (barPos > barHeight)
1001 		barPos = barHeight;
1002 
1003 	// the free space bar
1004 	view->SetHighColor(255, 255, 255, 192);
1005 
1006 	rect.InsetBy(1,1);
1007 	BRect bar(rect);
1008 	bar.bottom = bar.top + barPos - 1;
1009 	if (barPos > 0)
1010 		view->FillRect(bar);
1011 
1012 	// the used space bar
1013 	bar.top = bar.bottom + 1;
1014 	bar.bottom = rect.bottom;
1015 	view->SetHighColor(0, 203, 0, 192);
1016 	view->FillRect(bar);
1017 }
1018 
1019 
1020 void
Update(BView * owner,const BFont * font)1021 PackageItem::Update(BView *owner, const BFont *font)
1022 {
1023 	BListItem::Update(owner, font);
1024 	SetHeight(fSuperItem->GetPackageItemHeight());
1025 }
1026 
1027 
1028 void
CalculateZoomWidths(BView * owner)1029 PackageItem::CalculateZoomWidths(BView *owner)
1030 {
1031 	owner->PushState();
1032 
1033 	// More details
1034 	float offsetWidth = 2 * be_control_look->DefaultItemSpacing()
1035 		+ be_plain_font->Size()
1036 		+ fSuperItem->GetIconSize(true) + fLabelOffset;
1037 	// Name and repo
1038 	owner->SetFont(be_plain_font);
1039 	float stringWidth = owner->StringWidth(fName.String());
1040 	owner->SetFont(&fSmallFont);
1041 	stringWidth += fLabelOffset + owner->StringWidth(fRepository.String());
1042 	// Summary
1043 	float summaryWidth = owner->StringWidth(fSummary.String());
1044 	if (summaryWidth > stringWidth)
1045 		stringWidth = summaryWidth;
1046 	// Version
1047 	float versionWidth = owner->StringWidth(fDetailedVersion.String());
1048 	if (versionWidth > stringWidth)
1049 		stringWidth = versionWidth;
1050 	fMoreDetailsWidth = offsetWidth + stringWidth;
1051 
1052 	// Less details
1053 	offsetWidth = 2 * be_control_look->DefaultItemSpacing()
1054 		+ be_plain_font->Size()
1055 		+ fSuperItem->GetIconSize(false) + fLabelOffset;
1056 	// Name and version
1057 	owner->SetFont(be_plain_font);
1058 	stringWidth = owner->StringWidth(fName.String());
1059 	owner->SetFont(&fSmallFont);
1060 	stringWidth += fLabelOffset + owner->StringWidth(fSimpleVersion.String());
1061 	// Summary
1062 	if (summaryWidth > stringWidth)
1063 		stringWidth = summaryWidth;
1064 	fLessDetailsWidth = offsetWidth + stringWidth;
1065 
1066 	owner->PopState();
1067 }
1068 
1069 
1070 int
NameCompare(PackageItem * item)1071 PackageItem::NameCompare(PackageItem* item)
1072 {
1073 	// sort by package name
1074 	return fName.ICompare(item->fName);
1075 }
1076 
1077 
1078 void
SetDownloadProgress(float percent)1079 PackageItem::SetDownloadProgress(float percent)
1080 {
1081 	fDownloadProgress = percent;
1082 }
1083 
1084 
1085 int
SortPackageItems(const BListItem * item1,const BListItem * item2)1086 SortPackageItems(const BListItem* item1, const BListItem* item2)
1087 {
1088 	PackageItem* first = (PackageItem*)item1;
1089 	PackageItem* second = (PackageItem*)item2;
1090 	return first->NameCompare(second);
1091 }
1092 
1093 
PackageListView()1094 PackageListView::PackageListView()
1095 	:
1096 	BOutlineListView("Package list"),
1097 	fSuperUpdateItem(NULL),
1098 	fSuperInstallItem(NULL),
1099 	fSuperUninstallItem(NULL),
1100 	fShowMoreDetails(false),
1101 	fLastProgressItem(NULL),
1102 	fLastProgressValue(-1)
1103 {
1104 	SetExplicitMinSize(BSize(B_SIZE_UNSET, 40));
1105 	SetExplicitPreferredSize(BSize(B_SIZE_UNSET, 400));
1106 }
1107 
1108 
1109 void
FrameResized(float newWidth,float newHeight)1110 PackageListView::FrameResized(float newWidth, float newHeight)
1111 {
1112 	BOutlineListView::FrameResized(newWidth, newHeight);
1113 	Invalidate();
1114 }
1115 
1116 
1117 void
ExpandOrCollapse(BListItem * superItem,bool expand)1118 PackageListView::ExpandOrCollapse(BListItem *superItem, bool expand)
1119 {
1120 	BOutlineListView::ExpandOrCollapse(superItem, expand);
1121 	Window()->PostMessage(kMsgSetZoomLimits);
1122 }
1123 
1124 
1125 void
AddPackage(uint32 install_type,const char * name,const char * cur_ver,const char * new_ver,const char * summary,const char * repository,const char * file_name)1126 PackageListView::AddPackage(uint32 install_type, const char* name,
1127 	const char* cur_ver, const char* new_ver, const char* summary,
1128 	const char* repository, const char* file_name)
1129 {
1130 	SuperItem* super;
1131 	BString simpleVersion;
1132 	BString detailedVersion("");
1133 	BString repositoryText(B_TRANSLATE_COMMENT("from repository",
1134 		"List item text"));
1135 	repositoryText.Append(" ").Append(repository);
1136 
1137 	switch (install_type) {
1138 		case PACKAGE_UPDATE:
1139 		{
1140 			if (fSuperUpdateItem == NULL) {
1141 				fSuperUpdateItem = new SuperItem(B_TRANSLATE_COMMENT(
1142 					"Packages to be updated", "List super item label"));
1143 				AddItem(fSuperUpdateItem);
1144 			}
1145 			super = fSuperUpdateItem;
1146 
1147 			simpleVersion.SetTo(new_ver);
1148 			detailedVersion.Append(B_TRANSLATE_COMMENT("Updating version",
1149 					"List item text"))
1150 				.Append(" ").Append(cur_ver)
1151 				.Append(" ").Append(B_TRANSLATE_COMMENT("to",
1152 					"List item text"))
1153 				.Append(" ").Append(new_ver);
1154 			break;
1155 		}
1156 
1157 		case PACKAGE_INSTALL:
1158 		{
1159 			if (fSuperInstallItem == NULL) {
1160 				fSuperInstallItem = new SuperItem(B_TRANSLATE_COMMENT(
1161 					"New packages to be installed", "List super item label"));
1162 				AddItem(fSuperInstallItem);
1163 			}
1164 			super = fSuperInstallItem;
1165 
1166 			simpleVersion.SetTo(new_ver);
1167 			detailedVersion.Append(B_TRANSLATE_COMMENT("Installing version",
1168 					"List item text"))
1169 				.Append(" ").Append(new_ver);
1170 			break;
1171 		}
1172 
1173 		case PACKAGE_UNINSTALL:
1174 		{
1175 			if (fSuperUninstallItem == NULL) {
1176 				fSuperUninstallItem = new SuperItem(B_TRANSLATE_COMMENT(
1177 					"Packages to be uninstalled", "List super item label"));
1178 				AddItem(fSuperUninstallItem);
1179 			}
1180 			super = fSuperUninstallItem;
1181 
1182 			simpleVersion.SetTo("");
1183 			detailedVersion.Append(B_TRANSLATE_COMMENT("Uninstalling version",
1184 					"List item text"))
1185 				.Append(" ").Append(cur_ver);
1186 			break;
1187 		}
1188 
1189 		default:
1190 			return;
1191 
1192 	}
1193 	PackageItem* item = new PackageItem(name, simpleVersion.String(),
1194 		detailedVersion.String(), repositoryText.String(), summary, file_name,
1195 		super);
1196 	AddUnder(item, super);
1197 	super->SetItemCount(CountItemsUnder(super, true));
1198 	item->CalculateZoomWidths(this);
1199 }
1200 
1201 
1202 void
UpdatePackageProgress(const char * packageName,float percent)1203 PackageListView::UpdatePackageProgress(const char* packageName, float percent)
1204 {
1205 	// Update only every 1 percent change
1206 	int16 wholePercent = int16(percent);
1207 	if (wholePercent == fLastProgressValue)
1208 		return;
1209 	fLastProgressValue = wholePercent;
1210 
1211 	// A new package started downloading, find the PackageItem by name
1212 	if (percent == 0) {
1213 		fLastProgressItem = NULL;
1214 		int32 count = FullListCountItems();
1215 		for (int32 i = 0; i < count; i++) {
1216 			PackageItem* item = dynamic_cast<PackageItem*>(FullListItemAt(i));
1217 			if (item != NULL && strcmp(item->FileName(), packageName) == 0) {
1218 				fLastProgressItem = item;
1219 				fLastProgressItem->ShowProgressBar();
1220 				break;
1221 			}
1222 		}
1223 	}
1224 
1225 	if (fLastProgressItem != NULL) {
1226 		fLastProgressItem->SetDownloadProgress(percent);
1227 		Invalidate();
1228 	}
1229 }
1230 
1231 
1232 void
SortItems()1233 PackageListView::SortItems()
1234 {
1235 	if (fSuperUpdateItem != NULL)
1236 		SortItemsUnder(fSuperUpdateItem, true, SortPackageItems);
1237 	if (fSuperInstallItem != NULL)
1238 		SortItemsUnder(fSuperInstallItem, true, SortPackageItems);
1239 	if (fSuperUninstallItem != NULL)
1240 		SortItemsUnder(fSuperUninstallItem, true, SortPackageItems);
1241 }
1242 
1243 
1244 float
ItemHeight()1245 PackageListView::ItemHeight()
1246 {
1247 	if (fSuperUpdateItem != NULL)
1248 		return fSuperUpdateItem->GetPackageItemHeight();
1249 	if (fSuperInstallItem != NULL)
1250 		return fSuperInstallItem->GetPackageItemHeight();
1251 	if (fSuperUninstallItem != NULL)
1252 		return fSuperUninstallItem->GetPackageItemHeight();
1253 	return 0;
1254 }
1255 
1256 
1257 void
SetMoreDetails(bool showMore)1258 PackageListView::SetMoreDetails(bool showMore)
1259 {
1260 	if (showMore == fShowMoreDetails)
1261 		return;
1262 	fShowMoreDetails = showMore;
1263 	_SetItemHeights();
1264 	InvalidateLayout();
1265 	ResizeToPreferred();
1266 }
1267 
1268 
1269 BPoint
ZoomPoint()1270 PackageListView::ZoomPoint()
1271 {
1272 	BPoint zoomPoint(0, 0);
1273 	int32 count = CountItems();
1274 	for (int32 i = 0; i < count; i++)
1275 	{
1276 		BListItem* item = ItemAt(i);
1277 		float itemWidth = 0;
1278 		if (item->OutlineLevel() == 0) {
1279 			SuperItem* sItem = dynamic_cast<SuperItem*>(item);
1280 			itemWidth = sItem->ZoomWidth(this);
1281 		} else {
1282 			PackageItem* pItem = dynamic_cast<PackageItem*>(item);
1283 			itemWidth = fShowMoreDetails ? pItem->MoreDetailsWidth()
1284 				: pItem->LessDetailsWidth();
1285 		}
1286 		if (itemWidth > zoomPoint.x)
1287 			zoomPoint.x = itemWidth;
1288 	}
1289 	if (count > 0)
1290 		zoomPoint.y = ItemFrame(count - 1).bottom;
1291 
1292 	return zoomPoint;
1293 }
1294 
1295 
1296 void
_SetItemHeights()1297 PackageListView::_SetItemHeights()
1298 {
1299 	int32 itemCount = 0;
1300 	float itemHeight = 0;
1301 	BListItem* item = NULL;
1302 	if (fSuperUpdateItem != NULL) {
1303 		fSuperUpdateItem->SetDetailLevel(fShowMoreDetails);
1304 		itemHeight = fSuperUpdateItem->GetPackageItemHeight();
1305 		itemCount = CountItemsUnder(fSuperUpdateItem, true);
1306 		for (int32 i = 0; i < itemCount; i++) {
1307 			item = ItemUnderAt(fSuperUpdateItem, true, i);
1308 			item->SetHeight(itemHeight);
1309 		}
1310 	}
1311 	if (fSuperInstallItem != NULL) {
1312 		fSuperInstallItem->SetDetailLevel(fShowMoreDetails);
1313 		itemHeight = fSuperInstallItem->GetPackageItemHeight();
1314 		itemCount = CountItemsUnder(fSuperInstallItem, true);
1315 		for (int32 i = 0; i < itemCount; i++) {
1316 			item = ItemUnderAt(fSuperInstallItem, true, i);
1317 			item->SetHeight(itemHeight);
1318 		}
1319 
1320 	}
1321 	if (fSuperUninstallItem != NULL) {
1322 		fSuperUninstallItem->SetDetailLevel(fShowMoreDetails);
1323 		itemHeight = fSuperUninstallItem->GetPackageItemHeight();
1324 		itemCount = CountItemsUnder(fSuperUninstallItem, true);
1325 		for (int32 i = 0; i < itemCount; i++) {
1326 			item = ItemUnderAt(fSuperUninstallItem, true, i);
1327 			item->SetHeight(itemHeight);
1328 		}
1329 
1330 	}
1331 }
1332