2 * Copyright 2015, TigerKid001.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "PackageContentsView.h"
13 #include <FindDirectory.h>
14 #include <MessageFormat.h>
15 #include <LayoutBuilder.h>
16 #include <LayoutUtils.h>
17 #include <OutlineListView.h>
19 #include <ScrollBar.h>
20 #include <ScrollView.h>
21 #include <StringItem.h>
23 #include <package/PackageDefs.h>
24 #include <package/hpkg/NoErrorOutput.h>
25 #include <package/hpkg/PackageContentHandler.h>
26 #include <package/hpkg/PackageEntry.h>
27 #include <package/hpkg/PackageReader.h>
29 using namespace BPackageKit
;
31 using BPackageKit::BHPKG::BNoErrorOutput
;
32 using BPackageKit::BHPKG::BPackageContentHandler
;
33 using BPackageKit::BHPKG::BPackageEntry
;
34 using BPackageKit::BHPKG::BPackageEntryAttribute
;
35 using BPackageKit::BHPKG::BPackageInfoAttributeValue
;
36 using BPackageKit::BHPKG::BPackageReader
;
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "PackageContentsView"
42 //! Layouts the scrollbar so it looks nice with no border and the document
44 class CustomScrollView
: public BScrollView
{
46 CustomScrollView(const char* name
, BView
* target
)
48 BScrollView(name
, target
, 0, false, true, B_NO_BORDER
)
52 virtual void DoLayout()
54 BRect innerFrame
= Bounds();
55 innerFrame
.right
-= B_V_SCROLL_BAR_WIDTH
+ 1;
57 BView
* target
= Target();
59 Target()->MoveTo(innerFrame
.left
, innerFrame
.top
);
60 Target()->ResizeTo(innerFrame
.Width(), innerFrame
.Height());
63 BScrollBar
* scrollBar
= ScrollBar(B_VERTICAL
);
64 if (scrollBar
!= NULL
) {
65 BRect rect
= innerFrame
;
66 rect
.left
= rect
.right
+ 1;
67 rect
.right
= rect
.left
+ B_V_SCROLL_BAR_WIDTH
;
68 rect
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
70 scrollBar
->MoveTo(rect
.left
, rect
.top
);
71 scrollBar
->ResizeTo(rect
.Width(), rect
.Height());
76 // #pragma mark - PackageEntryItem
79 class PackageEntryItem
: public BStringItem
{
81 PackageEntryItem(const BPackageEntry
* entry
, const BString
& path
)
83 BStringItem(entry
->Name()),
86 if (fPath
.Length() > 0)
88 fPath
.Append(entry
->Name());
91 inline const BString
& EntryPath() const
101 // #pragma mark - PackageContentOutliner
104 class PackageContentOutliner
: public BPackageContentHandler
{
106 PackageContentOutliner(BOutlineListView
* listView
,
107 const PackageInfo
* packageInfo
,
108 BLocker
& packageLock
, PackageInfoRef
& packageInfoRef
)
111 fLastParentEntry(NULL
),
112 fLastParentItem(NULL
),
116 fPackageInfoToPopulate(packageInfo
),
117 fPackageLock(packageLock
),
118 fPackageInfoRef(packageInfoRef
)
122 virtual status_t
HandleEntry(BPackageEntry
* entry
)
124 // printf("HandleEntry(%s/%s)\n",
125 // entry->Parent() != NULL ? entry->Parent()->Name() : "NULL",
128 if (fListView
->LockLooperWithTimeout(1000000) != B_OK
)
131 // Check if we are still supposed to popuplate the list
132 if (fPackageInfoRef
.Get() != fPackageInfoToPopulate
) {
133 // printf("stopping package content population\n");
134 fListView
->UnlockLooper();
139 const BPackageEntry
* parent
= entry
->Parent();
140 while (parent
!= NULL
) {
141 if (path
.Length() > 0)
143 path
.Prepend(parent
->Name());
144 parent
= parent
->Parent();
147 PackageEntryItem
* item
= new PackageEntryItem(entry
, path
);
149 if (entry
->Parent() == NULL
) {
150 // printf(" adding root entry\n");
151 fListView
->AddItem(item
);
152 fLastParentEntry
= NULL
;
153 fLastParentItem
= NULL
;
154 } else if (entry
->Parent() == fLastEntry
) {
155 // printf(" adding to last entry %s\n", fLastEntry->Name());
156 fListView
->AddUnder(item
, fLastItem
);
157 fLastParentEntry
= fLastEntry
;
158 fLastParentItem
= fLastItem
;
159 } else if (entry
->Parent() == fLastParentEntry
) {
160 // printf(" adding to last parent %s\n", fLastParentEntry->Name());
161 fListView
->AddUnder(item
, fLastParentItem
);
163 // Not the last parent entry, need to search for the parent
164 // among the already added list items.
165 bool foundParent
= false;
166 for (int32 i
= 0; i
< fListView
->FullListCountItems(); i
++) {
167 PackageEntryItem
* listItem
168 = dynamic_cast<PackageEntryItem
*>(
169 fListView
->FullListItemAt(i
));
170 if (listItem
== NULL
)
172 if (listItem
->EntryPath() == path
) {
173 fLastParentEntry
= entry
->Parent();
174 fLastParentItem
= listItem
;
175 // printf(" found parent %s\n", listItem->Text());
176 fListView
->AddUnder(item
, listItem
);
182 // NOTE: Should not happen. Just add this entry at the
184 // printf("Did not find parent entry for %s (%s)!\n",
185 // entry->Name(), entry->Parent()->Name());
186 fListView
->AddItem(item
);
187 fLastParentEntry
= NULL
;
188 fLastParentItem
= NULL
;
195 fListView
->UnlockLooper();
200 virtual status_t
HandleEntryAttribute(BPackageEntry
* entry
,
201 BPackageEntryAttribute
* attribute
)
206 virtual status_t
HandleEntryDone(BPackageEntry
* entry
)
211 virtual status_t
HandlePackageAttribute(
212 const BPackageInfoAttributeValue
& value
)
217 virtual void HandleErrorOccurred()
222 BOutlineListView
* fListView
;
224 const BPackageEntry
* fLastParentEntry
;
225 PackageEntryItem
* fLastParentItem
;
227 const BPackageEntry
* fLastEntry
;
228 PackageEntryItem
* fLastItem
;
230 const PackageInfo
* fPackageInfoToPopulate
;
231 BLocker
& fPackageLock
;
232 PackageInfoRef
& fPackageInfoRef
;
236 // #pragma mark - PackageContentView
239 PackageContentsView::PackageContentsView(const char* name
)
241 BView("package_contents_view", B_WILL_DRAW
),
242 fPackageLock("package contents populator lock"),
243 fLastPackageState(NONE
)
245 fContentListView
= new BOutlineListView("content list view",
246 B_SINGLE_SELECTION_LIST
);
248 BScrollView
* scrollView
= new CustomScrollView("contents scroll view",
251 BLayoutBuilder::Group
<>(this)
252 .Add(scrollView
, 1.0f
)
253 .SetInsets(0.0f
, -1.0f
, -1.0f
, -1.0f
)
256 _InitContentPopulator();
260 PackageContentsView::~PackageContentsView()
264 delete_sem(fContentPopulatorSem
);
265 if (fContentPopulator
>= 0)
266 wait_for_thread(fContentPopulator
, NULL
);
271 PackageContentsView::AttachedToWindow()
273 BView::AttachedToWindow();
278 PackageContentsView::AllAttached()
280 BView::AllAttached();
285 PackageContentsView::SetPackage(const PackageInfoRef
& package
)
287 // When getting a ref to the same package, don't return when the
288 // package state has changed, since in that case, we may now be able
289 // to read contents where we previously could not. (For example, the
290 // package has been installed.)
291 if (fPackage
== package
292 && (package
.Get() == NULL
|| package
->State() == fLastPackageState
)) {
296 // printf("PackageContentsView::SetPackage(%s)\n",
297 // package.Get() != NULL ? package->Name().String() : "NULL");
302 BAutolock
lock(&fPackageLock
);
304 fLastPackageState
= package
.Get() != NULL
? package
->State() : NONE
;
306 release_sem_etc(fContentPopulatorSem
, 1, 0);
311 PackageContentsView::Clear()
314 BAutolock
lock(&fPackageLock
);
318 fContentListView
->MakeEmpty();
322 // #pragma mark - private
326 PackageContentsView::_InitContentPopulator()
328 fContentPopulatorSem
= create_sem(0, "PopulatePackageContents");
329 if (fContentPopulatorSem
>= 0) {
330 fContentPopulator
= spawn_thread(&_ContentPopulatorThread
,
331 "Package Contents Populator", B_NORMAL_PRIORITY
, this);
332 if (fContentPopulator
>= 0)
333 resume_thread(fContentPopulator
);
335 fContentPopulator
= -1;
340 PackageContentsView::_ContentPopulatorThread(void* arg
)
342 PackageContentsView
* view
= reinterpret_cast<PackageContentsView
*>(arg
);
344 while (acquire_sem(view
->fContentPopulatorSem
) == B_OK
) {
345 PackageInfoRef package
;
347 BAutolock
lock(&view
->fPackageLock
);
348 package
= view
->fPackage
;
351 if (package
.Get() != NULL
) {
352 if (!view
->_PopuplatePackageContens(*package
.Get())) {
353 if (view
->LockLooperWithTimeout(1000000) == B_OK
) {
354 view
->fContentListView
->AddItem(
355 new BStringItem(B_TRANSLATE("<Package contents not "
356 "available for remote packages>")));
357 view
->UnlockLooper();
368 PackageContentsView::_PopuplatePackageContens(const PackageInfo
& package
)
372 // Obtain path to the package file
373 if (package
.IsLocalFile()) {
374 BString pathString
= package
.LocalFilePath();
375 packagePath
.SetTo(pathString
.String());
377 int32 installLocation
= _InstallLocation(package
);
378 if (installLocation
== B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
) {
379 if (find_directory(B_SYSTEM_PACKAGES_DIRECTORY
, &packagePath
)
383 } else if (installLocation
== B_PACKAGE_INSTALLATION_LOCATION_HOME
) {
384 if (find_directory(B_USER_PACKAGES_DIRECTORY
, &packagePath
)
389 printf("PackageContentsView::_PopuplatePackageContens(): "
390 "unknown install location");
394 packagePath
.Append(package
.FileName());
397 // Setup a BPackageReader
398 BNoErrorOutput errorOutput
;
399 BPackageReader
reader(&errorOutput
);
401 status_t status
= reader
.Init(packagePath
.Path());
402 if (status
!= B_OK
) {
403 printf("PackageContentsView::_PopuplatePackageContens(): "
404 "failed to init BPackageReader(%s): %s\n",
405 packagePath
.Path(), strerror(status
));
409 // Scan package contents and populate list
410 PackageContentOutliner
contentHandler(fContentListView
, &package
,
411 fPackageLock
, fPackage
);
412 status
= reader
.ParseContent(&contentHandler
);
413 if (status
!= B_OK
) {
414 printf("PackageContentsView::_PopuplatePackageContens(): "
415 "failed parse package contents: %s\n", strerror(status
));
416 // NOTE: Do not return false, since it taken to mean this
417 // is a remote package, but is it not, we simply want to stop
418 // populating the contents early.
425 PackageContentsView::_InstallLocation(const PackageInfo
& package
) const
427 const PackageInstallationLocationSet
& locations
428 = package
.InstallationLocations();
430 // If the package is already installed, return its first installed location
431 if (locations
.size() != 0)
432 return *locations
.begin();
434 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
;