libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / apps / softwareupdater / SoftwareUpdaterWindow.cpp
blob95fff6496f3dc2485ee09dd79cb3eb5097557341
1 /*
2 * Copyright 2016-2017 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license
5 * Authors:
6 * Alexander von Gluck IV <kallisti5@unixzen.com>
7 * Brian Hill <supernova@tycho.email>
8 */
11 #include "SoftwareUpdaterWindow.h"
13 #include <Alert.h>
14 #include <AppDefs.h>
15 #include <Application.h>
16 #include <Catalog.h>
17 #include <ControlLook.h>
18 #include <FindDirectory.h>
19 #include <LayoutBuilder.h>
20 #include <LayoutUtils.h>
21 #include <Message.h>
22 #include <Roster.h>
23 #include <Screen.h>
24 #include <String.h>
26 #include "constants.h"
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "SoftwareUpdaterWindow"
32 SoftwareUpdaterWindow::SoftwareUpdaterWindow()
34 BWindow(BRect(0, 0, 300, 10),
35 B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW,
36 B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE | B_NOT_RESIZABLE),
37 fStripeView(NULL),
38 fHeaderView(NULL),
39 fDetailView(NULL),
40 fUpdateButton(NULL),
41 fCancelButton(NULL),
42 fStatusBar(NULL),
43 fCurrentState(STATE_HEAD),
44 fWaitingSem(-1),
45 fWaitingForButton(false),
46 fUpdateConfirmed(false),
47 fUserCancelRequested(false),
48 fWarningAlertCount(0),
49 fSettingsReadStatus(B_ERROR),
50 fSaveFrameChanges(false),
51 fMessageRunner(NULL),
52 fFrameChangeMessage(kMsgWindowFrameChanged)
54 // Layout
55 BBitmap icon = GetIcon(32 * icon_layout_scale());
56 fStripeView = new StripeView(icon);
58 fUpdateButton = new BButton(B_TRANSLATE("Update now"),
59 new BMessage(kMsgUpdateConfirmed));
60 fUpdateButton->MakeDefault(true);
61 fCancelButton = new BButton(B_TRANSLATE("Cancel"),
62 new BMessage(kMsgCancel));
64 fHeaderView = new BStringView("header",
65 B_TRANSLATE("Checking for updates"), B_WILL_DRAW);
66 fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
67 fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
68 fDetailView = new BStringView("detail", B_TRANSLATE("Contacting software "
69 "repositories to check for package updates."), B_WILL_DRAW);
70 fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
71 fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
72 fStatusBar = new BStatusBar("progress");
73 fStatusBar->SetMaxValue(100);
75 fListView = new PackageListView();
76 fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW,
77 false, true);
79 fDetailsCheckbox = new BCheckBox("detailscheckbox",
80 B_TRANSLATE("Show more details"),
81 new BMessage(kMsgMoreDetailsToggle));
83 BFont font;
84 fHeaderView->GetFont(&font);
85 font.SetFace(B_BOLD_FACE);
86 font.SetSize(font.Size() * 1.5);
87 fHeaderView->SetFont(&font,
88 B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE | B_FONT_FLAGS);
90 BLayoutBuilder::Group<>(this, B_HORIZONTAL, B_USE_ITEM_SPACING)
91 .Add(fStripeView)
92 .AddGroup(B_VERTICAL, 0)
93 .SetInsets(0, B_USE_WINDOW_SPACING,
94 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
95 .AddGroup(new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING))
96 .Add(fHeaderView)
97 .Add(fDetailView)
98 .Add(fStatusBar)
99 .Add(fScrollView)
100 .End()
101 .AddStrut(B_USE_SMALL_SPACING)
102 .AddGroup(new BGroupView(B_HORIZONTAL))
103 .Add(fDetailsCheckbox)
104 .AddGlue()
105 .Add(fCancelButton)
106 .Add(fUpdateButton)
107 .End()
108 .End()
109 .End();
111 fDetailsLayoutItem = layout_item_for(fDetailView);
112 fProgressLayoutItem = layout_item_for(fStatusBar);
113 fPackagesLayoutItem = layout_item_for(fScrollView);
114 fCancelButtonLayoutItem = layout_item_for(fCancelButton);
115 fUpdateButtonLayoutItem = layout_item_for(fUpdateButton);
116 fDetailsCheckboxLayoutItem = layout_item_for(fDetailsCheckbox);
118 _SetState(STATE_DISPLAY_STATUS);
119 CenterOnScreen();
120 SetFlags(Flags() ^ B_AUTO_UPDATE_SIZE_LIMITS);
122 // Prevent resizing for now
123 fDefaultRect = Bounds();
124 SetSizeLimits(fDefaultRect.Width(), fDefaultRect.Width(),
125 fDefaultRect.Height(), fDefaultRect.Height());
127 // Read settings file
128 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &fSettingsPath);
129 if (status == B_OK) {
130 fSettingsPath.Append(kSettingsFilename);
131 fSettingsReadStatus = _ReadSettings(fInitialSettings);
133 // Move to saved setting position
134 if (fSettingsReadStatus == B_OK) {
135 BRect windowFrame;
136 status = fInitialSettings.FindRect(kKeyWindowFrame, &windowFrame);
137 if (status == B_OK) {
138 BScreen screen(this);
139 if (screen.Frame().Contains(windowFrame.LeftTop()))
140 MoveTo(windowFrame.LeftTop());
143 Show();
145 BMessage registerMessage(kMsgRegister);
146 registerMessage.AddMessenger(kKeyMessenger, BMessenger(this));
147 be_app->PostMessage(&registerMessage);
149 fCancelAlertResponse.SetMessage(new BMessage(kMsgCancelResponse));
150 fCancelAlertResponse.SetTarget(this);
151 fWarningAlertDismissed.SetMessage(new BMessage(kMsgWarningDismissed));
152 fWarningAlertDismissed.SetTarget(this);
154 // Common elements used for the zoom height and width calculations
155 fZoomHeightBaseline = 6
156 + be_control_look->ComposeSpacing(B_USE_SMALL_SPACING)
157 + 2 * be_control_look->ComposeSpacing(B_USE_WINDOW_SPACING);
158 fZoomWidthBaseline = fStripeView->PreferredSize().Width()
159 + be_control_look->ComposeSpacing(B_USE_ITEM_SPACING)
160 + fScrollView->ScrollBar(B_VERTICAL)->PreferredSize().Width()
161 + be_control_look->ComposeSpacing(B_USE_WINDOW_SPACING);
165 bool
166 SoftwareUpdaterWindow::QuitRequested()
168 PostMessage(kMsgCancel);
169 return false;
173 void
174 SoftwareUpdaterWindow::FrameMoved(BPoint newPosition)
176 BWindow::FrameMoved(newPosition);
178 // Create a message runner to consolidate all function calls from a
179 // move into one message post after moving has ceased for .5 seconds
180 if (fSaveFrameChanges) {
181 if (fMessageRunner == NULL) {
182 fMessageRunner = new BMessageRunner(this, &fFrameChangeMessage,
183 500000, 1);
184 } else
185 fMessageRunner->SetInterval(500000);
190 void
191 SoftwareUpdaterWindow::FrameResized(float newWidth, float newHeight)
193 BWindow::FrameResized(newWidth, newHeight);
195 // Create a message runner to consolidate all function calls from a
196 // resize into one message post after resizing has ceased for .5 seconds
197 if (fSaveFrameChanges) {
198 if (fMessageRunner == NULL) {
199 fMessageRunner = new BMessageRunner(this, &fFrameChangeMessage,
200 500000, 1);
201 } else
202 fMessageRunner->SetInterval(500000);
207 void
208 SoftwareUpdaterWindow::Zoom(BPoint origin, float width, float height)
210 // Override default zoom behavior and keep window at same position instead
211 // of centering on screen
212 BWindow::Zoom(Frame().LeftTop(), width, height);
216 void
217 SoftwareUpdaterWindow::MessageReceived(BMessage* message)
219 switch (message->what) {
221 case kMsgTextUpdate:
223 if (fCurrentState == STATE_DISPLAY_PROGRESS)
224 _SetState(STATE_DISPLAY_STATUS);
225 else if (fCurrentState != STATE_DISPLAY_STATUS)
226 break;
228 BString header;
229 BString detail;
230 Lock();
231 status_t result = message->FindString(kKeyHeader, &header);
232 if (result == B_OK && header != fHeaderView->Text())
233 fHeaderView->SetText(header.String());
234 result = message->FindString(kKeyDetail, &detail);
235 if (result == B_OK)
236 fDetailView->SetText(detail.String());
237 Unlock();
238 break;
241 case kMsgProgressUpdate:
243 if (fCurrentState == STATE_DISPLAY_STATUS)
244 _SetState(STATE_DISPLAY_PROGRESS);
245 else if (fCurrentState != STATE_DISPLAY_PROGRESS)
246 break;
248 BString packageName;
249 status_t result = message->FindString(kKeyPackageName,
250 &packageName);
251 if (result != B_OK)
252 break;
253 BString packageCount;
254 result = message->FindString(kKeyPackageCount, &packageCount);
255 if (result != B_OK)
256 break;
257 float percent;
258 result = message->FindFloat(kKeyPercentage, &percent);
259 if (result != B_OK)
260 break;
262 BString header;
263 Lock();
264 result = message->FindString(kKeyHeader, &header);
265 if (result == B_OK && header != fHeaderView->Text())
266 fHeaderView->SetText(header.String());
267 fStatusBar->SetTo(percent, packageName.String(),
268 packageCount.String());
269 Unlock();
271 fListView->UpdatePackageProgress(packageName.String(), percent);
272 break;
275 case kMsgCancel:
277 if (_GetState() == STATE_FINAL_MESSAGE) {
278 be_app->PostMessage(kMsgFinalQuit);
279 break;
281 if (!fUpdateConfirmed) {
282 // Downloads have not started yet, we will request to cancel
283 // without confirming
284 Lock();
285 fHeaderView->SetText(B_TRANSLATE("Cancelling updates"));
286 fDetailView->SetText(
287 B_TRANSLATE("Attempting to cancel the updates..."));
288 Unlock();
289 fUserCancelRequested = true;
291 if (fWaitingForButton) {
292 fButtonResult = message->what;
293 delete_sem(fWaitingSem);
294 fWaitingSem = -1;
296 break;
299 // Confirm with the user to cancel
300 BAlert* alert = new BAlert("cancel request", B_TRANSLATE("Updates"
301 " have not been completed, are you sure you want to quit?"),
302 B_TRANSLATE("Quit"), B_TRANSLATE("Don't quit"), NULL,
303 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
304 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
305 alert->Go(&fCancelAlertResponse);
306 break;
309 case kMsgCancelResponse:
311 // Verify whether the cancel alert was confirmed
312 int32 selection = -1;
313 message->FindInt32("which", &selection);
314 if (selection != 0)
315 break;
317 Lock();
318 fHeaderView->SetText(B_TRANSLATE("Cancelling updates"));
319 fDetailView->SetText(
320 B_TRANSLATE("Attempting to cancel the updates..."));
321 Unlock();
322 fUserCancelRequested = true;
324 if (fWaitingForButton) {
325 fButtonResult = message->what;
326 delete_sem(fWaitingSem);
327 fWaitingSem = -1;
329 break;
332 case kMsgUpdateConfirmed:
334 if (fWaitingForButton) {
335 fButtonResult = message->what;
336 delete_sem(fWaitingSem);
337 fWaitingSem = -1;
338 fUpdateConfirmed = true;
340 break;
343 case kMsgMoreDetailsToggle:
344 fListView->SetMoreDetails(fDetailsCheckbox->Value() != 0);
345 PostMessage(kMsgSetZoomLimits);
346 _WriteSettings();
347 break;
349 case kMsgSetZoomLimits:
351 int32 count = fListView->CountItems();
352 if (count < 1)
353 break;
354 // Convert last item's bottom point to its layout group coordinates
355 BPoint zoomPoint = fListView->ZoomPoint();
356 fScrollView->ConvertToParent(&zoomPoint);
357 // Determine which BControl object height to use
358 float controlHeight;
359 if (fUpdateButtonLayoutItem->IsVisible())
360 fUpdateButton->GetPreferredSize(NULL, &controlHeight);
361 else
362 fDetailsCheckbox->GetPreferredSize(NULL, &controlHeight);
363 // Calculate height and width values
364 float zoomHeight = fZoomHeightBaseline + zoomPoint.y
365 + controlHeight;
366 float zoomWidth = fZoomWidthBaseline + zoomPoint.x;
367 SetZoomLimits(zoomWidth, zoomHeight);
368 break;
371 case kMsgWarningDismissed:
372 fWarningAlertCount--;
373 break;
375 case kMsgWindowFrameChanged:
376 delete fMessageRunner;
377 fMessageRunner = NULL;
378 _WriteSettings();
379 break;
381 case kMsgGetUpdateType:
383 BString text(
384 B_TRANSLATE("Please choose from these update options:\n\n"
385 "Update:\n"
386 " Updates all installed packages.\n"
387 "Full sync:\n"
388 " Synchronizes the installed packages with the repositories."
390 BAlert* alert = new BAlert("update_type",
391 text,
392 B_TRANSLATE_COMMENT("Cancel", "Alert button label"),
393 B_TRANSLATE_COMMENT("Full sync","Alert button label"),
394 B_TRANSLATE_COMMENT("Update","Alert button label"),
395 B_WIDTH_AS_USUAL, B_INFO_ALERT);
396 int32 result = alert->Go();
397 int32 action = INVALID_SELECTION;
398 switch(result) {
399 case 0:
400 action = CANCEL_UPDATE;
401 break;
403 case 1:
404 action = FULLSYNC;
405 break;
407 case 2:
408 action = UPDATE;
409 break;
411 BMessage reply;
412 reply.AddInt32(kKeyAlertResult, action);
413 message->SendReply(&reply);
414 break;
417 case kMsgNoRepositories:
419 BString text(
420 B_TRANSLATE_COMMENT(
421 "No remote repositories are available. Please verify that some"
422 " repositories are enabled using the Repositories preflet or"
423 " the \'pkgman\' command.", "Error message"));
424 BAlert* alert = new BAlert("repositories", text,
425 B_TRANSLATE_COMMENT("Quit", "Alert button label"),
426 B_TRANSLATE_COMMENT("Open Repositories","Alert button label"),
427 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
428 int32 result = alert->Go();
429 BMessage reply;
430 reply.AddInt32(kKeyAlertResult, result);
431 message->SendReply(&reply);
432 break;
435 default:
436 BWindow::MessageReceived(message);
441 bool
442 SoftwareUpdaterWindow::ConfirmUpdates()
444 Lock();
445 fHeaderView->SetText(B_TRANSLATE("Updates found"));
446 fDetailView->SetText(B_TRANSLATE("The following changes will be made:"));
447 fListView->SortItems();
448 Unlock();
450 uint32 priorState = _GetState();
451 _SetState(STATE_GET_CONFIRMATION);
453 _WaitForButtonClick();
454 _SetState(priorState);
455 return fButtonResult == kMsgUpdateConfirmed;
459 void
460 SoftwareUpdaterWindow::UpdatesApplying(const char* header, const char* detail)
462 Lock();
463 fHeaderView->SetText(header);
464 fDetailView->SetText(detail);
465 Unlock();
466 _SetState(STATE_APPLY_UPDATES);
470 bool
471 SoftwareUpdaterWindow::UserCancelRequested()
473 if (_GetState() > STATE_GET_CONFIRMATION)
474 return false;
476 return fUserCancelRequested;
480 void
481 SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type,
482 const char* package_name, const char* cur_ver, const char* new_ver,
483 const char* summary, const char* repository, const char* file_name)
485 Lock();
486 fListView->AddPackage(install_type, package_name, cur_ver, new_ver,
487 summary, repository, file_name);
488 Unlock();
492 void
493 SoftwareUpdaterWindow::ShowWarningAlert(const char* text)
495 BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL, NULL,
496 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
497 alert->Go(&fWarningAlertDismissed);
498 alert->CenterIn(Frame());
499 // Offset multiple alerts
500 alert->MoveBy(fWarningAlertCount * 15, fWarningAlertCount * 15);
501 fWarningAlertCount++;
505 BBitmap
506 SoftwareUpdaterWindow::GetIcon(int32 iconSize)
508 BBitmap icon(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
509 team_info teamInfo;
510 get_team_info(B_CURRENT_TEAM, &teamInfo);
511 app_info appInfo;
512 be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
513 BNodeInfo::GetTrackerIcon(&appInfo.ref, &icon, icon_size(iconSize));
514 return icon;
518 void
519 SoftwareUpdaterWindow::FinalUpdate(const char* header, const char* detail)
521 if (_GetState() == STATE_FINAL_MESSAGE)
522 return;
524 _SetState(STATE_FINAL_MESSAGE);
525 Lock();
526 fHeaderView->SetText(header);
527 fDetailView->SetText(detail);
528 Unlock();
532 BLayoutItem*
533 SoftwareUpdaterWindow::layout_item_for(BView* view)
535 BLayout* layout = view->Parent()->GetLayout();
536 int32 index = layout->IndexOfView(view);
537 return layout->ItemAt(index);
541 uint32
542 SoftwareUpdaterWindow::_WaitForButtonClick()
544 fButtonResult = 0;
545 fWaitingForButton = true;
546 fWaitingSem = create_sem(0, "WaitingSem");
547 while (acquire_sem(fWaitingSem) == B_INTERRUPTED) {
549 fWaitingForButton = false;
550 return fButtonResult;
554 void
555 SoftwareUpdaterWindow::_SetState(uint32 state)
557 if (state <= STATE_HEAD || state >= STATE_MAX)
558 return;
560 Lock();
562 // Initial settings
563 if (fCurrentState == STATE_HEAD) {
564 fProgressLayoutItem->SetVisible(false);
565 fPackagesLayoutItem->SetVisible(false);
566 fDetailsCheckboxLayoutItem->SetVisible(false);
567 fCancelButtonLayoutItem->SetVisible(false);
569 fCurrentState = state;
571 // Update confirmation button
572 // Show only when asking for confirmation to update
573 if (fCurrentState == STATE_GET_CONFIRMATION)
574 fUpdateButtonLayoutItem->SetVisible(true);
575 else
576 fUpdateButtonLayoutItem->SetVisible(false);
578 // View package info view and checkbox
579 // Show at confirmation prompt, hide at final update
580 if (fCurrentState == STATE_GET_CONFIRMATION) {
581 fPackagesLayoutItem->SetVisible(true);
582 fDetailsCheckboxLayoutItem->SetVisible(true);
583 if (fSettingsReadStatus == B_OK) {
584 bool showMoreDetails;
585 status_t result = fInitialSettings.FindBool(kKeyShowDetails,
586 &showMoreDetails);
587 if (result == B_OK) {
588 fDetailsCheckbox->SetValue(showMoreDetails ? 1 : 0);
589 fListView->SetMoreDetails(showMoreDetails);
592 } else if (fCurrentState == STATE_FINAL_MESSAGE) {
593 fPackagesLayoutItem->SetVisible(false);
594 fDetailsCheckboxLayoutItem->SetVisible(false);
597 // Progress bar and string view
598 // Hide detail text while showing status bar
599 if (fCurrentState == STATE_DISPLAY_PROGRESS) {
600 fDetailsLayoutItem->SetVisible(false);
601 fProgressLayoutItem->SetVisible(true);
602 } else {
603 fProgressLayoutItem->SetVisible(false);
604 fDetailsLayoutItem->SetVisible(true);
607 // Resizing and zooming
608 if (fCurrentState == STATE_GET_CONFIRMATION) {
609 // Enable resizing and zooming
610 float defaultWidth = fDefaultRect.Width();
611 SetSizeLimits(defaultWidth, B_SIZE_UNLIMITED,
612 fDefaultRect.Height() + 4 * fListView->ItemHeight(),
613 B_SIZE_UNLIMITED);
614 SetFlags(Flags() ^ (B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
615 PostMessage(kMsgSetZoomLimits);
616 // Recall saved settings
617 BScreen screen(this);
618 BRect screenFrame = screen.Frame();
619 bool windowResized = false;
620 if (fSettingsReadStatus == B_OK) {
621 BRect windowFrame;
622 status_t result = fInitialSettings.FindRect(kKeyWindowFrame,
623 &windowFrame);
624 if (result == B_OK) {
625 if (screenFrame.Contains(windowFrame)) {
626 ResizeTo(windowFrame.Width(), windowFrame.Height());
627 windowResized = true;
631 if (!windowResized)
632 ResizeTo(defaultWidth, .75 * defaultWidth);
633 // Check that the bottom of window is on screen
634 float screenBottom = screenFrame.bottom;
635 float windowBottom = DecoratorFrame().bottom;
636 if (windowBottom > screenBottom)
637 MoveBy(0, screenBottom - windowBottom);
638 fSaveFrameChanges = true;
639 } else if (fUpdateConfirmed && (fCurrentState == STATE_DISPLAY_PROGRESS
640 || fCurrentState == STATE_DISPLAY_STATUS)) {
641 PostMessage(kMsgSetZoomLimits);
642 } else if (fCurrentState == STATE_APPLY_UPDATES)
643 fSaveFrameChanges = false;
644 else if (fCurrentState == STATE_FINAL_MESSAGE) {
645 // Disable resizing and zooming
646 fSaveFrameChanges = false;
647 ResizeTo(fDefaultRect.Width(), fDefaultRect.Height());
648 SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_RESIZABLE
649 | B_NOT_ZOOMABLE);
652 // Quit button
653 if (fCurrentState == STATE_FINAL_MESSAGE) {
654 fCancelButtonLayoutItem->SetVisible(true);
655 fCancelButton->SetLabel(B_TRANSLATE_COMMENT("Quit", "Button label"));
656 fCancelButton->MakeDefault(true);
659 Unlock();
663 uint32
664 SoftwareUpdaterWindow::_GetState()
666 return fCurrentState;
670 status_t
671 SoftwareUpdaterWindow::_WriteSettings()
673 BFile file;
674 status_t status = file.SetTo(fSettingsPath.Path(),
675 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
676 if (status == B_OK) {
677 BMessage settings;
678 settings.AddBool(kKeyShowDetails, fDetailsCheckbox->Value() != 0);
679 settings.AddRect(kKeyWindowFrame, Frame());
680 status = settings.Flatten(&file);
682 file.Unset();
683 return status;
687 status_t
688 SoftwareUpdaterWindow::_ReadSettings(BMessage& settings)
690 BFile file;
691 status_t status = file.SetTo(fSettingsPath.Path(), B_READ_ONLY);
692 if (status == B_OK)
693 status = settings.Unflatten(&file);
694 file.Unset();
695 return status;
699 SuperItem::SuperItem(const char* label)
701 BListItem(),
702 fLabel(label),
703 fRegularFont(be_plain_font),
704 fBoldFont(be_plain_font),
705 fShowMoreDetails(false),
706 fPackageLessIcon(NULL),
707 fPackageMoreIcon(NULL),
708 fItemCount(0)
710 fBoldFont.SetFace(B_BOLD_FACE);
711 fBoldFont.GetHeight(&fBoldFontHeight);
712 font_height fontHeight;
713 fRegularFont.GetHeight(&fontHeight);
714 fPackageItemLineHeight = fontHeight.ascent + fontHeight.descent
715 + fontHeight.leading;
716 fPackageLessIcon = _GetPackageIcon(GetPackageItemHeight(false));
717 fPackageMoreIcon = _GetPackageIcon(GetPackageItemHeight(true));
721 SuperItem::~SuperItem()
723 delete fPackageLessIcon;
724 delete fPackageMoreIcon;
728 void
729 SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete)
731 owner->PushState();
733 float width;
734 owner->GetPreferredSize(&width, NULL);
735 BString text(fItemText);
736 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
737 owner->SetFont(&fBoldFont);
738 owner->TruncateString(&text, B_TRUNCATE_END, width);
739 owner->DrawString(text.String(), BPoint(item_rect.left,
740 item_rect.bottom - fBoldFontHeight.descent));
742 owner->PopState();
746 float
747 SuperItem::GetPackageItemHeight()
749 return GetPackageItemHeight(fShowMoreDetails);
753 float
754 SuperItem::GetPackageItemHeight(bool showMoreDetails)
756 int lineCount = showMoreDetails ? 3 : 2;
757 return lineCount * fPackageItemLineHeight;
761 BBitmap*
762 SuperItem::GetIcon(bool showMoreDetails)
764 if (showMoreDetails)
765 return fPackageMoreIcon;
766 else
767 return fPackageLessIcon;
771 float
772 SuperItem::GetIconSize(bool showMoreDetails)
774 if (showMoreDetails)
775 return fPackageMoreIcon->Bounds().Height();
776 else
777 return fPackageLessIcon->Bounds().Height();
781 void
782 SuperItem::SetDetailLevel(bool showMoreDetails)
784 fShowMoreDetails = showMoreDetails;
788 void
789 SuperItem::SetItemCount(int32 count)
791 fItemCount = count;
792 fItemText = fLabel;
793 fItemText.Append(" (");
794 fItemText << fItemCount;
795 fItemText.Append(")");
799 float
800 SuperItem::ZoomWidth(BView *owner)
802 owner->PushState();
803 owner->SetFont(&fBoldFont);
804 float width = owner->StringWidth(fItemText.String());
805 owner->PopState();
806 return width;
810 BBitmap*
811 SuperItem::_GetPackageIcon(float listItemHeight)
813 int32 iconSize = int(listItemHeight * .8);
814 status_t result = B_ERROR;
815 BRect iconRect(0, 0, iconSize - 1, iconSize - 1);
816 BBitmap* packageIcon = new BBitmap(iconRect, 0, B_RGBA32);
817 BMimeType nodeType;
818 nodeType.SetTo("application/x-vnd.haiku-package");
819 result = nodeType.GetIcon(packageIcon, icon_size(iconSize));
820 // Get super type icon
821 if (result != B_OK) {
822 BMimeType superType;
823 if (nodeType.GetSupertype(&superType) == B_OK)
824 result = superType.GetIcon(packageIcon, icon_size(iconSize));
826 if (result != B_OK) {
827 delete packageIcon;
828 return NULL;
830 return packageIcon;
834 PackageItem::PackageItem(const char* name, const char* simple_version,
835 const char* detailed_version, const char* repository, const char* summary,
836 const char* file_name, SuperItem* super)
838 BListItem(),
839 fName(name),
840 fSimpleVersion(simple_version),
841 fDetailedVersion(detailed_version),
842 fRepository(repository),
843 fSummary(summary),
844 fSmallFont(be_plain_font),
845 fSuperItem(super),
846 fFileName(file_name),
847 fDownloadProgress(0),
848 fDrawBarFlag(false),
849 fMoreDetailsWidth(0),
850 fLessDetailsWidth(0)
852 fLabelOffset = be_control_look->DefaultLabelSpacing();
853 fSmallFont.SetSize(be_plain_font->Size() - 2);
854 fSmallFont.GetHeight(&fSmallFontHeight);
855 fSmallTotalHeight = fSmallFontHeight.ascent + fSmallFontHeight.descent
856 + fSmallFontHeight.leading;
860 void
861 PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete)
863 owner->PushState();
865 float width = owner->Frame().Width();
866 float nameWidth = width / 2.0;
867 float offsetWidth = 0;
868 bool showMoreDetails = fSuperItem->GetDetailLevel();
870 BBitmap* icon = fSuperItem->GetIcon(showMoreDetails);
871 if (icon != NULL && icon->IsValid()) {
872 float iconSize = icon->Bounds().Height();
873 float offsetMarginHeight = floor((Height() - iconSize) / 2);
874 owner->SetDrawingMode(B_OP_ALPHA);
875 BPoint location = BPoint(item_rect.left,
876 item_rect.top + offsetMarginHeight);
877 owner->DrawBitmap(icon, location);
878 owner->SetDrawingMode(B_OP_COPY);
879 offsetWidth = iconSize + fLabelOffset;
881 if (fDrawBarFlag)
882 _DrawBar(location, owner, icon_size(iconSize));
885 owner->SetFont(be_plain_font);
886 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
888 // Package name
889 BString name(fName);
890 owner->TruncateString(&name, B_TRUNCATE_END, nameWidth);
891 BPoint cursor(item_rect.left + offsetWidth,
892 item_rect.bottom - fSmallTotalHeight - fSmallFontHeight.descent - 2);
893 if (showMoreDetails)
894 cursor.y -= fSmallTotalHeight + 1;
895 owner->DrawString(name.String(), cursor);
896 cursor.x += owner->StringWidth(name.String()) + fLabelOffset;
898 // Change font and color
899 owner->SetFont(&fSmallFont);
900 owner->SetHighColor(tint_color(ui_color(B_LIST_ITEM_TEXT_COLOR), 0.7));
902 // Simple version or repository
903 BString versionOrRepo;
904 if (showMoreDetails)
905 versionOrRepo.SetTo(fRepository);
906 else
907 versionOrRepo.SetTo(fSimpleVersion);
908 owner->TruncateString(&versionOrRepo, B_TRUNCATE_END, width - cursor.x);
909 owner->DrawString(versionOrRepo.String(), cursor);
911 // Summary
912 BString summary(fSummary);
913 cursor.x = item_rect.left + offsetWidth;
914 cursor.y += fSmallTotalHeight;
915 owner->TruncateString(&summary, B_TRUNCATE_END, width - cursor.x);
916 owner->DrawString(summary.String(), cursor);
918 // Detailed version
919 if (showMoreDetails) {
920 BString version(fDetailedVersion);
921 cursor.y += fSmallTotalHeight;
922 owner->TruncateString(&version, B_TRUNCATE_END, width - cursor.x);
923 owner->DrawString(version.String(), cursor);
926 owner->PopState();
930 // Modified slightly from Tracker's BPose::DrawBar
931 void
932 PackageItem::_DrawBar(BPoint where, BView* view, icon_size which)
934 int32 yOffset;
935 int32 size = which - 1;
936 int32 barWidth = (int32)(7.0f / 32.0f * (float)which);
937 if (barWidth < 4) {
938 barWidth = 4;
939 yOffset = 0;
940 } else
941 yOffset = 2;
942 int32 barHeight = size - 3 - 2 * yOffset;
945 // the black shadowed line
946 view->SetHighColor(32, 32, 32, 92);
947 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset));
948 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset));
949 view->StrokeLine(BPoint(where.x + size - barWidth + 1,
950 where.y + size - yOffset));
952 view->SetDrawingMode(B_OP_ALPHA);
954 // the gray frame
955 view->SetHighColor(76, 76, 76, 192);
956 BRect rect(where.x + size - barWidth,where.y + yOffset,
957 where.x + size - 1,where.y + size - 1 - yOffset);
958 view->StrokeRect(rect);
960 // calculate bar height
961 int32 barPos = barHeight - int32(barHeight * fDownloadProgress / 100.0);
962 if (barPos < 0)
963 barPos = 0;
964 else if (barPos > barHeight)
965 barPos = barHeight;
967 // the free space bar
968 view->SetHighColor(255, 255, 255, 192);
970 rect.InsetBy(1,1);
971 BRect bar(rect);
972 bar.bottom = bar.top + barPos - 1;
973 if (barPos > 0)
974 view->FillRect(bar);
976 // the used space bar
977 bar.top = bar.bottom + 1;
978 bar.bottom = rect.bottom;
979 view->SetHighColor(0, 203, 0, 192);
980 view->FillRect(bar);
984 void
985 PackageItem::Update(BView *owner, const BFont *font)
987 BListItem::Update(owner, font);
988 SetHeight(fSuperItem->GetPackageItemHeight());
992 void
993 PackageItem::CalculateZoomWidths(BView *owner)
995 owner->PushState();
997 // More details
998 float offsetWidth = 2 * be_control_look->DefaultItemSpacing()
999 + be_plain_font->Size()
1000 + fSuperItem->GetIconSize(true) + fLabelOffset;
1001 // Name and repo
1002 owner->SetFont(be_plain_font);
1003 float stringWidth = owner->StringWidth(fName.String());
1004 owner->SetFont(&fSmallFont);
1005 stringWidth += fLabelOffset + owner->StringWidth(fRepository.String());
1006 // Summary
1007 float summaryWidth = owner->StringWidth(fSummary.String());
1008 if (summaryWidth > stringWidth)
1009 stringWidth = summaryWidth;
1010 // Version
1011 float versionWidth = owner->StringWidth(fDetailedVersion.String());
1012 if (versionWidth > stringWidth)
1013 stringWidth = versionWidth;
1014 fMoreDetailsWidth = offsetWidth + stringWidth;
1016 // Less details
1017 offsetWidth = 2 * be_control_look->DefaultItemSpacing()
1018 + be_plain_font->Size()
1019 + fSuperItem->GetIconSize(false) + fLabelOffset;
1020 // Name and version
1021 owner->SetFont(be_plain_font);
1022 stringWidth = owner->StringWidth(fName.String());
1023 owner->SetFont(&fSmallFont);
1024 stringWidth += fLabelOffset + owner->StringWidth(fSimpleVersion.String());
1025 // Summary
1026 if (summaryWidth > stringWidth)
1027 stringWidth = summaryWidth;
1028 fLessDetailsWidth = offsetWidth + stringWidth;
1030 owner->PopState();
1035 PackageItem::NameCompare(PackageItem* item)
1037 // sort by package name
1038 return fName.ICompare(item->fName);
1042 void
1043 PackageItem::SetDownloadProgress(float percent)
1045 fDownloadProgress = percent;
1050 SortPackageItems(const BListItem* item1, const BListItem* item2)
1052 PackageItem* first = (PackageItem*)item1;
1053 PackageItem* second = (PackageItem*)item2;
1054 return first->NameCompare(second);
1058 PackageListView::PackageListView()
1060 BOutlineListView("Package list"),
1061 fSuperUpdateItem(NULL),
1062 fSuperInstallItem(NULL),
1063 fSuperUninstallItem(NULL),
1064 fShowMoreDetails(false),
1065 fLastProgressItem(NULL),
1066 fLastProgressValue(-1)
1068 SetExplicitMinSize(BSize(B_SIZE_UNSET, 40));
1069 SetExplicitPreferredSize(BSize(B_SIZE_UNSET, 400));
1073 void
1074 PackageListView::FrameResized(float newWidth, float newHeight)
1076 BOutlineListView::FrameResized(newWidth, newHeight);
1077 Invalidate();
1081 void
1082 PackageListView::ExpandOrCollapse(BListItem *superItem, bool expand)
1084 BOutlineListView::ExpandOrCollapse(superItem, expand);
1085 Window()->PostMessage(kMsgSetZoomLimits);
1089 void
1090 PackageListView::AddPackage(uint32 install_type, const char* name,
1091 const char* cur_ver, const char* new_ver, const char* summary,
1092 const char* repository, const char* file_name)
1094 SuperItem* super;
1095 BString simpleVersion;
1096 BString detailedVersion("");
1097 BString repositoryText(B_TRANSLATE_COMMENT("from repository",
1098 "List item text"));
1099 repositoryText.Append(" ").Append(repository);
1101 switch (install_type) {
1102 case PACKAGE_UPDATE:
1104 if (fSuperUpdateItem == NULL) {
1105 fSuperUpdateItem = new SuperItem(B_TRANSLATE_COMMENT(
1106 "Packages to be updated", "List super item label"));
1107 AddItem(fSuperUpdateItem);
1109 super = fSuperUpdateItem;
1111 simpleVersion.SetTo(new_ver);
1112 detailedVersion.Append(B_TRANSLATE_COMMENT("Updating version",
1113 "List item text"))
1114 .Append(" ").Append(cur_ver)
1115 .Append(" ").Append(B_TRANSLATE_COMMENT("to",
1116 "List item text"))
1117 .Append(" ").Append(new_ver);
1118 break;
1121 case PACKAGE_INSTALL:
1123 if (fSuperInstallItem == NULL) {
1124 fSuperInstallItem = new SuperItem(B_TRANSLATE_COMMENT(
1125 "New packages to be installed", "List super item label"));
1126 AddItem(fSuperInstallItem);
1128 super = fSuperInstallItem;
1130 simpleVersion.SetTo(new_ver);
1131 detailedVersion.Append(B_TRANSLATE_COMMENT("Installing version",
1132 "List item text"))
1133 .Append(" ").Append(new_ver);
1134 break;
1137 case PACKAGE_UNINSTALL:
1139 if (fSuperUninstallItem == NULL) {
1140 fSuperUninstallItem = new SuperItem(B_TRANSLATE_COMMENT(
1141 "Packages to be uninstalled", "List super item label"));
1142 AddItem(fSuperUninstallItem);
1144 super = fSuperUninstallItem;
1146 simpleVersion.SetTo("");
1147 detailedVersion.Append(B_TRANSLATE_COMMENT("Uninstalling version",
1148 "List item text"))
1149 .Append(" ").Append(cur_ver);
1150 break;
1153 default:
1154 return;
1157 PackageItem* item = new PackageItem(name, simpleVersion.String(),
1158 detailedVersion.String(), repositoryText.String(), summary, file_name,
1159 super);
1160 AddUnder(item, super);
1161 super->SetItemCount(CountItemsUnder(super, true));
1162 item->CalculateZoomWidths(this);
1166 void
1167 PackageListView::UpdatePackageProgress(const char* packageName, float percent)
1169 // Update only every 1 percent change
1170 int16 wholePercent = int16(percent);
1171 if (wholePercent == fLastProgressValue)
1172 return;
1173 fLastProgressValue = wholePercent;
1175 // A new package started downloading, find the PackageItem by name
1176 if (percent == 0) {
1177 fLastProgressItem = NULL;
1178 int32 count = FullListCountItems();
1179 for (int32 i = 0; i < count; i++) {
1180 PackageItem* item = dynamic_cast<PackageItem*>(FullListItemAt(i));
1181 if (item != NULL && strcmp(item->FileName(), packageName) == 0) {
1182 fLastProgressItem = item;
1183 fLastProgressItem->ShowProgressBar();
1184 break;
1189 if (fLastProgressItem != NULL) {
1190 fLastProgressItem->SetDownloadProgress(percent);
1191 Invalidate();
1196 void
1197 PackageListView::SortItems()
1199 if (fSuperUpdateItem != NULL)
1200 SortItemsUnder(fSuperUpdateItem, true, SortPackageItems);
1201 if (fSuperInstallItem != NULL)
1202 SortItemsUnder(fSuperInstallItem, true, SortPackageItems);
1203 if (fSuperUninstallItem != NULL)
1204 SortItemsUnder(fSuperUninstallItem, true, SortPackageItems);
1208 float
1209 PackageListView::ItemHeight()
1211 if (fSuperUpdateItem != NULL)
1212 return fSuperUpdateItem->GetPackageItemHeight();
1213 if (fSuperInstallItem != NULL)
1214 return fSuperInstallItem->GetPackageItemHeight();
1215 if (fSuperUninstallItem != NULL)
1216 return fSuperUninstallItem->GetPackageItemHeight();
1217 return 0;
1221 void
1222 PackageListView::SetMoreDetails(bool showMore)
1224 if (showMore == fShowMoreDetails)
1225 return;
1226 fShowMoreDetails = showMore;
1227 _SetItemHeights();
1228 InvalidateLayout();
1229 ResizeToPreferred();
1233 BPoint
1234 PackageListView::ZoomPoint()
1236 BPoint zoomPoint(0, 0);
1237 int32 count = CountItems();
1238 for (int32 i = 0; i < count; i++)
1240 BListItem* item = ItemAt(i);
1241 float itemWidth = 0;
1242 if (item->OutlineLevel() == 0) {
1243 SuperItem* sItem = dynamic_cast<SuperItem*>(item);
1244 itemWidth = sItem->ZoomWidth(this);
1245 } else {
1246 PackageItem* pItem = dynamic_cast<PackageItem*>(item);
1247 itemWidth = fShowMoreDetails ? pItem->MoreDetailsWidth()
1248 : pItem->LessDetailsWidth();
1250 if (itemWidth > zoomPoint.x)
1251 zoomPoint.x = itemWidth;
1253 if (count > 0)
1254 zoomPoint.y = ItemFrame(count - 1).bottom;
1256 return zoomPoint;
1260 void
1261 PackageListView::_SetItemHeights()
1263 int32 itemCount = 0;
1264 float itemHeight = 0;
1265 BListItem* item = NULL;
1266 if (fSuperUpdateItem != NULL) {
1267 fSuperUpdateItem->SetDetailLevel(fShowMoreDetails);
1268 itemHeight = fSuperUpdateItem->GetPackageItemHeight();
1269 itemCount = CountItemsUnder(fSuperUpdateItem, true);
1270 for (int32 i = 0; i < itemCount; i++) {
1271 item = ItemUnderAt(fSuperUpdateItem, true, i);
1272 item->SetHeight(itemHeight);
1275 if (fSuperInstallItem != NULL) {
1276 fSuperInstallItem->SetDetailLevel(fShowMoreDetails);
1277 itemHeight = fSuperInstallItem->GetPackageItemHeight();
1278 itemCount = CountItemsUnder(fSuperInstallItem, true);
1279 for (int32 i = 0; i < itemCount; i++) {
1280 item = ItemUnderAt(fSuperInstallItem, true, i);
1281 item->SetHeight(itemHeight);
1285 if (fSuperUninstallItem != NULL) {
1286 fSuperUninstallItem->SetDetailLevel(fShowMoreDetails);
1287 itemHeight = fSuperUninstallItem->GetPackageItemHeight();
1288 itemCount = CountItemsUnder(fSuperUninstallItem, true);
1289 for (int32 i = 0; i < itemCount; i++) {
1290 item = ItemUnderAt(fSuperUninstallItem, true, i);
1291 item->SetHeight(itemHeight);