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(®isterMessage);
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