repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / haikudepot / ui / PackageContentsView.cpp
blob17f03843259e322a1f4dabc3f2c8429ded02d064
1 /*
2 * Copyright 2015, TigerKid001.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "PackageContentsView.h"
8 #include <algorithm>
9 #include <stdio.h>
11 #include <Autolock.h>
12 #include <Catalog.h>
13 #include <FindDirectory.h>
14 #include <MessageFormat.h>
15 #include <LayoutBuilder.h>
16 #include <LayoutUtils.h>
17 #include <OutlineListView.h>
18 #include <Path.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
43 // window look.
44 class CustomScrollView : public BScrollView {
45 public:
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();
58 if (target != NULL) {
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 {
80 public:
81 PackageEntryItem(const BPackageEntry* entry, const BString& path)
83 BStringItem(entry->Name()),
84 fPath(path)
86 if (fPath.Length() > 0)
87 fPath.Append("/");
88 fPath.Append(entry->Name());
91 inline const BString& EntryPath() const
93 return fPath;
96 private:
97 BString fPath;
101 // #pragma mark - PackageContentOutliner
104 class PackageContentOutliner : public BPackageContentHandler {
105 public:
106 PackageContentOutliner(BOutlineListView* listView,
107 const PackageInfo* packageInfo,
108 BLocker& packageLock, PackageInfoRef& packageInfoRef)
110 fListView(listView),
111 fLastParentEntry(NULL),
112 fLastParentItem(NULL),
113 fLastEntry(NULL),
114 fLastItem(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",
126 // entry->Name());
128 if (fListView->LockLooperWithTimeout(1000000) != B_OK)
129 return B_ERROR;
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();
135 return B_ERROR;
138 BString path;
139 const BPackageEntry* parent = entry->Parent();
140 while (parent != NULL) {
141 if (path.Length() > 0)
142 path.Prepend("/");
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);
162 } else {
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)
171 continue;
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);
177 foundParent = true;
178 break;
181 if (!foundParent) {
182 // NOTE: Should not happen. Just add this entry at the
183 // root level.
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;
192 fLastEntry = entry;
193 fLastItem = item;
195 fListView->UnlockLooper();
197 return B_OK;
200 virtual status_t HandleEntryAttribute(BPackageEntry* entry,
201 BPackageEntryAttribute* attribute)
203 return B_OK;
206 virtual status_t HandleEntryDone(BPackageEntry* entry)
208 return B_OK;
211 virtual status_t HandlePackageAttribute(
212 const BPackageInfoAttributeValue& value)
214 return B_OK;
217 virtual void HandleErrorOccurred()
221 private:
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",
249 fContentListView);
251 BLayoutBuilder::Group<>(this)
252 .Add(scrollView, 1.0f)
253 .SetInsets(0.0f, -1.0f, -1.0f, -1.0f)
256 _InitContentPopulator();
260 PackageContentsView::~PackageContentsView()
262 Clear();
264 delete_sem(fContentPopulatorSem);
265 if (fContentPopulator >= 0)
266 wait_for_thread(fContentPopulator, NULL);
270 void
271 PackageContentsView::AttachedToWindow()
273 BView::AttachedToWindow();
277 void
278 PackageContentsView::AllAttached()
280 BView::AllAttached();
284 void
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)) {
293 return;
296 // printf("PackageContentsView::SetPackage(%s)\n",
297 // package.Get() != NULL ? package->Name().String() : "NULL");
299 Clear();
302 BAutolock lock(&fPackageLock);
303 fPackage = package;
304 fLastPackageState = package.Get() != NULL ? package->State() : NONE;
306 release_sem_etc(fContentPopulatorSem, 1, 0);
310 void
311 PackageContentsView::Clear()
314 BAutolock lock(&fPackageLock);
315 fPackage.Unset();
318 fContentListView->MakeEmpty();
322 // #pragma mark - private
325 void
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);
334 } else
335 fContentPopulator = -1;
339 /*static*/ int32
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();
363 return 0;
367 bool
368 PackageContentsView::_PopuplatePackageContens(const PackageInfo& package)
370 BPath packagePath;
372 // Obtain path to the package file
373 if (package.IsLocalFile()) {
374 BString pathString = package.LocalFilePath();
375 packagePath.SetTo(pathString.String());
376 } else {
377 int32 installLocation = _InstallLocation(package);
378 if (installLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
379 if (find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &packagePath)
380 != B_OK) {
381 return false;
383 } else if (installLocation == B_PACKAGE_INSTALLATION_LOCATION_HOME) {
384 if (find_directory(B_USER_PACKAGES_DIRECTORY, &packagePath)
385 != B_OK) {
386 return false;
388 } else {
389 printf("PackageContentsView::_PopuplatePackageContens(): "
390 "unknown install location");
391 return false;
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));
406 return false;
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.
420 return true;
424 int32
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;