xref: /haiku/src/apps/haikudepot/ui/MainWindow.cpp (revision 16ad15142c48ee36cd6a807a24efc99c88d4310d)
1 /*
2  * Copyright 2015, Axel Dörfler, <axeld@pinc-software.de>.
3  * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
4  * Copyright 2013, Rene Gollent, rene@gollent.com.
5  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
6  * Copyright 2016-2020, Andrew Lindesay <apl@lindesay.co.nz>.
7  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
8  * All rights reserved. Distributed under the terms of the MIT License.
9  */
10 
11 
12 #include "MainWindow.h"
13 
14 #include <map>
15 #include <vector>
16 
17 #include <stdio.h>
18 #include <Alert.h>
19 #include <Autolock.h>
20 #include <Application.h>
21 #include <Button.h>
22 #include <Catalog.h>
23 #include <CardLayout.h>
24 #include <LayoutBuilder.h>
25 #include <MenuBar.h>
26 #include <MenuItem.h>
27 #include <Messenger.h>
28 #include <Roster.h>
29 #include <Screen.h>
30 #include <ScrollView.h>
31 #include <StringList.h>
32 #include <StringView.h>
33 #include <TabView.h>
34 
35 #include "AppUtils.h"
36 #include "AutoDeleter.h"
37 #include "AutoLocker.h"
38 #include "DecisionProvider.h"
39 #include "FeaturedPackagesView.h"
40 #include "FilterView.h"
41 #include "Logger.h"
42 #include "PackageInfoView.h"
43 #include "PackageListView.h"
44 #include "PackageManager.h"
45 #include "ProcessCoordinator.h"
46 #include "ProcessCoordinatorFactory.h"
47 #include "RatePackageWindow.h"
48 #include "support.h"
49 #include "ScreenshotWindow.h"
50 #include "ToLatestUserUsageConditionsWindow.h"
51 #include "UserLoginWindow.h"
52 #include "UserUsageConditionsWindow.h"
53 #include "WorkStatusView.h"
54 
55 
56 #undef B_TRANSLATION_CONTEXT
57 #define B_TRANSLATION_CONTEXT "MainWindow"
58 
59 
60 enum {
61 	MSG_REFRESH_REPOS						= 'mrrp',
62 	MSG_MANAGE_REPOS						= 'mmrp',
63 	MSG_SOFTWARE_UPDATER					= 'mswu',
64 	MSG_LOG_IN								= 'lgin',
65 	MSG_AUTHORIZATION_CHANGED				= 'athc',
66 	MSG_CATEGORIES_LIST_CHANGED				= 'clic',
67 	MSG_PACKAGE_CHANGED						= 'pchd',
68 	MSG_WORK_STATUS_CHANGE					= 'wsch',
69 	MSG_WORK_STATUS_CLEAR					= 'wscl',
70 
71 	MSG_CHANGE_PACKAGE_LIST_VIEW_MODE		= 'cplm',
72 	MSG_SHOW_AVAILABLE_PACKAGES				= 'savl',
73 	MSG_SHOW_INSTALLED_PACKAGES				= 'sins',
74 	MSG_SHOW_SOURCE_PACKAGES				= 'ssrc',
75 	MSG_SHOW_DEVELOP_PACKAGES				= 'sdvl'
76 };
77 
78 #define KEY_ERROR_STATUS				"errorStatus"
79 #define KEY_PACKAGE_LIST_VIEW_MODE		"packageListViewMode"
80 
81 #define TAB_PROMINENT_PACKAGES	0
82 #define TAB_ALL_PACKAGES		1
83 
84 using namespace BPackageKit;
85 using namespace BPackageKit::BManager::BPrivate;
86 
87 
88 typedef std::map<BString, PackageInfoRef> PackageInfoMap;
89 
90 
91 struct RefreshWorkerParameters {
92 	MainWindow* window;
93 	bool forceRefresh;
94 
95 	RefreshWorkerParameters(MainWindow* window, bool forceRefresh)
96 		:
97 		window(window),
98 		forceRefresh(forceRefresh)
99 	{
100 	}
101 };
102 
103 
104 class MainWindowModelListener : public ModelListener {
105 public:
106 	MainWindowModelListener(const BMessenger& messenger)
107 		:
108 		fMessenger(messenger)
109 	{
110 	}
111 
112 	virtual void AuthorizationChanged()
113 	{
114 		if (fMessenger.IsValid())
115 			fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
116 	}
117 
118 	virtual void CategoryListChanged()
119 	{
120 		if (fMessenger.IsValid())
121 			fMessenger.SendMessage(MSG_CATEGORIES_LIST_CHANGED);
122 	}
123 
124 private:
125 	BMessenger	fMessenger;
126 };
127 
128 
129 MainWindow::MainWindow(const BMessage& settings)
130 	:
131 	BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
132 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
133 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
134 	fScreenshotWindow(NULL),
135 	fUserMenu(NULL),
136 	fLogInItem(NULL),
137 	fLogOutItem(NULL),
138 	fUsersUserUsageConditionsMenuItem(NULL),
139 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
140 	fCoordinator(NULL),
141 	fSinglePackageMode(false)
142 {
143 	if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
144 		debugger("unable to create the process coordinator semaphore");
145 
146 	BMenuBar* menuBar = new BMenuBar("Main Menu");
147 	_BuildMenu(menuBar);
148 
149 	BMenuBar* userMenuBar = new BMenuBar("User Menu");
150 	_BuildUserMenu(userMenuBar);
151 	set_small_font(userMenuBar);
152 	userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET,
153 		menuBar->MaxSize().height));
154 
155 	fFilterView = new FilterView();
156 	fFeaturedPackagesView = new FeaturedPackagesView();
157 	fPackageListView = new PackageListView(fModel.Lock());
158 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
159 
160 	fSplitView = new BSplitView(B_VERTICAL, 5.0f);
161 
162 	fWorkStatusView = new WorkStatusView("work status");
163 	fPackageListView->AttachWorkStatusView(fWorkStatusView);
164 
165 	fListTabs = new TabView(BMessenger(this),
166 		BMessage(MSG_CHANGE_PACKAGE_LIST_VIEW_MODE), "list tabs");
167 	fListTabs->AddTab(fFeaturedPackagesView);
168 	fListTabs->AddTab(fPackageListView);
169 
170 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
171 		.AddGroup(B_HORIZONTAL, 0.0f)
172 			.Add(menuBar, 1.0f)
173 			.Add(userMenuBar, 0.0f)
174 		.End()
175 		.Add(fFilterView)
176 		.AddSplit(fSplitView)
177 			.AddGroup(B_VERTICAL)
178 				.Add(fListTabs)
179 				.SetInsets(
180 					B_USE_DEFAULT_SPACING, 0.0f,
181 					B_USE_DEFAULT_SPACING, 0.0f)
182 			.End()
183 			.Add(fPackageInfoView)
184 		.End()
185 		.Add(fWorkStatusView)
186 	;
187 
188 	fSplitView->SetCollapsible(0, false);
189 	fSplitView->SetCollapsible(1, false);
190 
191 	fModel.AddListener(fModelListener);
192 
193 	BMessage columnSettings;
194 	if (settings.FindMessage("column settings", &columnSettings) == B_OK)
195 		fPackageListView->LoadState(&columnSettings);
196 
197 	_RestoreModelSettings(settings);
198 
199 	if (fModel.PackageListViewMode() == PROMINENT)
200 		fListTabs->Select(TAB_PROMINENT_PACKAGES);
201 	else
202 		fListTabs->Select(TAB_ALL_PACKAGES);
203 
204 	_RestoreNickname(settings);
205 	_UpdateAuthorization();
206 	_RestoreWindowFrame(settings);
207 
208 	// start worker threads
209 	BPackageRoster().StartWatching(this,
210 		B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
211 
212 	_InitWorkerThreads();
213 	_AdoptModel();
214 	_StartBulkLoad();
215 }
216 
217 
218 MainWindow::MainWindow(const BMessage& settings, const PackageInfoRef& package)
219 	:
220 	BWindow(BRect(50, 50, 650, 350), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
221 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
222 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
223 	fFeaturedPackagesView(NULL),
224 	fPackageListView(NULL),
225 	fWorkStatusView(NULL),
226 	fScreenshotWindow(NULL),
227 	fUserMenu(NULL),
228 	fLogInItem(NULL),
229 	fLogOutItem(NULL),
230 	fUsersUserUsageConditionsMenuItem(NULL),
231 	fModelListener(new MainWindowModelListener(BMessenger(this)), true),
232 	fCoordinator(NULL),
233 	fSinglePackageMode(true)
234 {
235 	if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
236 		debugger("unable to create the process coordinator semaphore");
237 
238 	fFilterView = new FilterView();
239 	fPackageInfoView = new PackageInfoView(fModel.Lock(), this);
240 	fWorkStatusView = new WorkStatusView("work status");
241 
242 	BLayoutBuilder::Group<>(this, B_VERTICAL)
243 		.Add(fPackageInfoView)
244 		.Add(fWorkStatusView)
245 		.SetInsets(0, B_USE_WINDOW_INSETS, 0, 0)
246 	;
247 
248 	fModel.AddListener(fModelListener);
249 
250 	// Restore settings
251 	_RestoreNickname(settings);
252 	_UpdateAuthorization();
253 	_RestoreWindowFrame(settings);
254 
255 	fPackageInfoView->SetPackage(package);
256 
257 	_InitWorkerThreads();
258 }
259 
260 
261 MainWindow::~MainWindow()
262 {
263 	_SpinUntilProcessCoordinatorComplete();
264 	delete_sem(fCoordinatorRunningSem);
265 	fCoordinatorRunningSem = 0;
266 
267 	BPackageRoster().StopWatching(this);
268 
269 	delete_sem(fPendingActionsSem);
270 	if (fPendingActionsWorker >= 0)
271 		wait_for_thread(fPendingActionsWorker, NULL);
272 
273 	if (fScreenshotWindow != NULL && fScreenshotWindow->Lock())
274 		fScreenshotWindow->Quit();
275 }
276 
277 
278 bool
279 MainWindow::QuitRequested()
280 {
281 	BMessage settings;
282 	StoreSettings(settings);
283 
284 	BMessage message(MSG_MAIN_WINDOW_CLOSED);
285 	message.AddMessage(KEY_WINDOW_SETTINGS, &settings);
286 
287 	be_app->PostMessage(&message);
288 
289 	_StopProcessCoordinators();
290 
291 	return true;
292 }
293 
294 
295 void
296 MainWindow::MessageReceived(BMessage* message)
297 {
298 	switch (message->what) {
299 		case MSG_BULK_LOAD_DONE:
300 		{
301 			int64 errorStatus64;
302 			if (message->FindInt64(KEY_ERROR_STATUS, &errorStatus64) == B_OK)
303 				_BulkLoadCompleteReceived((status_t) errorStatus64);
304 			else
305 				printf("! expected [%s] value in message\n", KEY_ERROR_STATUS);
306 			break;
307 		}
308 		case B_SIMPLE_DATA:
309 		case B_REFS_RECEIVED:
310 			// TODO: ?
311 			break;
312 
313 		case B_PACKAGE_UPDATE:
314 			// TODO: see ticket #15879
315 			// work needs to be done here to selectively update package data in
316 			// the running HaikuDepot application when there are changes on the
317 			// system.  There is now too much data to just load everything when
318 			// there is a change.
319 			//_StartBulkLoad(false);
320 			break;
321 
322 		case MSG_REFRESH_REPOS:
323 			_StartBulkLoad(true);
324 			break;
325 
326 		case MSG_WORK_STATUS_CLEAR:
327 			_HandleWorkStatusClear();
328 			break;
329 
330 		case MSG_WORK_STATUS_CHANGE:
331 			_HandleWorkStatusChangeMessageReceived(message);
332 			break;
333 
334 		case MSG_MANAGE_REPOS:
335 			be_roster->Launch("application/x-vnd.Haiku-Repositories");
336 			break;
337 
338 		case MSG_SOFTWARE_UPDATER:
339 			be_roster->Launch("application/x-vnd.haiku-softwareupdater");
340 			break;
341 
342 		case MSG_LOG_IN:
343 			_OpenLoginWindow(BMessage());
344 			break;
345 
346 		case MSG_LOG_OUT:
347 			fModel.SetNickname("");
348 			break;
349 
350 		case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS:
351 			_ViewUserUsageConditions(LATEST);
352 			break;
353 
354 		case MSG_VIEW_USERS_USER_USAGE_CONDITIONS:
355 			_ViewUserUsageConditions(USER);
356 			break;
357 
358 		case MSG_AUTHORIZATION_CHANGED:
359 			_StartUserVerify();
360 			_UpdateAuthorization();
361 			break;
362 
363 		case MSG_CATEGORIES_LIST_CHANGED:
364 			fFilterView->AdoptModel(fModel);
365 			break;
366 
367 		case MSG_CHANGE_PACKAGE_LIST_VIEW_MODE:
368 			_HandleChangePackageListViewMode();
369 			break;
370 
371 		case MSG_SHOW_AVAILABLE_PACKAGES:
372 			{
373 				BAutolock locker(fModel.Lock());
374 				fModel.SetShowAvailablePackages(
375 					!fModel.ShowAvailablePackages());
376 			}
377 			_AdoptModel();
378 			break;
379 
380 		case MSG_SHOW_INSTALLED_PACKAGES:
381 			{
382 				BAutolock locker(fModel.Lock());
383 				fModel.SetShowInstalledPackages(
384 					!fModel.ShowInstalledPackages());
385 			}
386 			_AdoptModel();
387 			break;
388 
389 		case MSG_SHOW_SOURCE_PACKAGES:
390 			{
391 				BAutolock locker(fModel.Lock());
392 				fModel.SetShowSourcePackages(!fModel.ShowSourcePackages());
393 			}
394 			_AdoptModel();
395 			break;
396 
397 		case MSG_SHOW_DEVELOP_PACKAGES:
398 			{
399 				BAutolock locker(fModel.Lock());
400 				fModel.SetShowDevelopPackages(!fModel.ShowDevelopPackages());
401 			}
402 			_AdoptModel();
403 			break;
404 
405 			// this may be triggered by, for example, a user rating being added
406 			// or having been altered.
407 		case MSG_SERVER_DATA_CHANGED:
408 		{
409 			BString name;
410 			if (message->FindString("name", &name) == B_OK) {
411 				BAutolock locker(fModel.Lock());
412 				if (fPackageInfoView->Package()->Name() == name) {
413 					_PopulatePackageAsync(true);
414 				} else {
415 					if (Logger::IsDebugEnabled()) {
416 						printf("pkg [%s] is updated on the server, but is "
417 							"not selected so will not be updated.\n",
418 							name.String());
419 					}
420 				}
421 			}
422         	break;
423         }
424 
425 		case MSG_PACKAGE_SELECTED:
426 		{
427 			BString name;
428 			if (message->FindString("name", &name) == B_OK) {
429 				PackageInfoRef package;
430 				{
431 					BAutolock locker(fModel.Lock());
432 					package = fModel.PackageForName(name);
433 				}
434 				if (package.Get() == NULL)
435 					debugger("unable to find the named package");
436 				else
437 					_AdoptPackage(package);
438 			} else {
439 				_ClearPackage();
440 			}
441 			break;
442 		}
443 
444 		case MSG_CATEGORY_SELECTED:
445 		{
446 			BString code;
447 			if (message->FindString("code", &code) != B_OK)
448 				code = "";
449 			{
450 				BAutolock locker(fModel.Lock());
451 				fModel.SetCategory(code);
452 			}
453 			_AdoptModel();
454 			break;
455 		}
456 
457 		case MSG_DEPOT_SELECTED:
458 		{
459 			BString name;
460 			if (message->FindString("name", &name) != B_OK)
461 				name = "";
462 			{
463 				BAutolock locker(fModel.Lock());
464 				fModel.SetDepot(name);
465 			}
466 			_AdoptModel();
467 			_UpdateAvailableRepositories();
468 			break;
469 		}
470 
471 		case MSG_SEARCH_TERMS_MODIFIED:
472 		{
473 			// TODO: Do this with a delay!
474 			BString searchTerms;
475 			if (message->FindString("search terms", &searchTerms) != B_OK)
476 				searchTerms = "";
477 			{
478 				BAutolock locker(fModel.Lock());
479 				fModel.SetSearchTerms(searchTerms);
480 			}
481 			_AdoptModel();
482 			break;
483 		}
484 
485 		case MSG_PACKAGE_CHANGED:
486 		{
487 			PackageInfo* info;
488 			if (message->FindPointer("package", (void**)&info) == B_OK) {
489 				PackageInfoRef ref(info, true);
490 				uint32 changes;
491 				if (message->FindUInt32("changes", &changes) != B_OK)
492 					changes = 0;
493 				if ((changes & PKG_CHANGED_STATE) != 0) {
494 					BAutolock locker(fModel.Lock());
495 					fModel.SetPackageState(ref, ref->State());
496 				}
497 				_AddRemovePackageFromLists(ref);
498 				if ((changes & PKG_CHANGED_STATE) != 0
499 						&& fCoordinator.Get() == NULL) {
500 					fWorkStatusView->PackageStatusChanged(ref);
501 				}
502 			}
503 			break;
504 		}
505 
506 		case MSG_RATE_PACKAGE:
507 			_RatePackage();
508 			break;
509 
510 		case MSG_SHOW_SCREENSHOT:
511 			_ShowScreenshot();
512 			break;
513 
514 		case MSG_PACKAGE_WORKER_BUSY:
515 		{
516 			BString reason;
517 			status_t status = message->FindString("reason", &reason);
518 			if (status != B_OK)
519 				break;
520 			fWorkStatusView->SetBusy(reason);
521 			break;
522 		}
523 
524 		case MSG_PACKAGE_WORKER_IDLE:
525 			fWorkStatusView->SetIdle();
526 			break;
527 
528 		case MSG_USER_USAGE_CONDITIONS_NOT_LATEST:
529 		{
530 			BMessage userDetailMsg;
531 			if (message->FindMessage("userDetail", &userDetailMsg) != B_OK) {
532 				debugger("expected the [userDetail] data to be carried in the "
533 					"message.");
534 			}
535 			UserDetail userDetail(&userDetailMsg);
536 			_HandleUserUsageConditionsNotLatest(userDetail);
537 			break;
538 		}
539 
540 		default:
541 			BWindow::MessageReceived(message);
542 			break;
543 	}
544 }
545 
546 
547 static const char*
548 main_window_package_list_view_mode_str(package_list_view_mode mode)
549 {
550 	if (mode == PROMINENT)
551 		return "PROMINENT";
552 	return "ALL";
553 }
554 
555 
556 static package_list_view_mode
557 main_window_str_to_package_list_view_mode(const BString& str)
558 {
559 	if (str == "PROMINENT")
560 		return PROMINENT;
561 	return ALL;
562 }
563 
564 
565 void
566 MainWindow::StoreSettings(BMessage& settings) const
567 {
568 	settings.AddRect(_WindowFrameName(), Frame());
569 	if (!fSinglePackageMode) {
570 		settings.AddRect("window frame", Frame());
571 
572 		BMessage columnSettings;
573 		if (fPackageListView != NULL)
574 			fPackageListView->SaveState(&columnSettings);
575 
576 		settings.AddMessage("column settings", &columnSettings);
577 
578 		settings.AddString(KEY_PACKAGE_LIST_VIEW_MODE,
579 			main_window_package_list_view_mode_str(
580 				fModel.PackageListViewMode()));
581 		settings.AddBool("show available packages",
582 			fModel.ShowAvailablePackages());
583 		settings.AddBool("show installed packages",
584 			fModel.ShowInstalledPackages());
585 		settings.AddBool("show develop packages", fModel.ShowDevelopPackages());
586 		settings.AddBool("show source packages", fModel.ShowSourcePackages());
587 	}
588 
589 	settings.AddString("username", fModel.Nickname());
590 }
591 
592 
593 void
594 MainWindow::PackageChanged(const PackageInfoEvent& event)
595 {
596 	uint32 watchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE;
597 	if ((event.Changes() & watchedChanges) != 0) {
598 		PackageInfoRef ref(event.Package());
599 		BMessage message(MSG_PACKAGE_CHANGED);
600 		message.AddPointer("package", ref.Get());
601 		message.AddUInt32("changes", event.Changes());
602 		ref.Detach();
603 			// reference needs to be released by MessageReceived();
604 		PostMessage(&message);
605 	}
606 }
607 
608 
609 status_t
610 MainWindow::SchedulePackageActions(PackageActionList& list)
611 {
612 	AutoLocker<BLocker> lock(&fPendingActionsLock);
613 	for (int32 i = 0; i < list.CountItems(); i++) {
614 		if (!fPendingActions.Add(list.ItemAtFast(i)))
615 			return B_NO_MEMORY;
616 	}
617 
618 	return release_sem_etc(fPendingActionsSem, list.CountItems(), 0);
619 }
620 
621 
622 Model*
623 MainWindow::GetModel()
624 {
625 	return &fModel;
626 }
627 
628 
629 void
630 MainWindow::_BuildMenu(BMenuBar* menuBar)
631 {
632 	BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
633 	fRefreshRepositoriesItem = new BMenuItem(
634 		B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS));
635 	menu->AddItem(fRefreshRepositoriesItem);
636 	menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
637 		B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS)));
638 	menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates"
639 		B_UTF8_ELLIPSIS), new BMessage(MSG_SOFTWARE_UPDATER)));
640 
641 	menuBar->AddItem(menu);
642 
643 	fRepositoryMenu = new BMenu(B_TRANSLATE("Repositories"));
644 	menuBar->AddItem(fRepositoryMenu);
645 
646 	menu = new BMenu(B_TRANSLATE("Show"));
647 
648 	fShowAvailablePackagesItem = new BMenuItem(
649 		B_TRANSLATE("Available packages"),
650 		new BMessage(MSG_SHOW_AVAILABLE_PACKAGES));
651 	menu->AddItem(fShowAvailablePackagesItem);
652 
653 	fShowInstalledPackagesItem = new BMenuItem(
654 		B_TRANSLATE("Installed packages"),
655 		new BMessage(MSG_SHOW_INSTALLED_PACKAGES));
656 	menu->AddItem(fShowInstalledPackagesItem);
657 
658 	menu->AddSeparatorItem();
659 
660 	fShowDevelopPackagesItem = new BMenuItem(
661 		B_TRANSLATE("Develop packages"),
662 		new BMessage(MSG_SHOW_DEVELOP_PACKAGES));
663 	menu->AddItem(fShowDevelopPackagesItem);
664 
665 	fShowSourcePackagesItem = new BMenuItem(
666 		B_TRANSLATE("Source packages"),
667 		new BMessage(MSG_SHOW_SOURCE_PACKAGES));
668 	menu->AddItem(fShowSourcePackagesItem);
669 
670 	menuBar->AddItem(menu);
671 }
672 
673 
674 void
675 MainWindow::_BuildUserMenu(BMenuBar* menuBar)
676 {
677 	fUserMenu = new BMenu(B_TRANSLATE("Not logged in"));
678 
679 	fLogInItem = new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
680 		new BMessage(MSG_LOG_IN));
681 	fUserMenu->AddItem(fLogInItem);
682 
683 	fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
684 		new BMessage(MSG_LOG_OUT));
685 	fUserMenu->AddItem(fLogOutItem);
686 
687 	BMenuItem *latestUserUsageConditionsMenuItem =
688 		new BMenuItem(B_TRANSLATE("View latest usage conditions"
689 			B_UTF8_ELLIPSIS),
690 			new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS));
691 	fUserMenu->AddItem(latestUserUsageConditionsMenuItem);
692 
693 	fUsersUserUsageConditionsMenuItem =
694 		new BMenuItem(B_TRANSLATE("View agreed usage conditions"
695 			B_UTF8_ELLIPSIS),
696 			new BMessage(MSG_VIEW_USERS_USER_USAGE_CONDITIONS));
697 	fUserMenu->AddItem(fUsersUserUsageConditionsMenuItem);
698 
699 	menuBar->AddItem(fUserMenu);
700 }
701 
702 
703 void
704 MainWindow::_RestoreNickname(const BMessage& settings)
705 {
706 	BString nickname;
707 	if (settings.FindString("username", &nickname) == B_OK
708 		&& nickname.Length() > 0) {
709 		fModel.SetNickname(nickname);
710 	}
711 }
712 
713 
714 const char*
715 MainWindow::_WindowFrameName() const
716 {
717 	if (fSinglePackageMode)
718 		return "small window frame";
719 
720 	return "window frame";
721 }
722 
723 
724 void
725 MainWindow::_RestoreWindowFrame(const BMessage& settings)
726 {
727 	BRect frame = Frame();
728 
729 	BRect windowFrame;
730 	bool fromSettings = false;
731 	if (settings.FindRect(_WindowFrameName(), &windowFrame) == B_OK) {
732 		frame = windowFrame;
733 		fromSettings = true;
734 	} else if (!fSinglePackageMode) {
735 		// Resize to occupy a certain screen size
736 		BRect screenFrame = BScreen(this).Frame();
737 		float width = frame.Width();
738 		float height = frame.Height();
739 		if (width < screenFrame.Width() * .666f
740 			&& height < screenFrame.Height() * .666f) {
741 			frame.bottom = frame.top + screenFrame.Height() * .666f;
742 			frame.right = frame.left
743 				+ std::min(screenFrame.Width() * .666f, height * 7 / 5);
744 		}
745 	}
746 
747 	MoveTo(frame.LeftTop());
748 	ResizeTo(frame.Width(), frame.Height());
749 
750 	if (fromSettings)
751 		MoveOnScreen();
752 	else
753 		CenterOnScreen();
754 }
755 
756 
757 void
758 MainWindow::_RestoreModelSettings(const BMessage& settings)
759 {
760 	BString packageListViewMode;
761 	if (settings.FindString(KEY_PACKAGE_LIST_VIEW_MODE,
762 			&packageListViewMode) == B_OK) {
763 		fModel.SetPackageListViewMode(
764 			main_window_str_to_package_list_view_mode(packageListViewMode));
765 	}
766 
767 	bool showOption;
768 	if (settings.FindBool("show available packages", &showOption) == B_OK)
769 		fModel.SetShowAvailablePackages(showOption);
770 	if (settings.FindBool("show installed packages", &showOption) == B_OK)
771 		fModel.SetShowInstalledPackages(showOption);
772 	if (settings.FindBool("show develop packages", &showOption) == B_OK)
773 		fModel.SetShowDevelopPackages(showOption);
774 	if (settings.FindBool("show source packages", &showOption) == B_OK)
775 		fModel.SetShowSourcePackages(showOption);
776 }
777 
778 
779 void
780 MainWindow::_InitWorkerThreads()
781 {
782 	fPendingActionsSem = create_sem(0, "PendingPackageActions");
783 	if (fPendingActionsSem >= 0) {
784 		fPendingActionsWorker = spawn_thread(&_PackageActionWorker,
785 			"Planet Express", B_NORMAL_PRIORITY, this);
786 		if (fPendingActionsWorker >= 0)
787 			resume_thread(fPendingActionsWorker);
788 	} else
789 		fPendingActionsWorker = -1;
790 
791 	fPackageToPopulateSem = create_sem(0, "PopulatePackage");
792 	if (fPackageToPopulateSem >= 0) {
793 		fPopulatePackageWorker = spawn_thread(&_PopulatePackageWorker,
794 			"Package Populator", B_NORMAL_PRIORITY, this);
795 		if (fPopulatePackageWorker >= 0)
796 			resume_thread(fPopulatePackageWorker);
797 	} else
798 		fPopulatePackageWorker = -1;
799 }
800 
801 
802 void
803 MainWindow::_AdoptModelControls()
804 {
805 	if (fSinglePackageMode)
806 		return;
807 
808 	BAutolock locker(fModel.Lock());
809 	fShowAvailablePackagesItem->SetMarked(fModel.ShowAvailablePackages());
810 	fShowInstalledPackagesItem->SetMarked(fModel.ShowInstalledPackages());
811 	fShowSourcePackagesItem->SetMarked(fModel.ShowSourcePackages());
812 	fShowDevelopPackagesItem->SetMarked(fModel.ShowDevelopPackages());
813 
814 	if (fModel.PackageListViewMode() == PROMINENT)
815 		fListTabs->Select(TAB_PROMINENT_PACKAGES);
816 	else
817 		fListTabs->Select(TAB_ALL_PACKAGES);
818 
819 	fFilterView->AdoptModel(fModel);
820 }
821 
822 
823 void
824 MainWindow::_AdoptModel()
825 {
826 	if (Logger::IsTraceEnabled())
827 		printf("adopting model to main window ui\n");
828 
829 	if (fSinglePackageMode)
830 		return;
831 
832 	fModel.Lock()->Lock();
833 	const DepotList& depots = fModel.Depots();
834 	fModel.Lock()->Unlock();
835 
836 	for (int32 d = 0; d < depots.CountItems(); d++) {
837 		const DepotInfo& depot = depots.ItemAtFast(d);
838 		const PackageList& packages = depot.Packages();
839 		for (int32 p = 0; p < packages.CountItems(); p++)
840 			_AddRemovePackageFromLists(packages.ItemAtFast(p));
841 	}
842 
843 	_AdoptModelControls();
844 }
845 
846 
847 void
848 MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package)
849 {
850 	bool matches;
851 
852 	{
853 		AutoLocker<BLocker> modelLocker(fModel.Lock());
854 		matches = fModel.MatchesFilter(package);
855 	}
856 
857 	if (matches) {
858 		if (package->IsProminent())
859 			fFeaturedPackagesView->AddPackage(package);
860 		fPackageListView->AddPackage(package);
861 	} else {
862 		fFeaturedPackagesView->RemovePackage(package);
863 		fPackageListView->RemovePackage(package);
864 	}
865 }
866 
867 
868 void
869 MainWindow::_AdoptPackage(const PackageInfoRef& package)
870 {
871 	{
872 		BAutolock locker(fModel.Lock());
873 		fPackageInfoView->SetPackage(package);
874 
875 		if (fFeaturedPackagesView != NULL)
876 			fFeaturedPackagesView->SelectPackage(package);
877 		if (fPackageListView != NULL)
878 			fPackageListView->SelectPackage(package);
879 	}
880 
881 	_PopulatePackageAsync(false);
882 }
883 
884 
885 void
886 MainWindow::_ClearPackage()
887 {
888 	fPackageInfoView->Clear();
889 }
890 
891 
892 void
893 MainWindow::_StartBulkLoad(bool force)
894 {
895 	if (fFeaturedPackagesView != NULL)
896 		fFeaturedPackagesView->Clear();
897 	if (fPackageListView != NULL)
898 		fPackageListView->Clear();
899 	fPackageInfoView->Clear();
900 
901 	fRefreshRepositoriesItem->SetEnabled(false);
902 	ProcessCoordinator* bulkLoadCoordinator =
903 		ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
904 			this,
905 				// PackageInfoListener
906 			this,
907 				// ProcessCoordinatorListener
908 			&fModel, force);
909 	_AddProcessCoordinator(bulkLoadCoordinator);
910 }
911 
912 
913 void
914 MainWindow::_BulkLoadCompleteReceived(status_t errorStatus)
915 {
916 	if (errorStatus != B_OK) {
917 		AppUtils::NotifySimpleError(
918 			B_TRANSLATE("Package update error"),
919 			B_TRANSLATE("While updating package data, a problem has arisen "
920 				"that may cause data to be outdated or missing from the "
921 				"application's display. Additional details regarding this "
922 				"problem may be able to be obtained from the application "
923 				"logs."
924 				ALERT_MSG_LOGS_USER_GUIDE));
925 	}
926 
927 	fRefreshRepositoriesItem->SetEnabled(true);
928 	_AdoptModel();
929 	_UpdateAvailableRepositories();
930 
931 	// if after loading everything in, it transpires that there are no
932 	// featured packages then the featured packages should be disabled
933 	// and the user should be switched to the "all packages" view so
934 	// that they are not presented with a blank window!
935 
936 	bool hasProminentPackages = fModel.HasAnyProminentPackages();
937 	fListTabs->TabAt(TAB_PROMINENT_PACKAGES)->SetEnabled(hasProminentPackages);
938 	if (!hasProminentPackages
939 			&& fListTabs->Selection() == TAB_PROMINENT_PACKAGES) {
940 		fModel.SetPackageListViewMode(ALL);
941 		fListTabs->Select(TAB_ALL_PACKAGES);
942 	}
943 }
944 
945 
946 void
947 MainWindow::_NotifyWorkStatusClear()
948 {
949 	BMessage message(MSG_WORK_STATUS_CLEAR);
950 	this->PostMessage(&message, this);
951 }
952 
953 
954 void
955 MainWindow::_HandleWorkStatusClear()
956 {
957 	fWorkStatusView->SetText("");
958 	fWorkStatusView->SetIdle();
959 }
960 
961 
962 /*! Sends off a message to the Window so that it can change the status view
963     on the front-end in the UI thread.
964 */
965 
966 void
967 MainWindow::_NotifyWorkStatusChange(const BString& text, float progress)
968 {
969 	BMessage message(MSG_WORK_STATUS_CHANGE);
970 
971 	if (!text.IsEmpty())
972 		message.AddString(KEY_WORK_STATUS_TEXT, text);
973 	message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress);
974 
975 	this->PostMessage(&message, this);
976 }
977 
978 
979 void
980 MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message)
981 {
982 	if (fWorkStatusView == NULL)
983 		return;
984 
985 	BString text;
986 	float progress;
987 
988 	if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK)
989 		fWorkStatusView->SetText(text);
990 
991 	if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK)
992 		fWorkStatusView->SetProgress(progress);
993 }
994 
995 
996 status_t
997 MainWindow::_PackageActionWorker(void* arg)
998 {
999 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
1000 
1001 	while (acquire_sem(window->fPendingActionsSem) == B_OK) {
1002 		PackageActionRef ref;
1003 		{
1004 			AutoLocker<BLocker> lock(&window->fPendingActionsLock);
1005 			ref = window->fPendingActions.ItemAt(0);
1006 			if (ref.Get() == NULL)
1007 				break;
1008 			window->fPendingActions.Remove(0);
1009 		}
1010 
1011 		BMessenger messenger(window);
1012 		BMessage busyMessage(MSG_PACKAGE_WORKER_BUSY);
1013 		BString text(ref->Label());
1014 		text << B_UTF8_ELLIPSIS;
1015 		busyMessage.AddString("reason", text);
1016 
1017 		messenger.SendMessage(&busyMessage);
1018 		ref->Perform();
1019 		messenger.SendMessage(MSG_PACKAGE_WORKER_IDLE);
1020 	}
1021 
1022 	return 0;
1023 }
1024 
1025 
1026 /*! This method will cause the package to have its data refreshed from
1027     the server application.  The refresh happens in the background; this method
1028     is asynchronous.
1029 */
1030 
1031 void
1032 MainWindow::_PopulatePackageAsync(bool forcePopulate)
1033 {
1034 		// Trigger asynchronous package population from the web-app
1035 	{
1036 		AutoLocker<BLocker> lock(&fPackageToPopulateLock);
1037 		fPackageToPopulate = fPackageInfoView->Package();
1038 		fForcePopulatePackage = forcePopulate;
1039 	}
1040 	release_sem_etc(fPackageToPopulateSem, 1, 0);
1041 
1042 	if (Logger::IsDebugEnabled()) {
1043 		printf("pkg [%s] will be updated from the server.\n",
1044 			fPackageToPopulate->Name().String());
1045 	}
1046 }
1047 
1048 
1049 /*! This method will run in the background.  The thread will block until there
1050     is a package to be updated.  When the thread unblocks, it will update the
1051     package with information from the server.
1052 */
1053 
1054 status_t
1055 MainWindow::_PopulatePackageWorker(void* arg)
1056 {
1057 	MainWindow* window = reinterpret_cast<MainWindow*>(arg);
1058 
1059 	while (acquire_sem(window->fPackageToPopulateSem) == B_OK) {
1060 		PackageInfoRef package;
1061 		bool force;
1062 		{
1063 			AutoLocker<BLocker> lock(&window->fPackageToPopulateLock);
1064 			package = window->fPackageToPopulate;
1065 			force = window->fForcePopulatePackage;
1066 		}
1067 
1068 		if (package.Get() != NULL) {
1069 			uint32 populateFlags = Model::POPULATE_USER_RATINGS
1070 				| Model::POPULATE_SCREEN_SHOTS
1071 				| Model::POPULATE_CHANGELOG;
1072 
1073 			if (force)
1074 				populateFlags |= Model::POPULATE_FORCE;
1075 
1076 			window->fModel.PopulatePackage(package, populateFlags);
1077 
1078 			if (Logger::IsDebugEnabled()) {
1079 				printf("populating package [%s]\n",
1080 					package->Name().String());
1081 			}
1082 		}
1083 	}
1084 
1085 	return 0;
1086 }
1087 
1088 
1089 void
1090 MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage)
1091 {
1092 	UserLoginWindow* window = new UserLoginWindow(this,
1093 		BRect(0, 0, 500, 400), fModel);
1094 
1095 	if (onSuccessMessage.what != 0)
1096 		window->SetOnSuccessMessage(BMessenger(this), onSuccessMessage);
1097 
1098 	window->Show();
1099 }
1100 
1101 
1102 void
1103 MainWindow::_StartUserVerify()
1104 {
1105 	if (!fModel.Nickname().IsEmpty()) {
1106 		_AddProcessCoordinator(
1107 			ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
1108 				this,
1109 					// UserDetailVerifierListener
1110 				this,
1111 					// ProcessCoordinatorListener
1112 				&fModel) );
1113 	}
1114 }
1115 
1116 
1117 void
1118 MainWindow::_UpdateAuthorization()
1119 {
1120 	BString nickname(fModel.Nickname());
1121 	bool hasUser = !nickname.IsEmpty();
1122 
1123 	if (fLogOutItem != NULL)
1124 		fLogOutItem->SetEnabled(hasUser);
1125 	if (fUsersUserUsageConditionsMenuItem != NULL)
1126 		fUsersUserUsageConditionsMenuItem->SetEnabled(hasUser);
1127 	if (fLogInItem != NULL) {
1128 		if (hasUser)
1129 			fLogInItem->SetLabel(B_TRANSLATE("Switch account" B_UTF8_ELLIPSIS));
1130 		else
1131 			fLogInItem->SetLabel(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS));
1132 	}
1133 
1134 	if (fUserMenu != NULL) {
1135 		BString label;
1136 		if (hasUser) {
1137 			label = B_TRANSLATE("Logged in as %User%");
1138 			label.ReplaceAll("%User%", nickname);
1139 		} else {
1140 			label = B_TRANSLATE("Not logged in");
1141 		}
1142 		fUserMenu->Superitem()->SetLabel(label);
1143 	}
1144 }
1145 
1146 
1147 void
1148 MainWindow::_UpdateAvailableRepositories()
1149 {
1150 	fRepositoryMenu->RemoveItems(0, fRepositoryMenu->CountItems(), true);
1151 
1152 	fRepositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"),
1153 		new BMessage(MSG_DEPOT_SELECTED)));
1154 
1155 	fRepositoryMenu->AddItem(new BSeparatorItem());
1156 
1157 	bool foundSelectedDepot = false;
1158 	const DepotList& depots = fModel.Depots();
1159 	for (int i = 0; i < depots.CountItems(); i++) {
1160 		const DepotInfo& depot = depots.ItemAtFast(i);
1161 
1162 		if (depot.Name().Length() != 0) {
1163 			BMessage* message = new BMessage(MSG_DEPOT_SELECTED);
1164 			message->AddString("name", depot.Name());
1165 			BMenuItem* item = new BMenuItem(depot.Name(), message);
1166 			fRepositoryMenu->AddItem(item);
1167 
1168 			if (depot.Name() == fModel.Depot()) {
1169 				item->SetMarked(true);
1170 				foundSelectedDepot = true;
1171 			}
1172 		}
1173 	}
1174 
1175 	if (!foundSelectedDepot)
1176 		fRepositoryMenu->ItemAt(0)->SetMarked(true);
1177 }
1178 
1179 
1180 bool
1181 MainWindow::_SelectedPackageHasWebAppRepositoryCode()
1182 {
1183 	const PackageInfoRef& package = fPackageInfoView->Package();
1184 	const BString depotName = package->DepotName();
1185 
1186 	if (depotName.IsEmpty()) {
1187 		if (Logger::IsDebugEnabled()) {
1188 			printf("the package [%s] has no depot name\n",
1189 				package->Name().String());
1190 		}
1191 	} else {
1192 		const DepotInfo* depot = fModel.DepotForName(depotName);
1193 
1194 		if (depot == NULL) {
1195 			printf("the depot [%s] was not able to be found\n",
1196 				depotName.String());
1197 		} else {
1198 			BString repositoryCode = depot->WebAppRepositoryCode();
1199 
1200 			if (repositoryCode.IsEmpty()) {
1201 				printf("the depot [%s] has no web app repository code\n",
1202 					depotName.String());
1203 			} else {
1204 				return true;
1205 			}
1206 		}
1207 	}
1208 
1209 	return false;
1210 }
1211 
1212 
1213 void
1214 MainWindow::_RatePackage()
1215 {
1216 	if (!_SelectedPackageHasWebAppRepositoryCode()) {
1217 		BAlert* alert = new(std::nothrow) BAlert(
1218 			B_TRANSLATE("Rating not possible"),
1219 			B_TRANSLATE("This package doesn't seem to be on the HaikuDepot "
1220 				"Server, so it's not possible to create a new rating "
1221 				"or edit an existing rating."),
1222 			B_TRANSLATE("OK"));
1223 		alert->Go();
1224     	return;
1225 	}
1226 
1227 	if (fModel.Nickname().IsEmpty()) {
1228 		BAlert* alert = new(std::nothrow) BAlert(
1229 			B_TRANSLATE("Not logged in"),
1230 			B_TRANSLATE("You need to be logged into an account before you "
1231 				"can rate packages."),
1232 			B_TRANSLATE("Cancel"),
1233 			B_TRANSLATE("Login or Create account"));
1234 
1235 		if (alert == NULL)
1236 			return;
1237 
1238 		int32 choice = alert->Go();
1239 		if (choice == 1)
1240 			_OpenLoginWindow(BMessage(MSG_RATE_PACKAGE));
1241 		return;
1242 	}
1243 
1244 	// TODO: Allow only one RatePackageWindow
1245 	// TODO: Mechanism for remembering the window frame
1246 	RatePackageWindow* window = new RatePackageWindow(this,
1247 		BRect(0, 0, 500, 400), fModel);
1248 	window->SetPackage(fPackageInfoView->Package());
1249 	window->Show();
1250 }
1251 
1252 
1253 void
1254 MainWindow::_ShowScreenshot()
1255 {
1256 	// TODO: Mechanism for remembering the window frame
1257 	if (fScreenshotWindow == NULL)
1258 		fScreenshotWindow = new ScreenshotWindow(this, BRect(0, 0, 500, 400));
1259 
1260 	if (fScreenshotWindow->LockWithTimeout(1000) != B_OK)
1261 		return;
1262 
1263 	fScreenshotWindow->SetPackage(fPackageInfoView->Package());
1264 
1265 	if (fScreenshotWindow->IsHidden())
1266 		fScreenshotWindow->Show();
1267 	else
1268 		fScreenshotWindow->Activate();
1269 
1270 	fScreenshotWindow->Unlock();
1271 }
1272 
1273 
1274 void
1275 MainWindow::_ViewUserUsageConditions(
1276 	UserUsageConditionsSelectionMode mode)
1277 {
1278 	UserUsageConditionsWindow* window = new UserUsageConditionsWindow(
1279 		fModel, mode);
1280 	window->Show();
1281 }
1282 
1283 
1284 void
1285 MainWindow::UserCredentialsFailed()
1286 {
1287 	BString message = B_TRANSLATE("The password previously "
1288 		"supplied for the user [%Nickname%] is not currently "
1289 		"valid. The user will be logged-out of this application "
1290 		"and you should login again with your updated password.");
1291 	message.ReplaceAll("%Nickname%", fModel.Nickname());
1292 
1293 	AppUtils::NotifySimpleError(B_TRANSLATE("Login issue"),
1294 		message);
1295 
1296 	{
1297 		AutoLocker<BLocker> locker(fModel.Lock());
1298 		fModel.SetNickname("");
1299 	}
1300 }
1301 
1302 
1303 /*! \brief This method is invoked from the UserDetailVerifierProcess on a
1304 		   background thread.  For this reason it lodges a message into itself
1305 		   which can then be handled on the main thread.
1306 */
1307 
1308 void
1309 MainWindow::UserUsageConditionsNotLatest(const UserDetail& userDetail)
1310 {
1311 	BMessage message(MSG_USER_USAGE_CONDITIONS_NOT_LATEST);
1312 	BMessage detailsMessage;
1313 	if (userDetail.Archive(&detailsMessage, true) != B_OK
1314 			|| message.AddMessage("userDetail", &detailsMessage) != B_OK) {
1315 		printf("!! unable to archive the user detail into a message\n");
1316 	}
1317 	else
1318 		BMessenger(this).SendMessage(&message);
1319 }
1320 
1321 
1322 void
1323 MainWindow::_HandleUserUsageConditionsNotLatest(
1324 	const UserDetail& userDetail)
1325 {
1326 	ToLatestUserUsageConditionsWindow* window =
1327 		new ToLatestUserUsageConditionsWindow(this, fModel, userDetail);
1328 	window->Show();
1329 }
1330 
1331 
1332 void
1333 MainWindow::_AddProcessCoordinator(ProcessCoordinator* item)
1334 {
1335 	AutoLocker<BLocker> lock(&fCoordinatorLock);
1336 
1337 	if (fCoordinator.Get() == NULL) {
1338 		if (acquire_sem(fCoordinatorRunningSem) != B_OK)
1339 			debugger("unable to acquire the process coordinator sem");
1340 		if (Logger::IsInfoEnabled()) {
1341 			printf("adding and starting a process coordinator [%s]\n",
1342 				item->Name().String());
1343 		}
1344 		fCoordinator = BReference<ProcessCoordinator>(item);
1345 		fCoordinator->Start();
1346 	}
1347 	else {
1348 		if (Logger::IsInfoEnabled()) {
1349 			printf("adding process coordinator [%s] to the queue\n",
1350 				item->Name().String());
1351 		}
1352 		fCoordinatorQueue.push(item);
1353 	}
1354 }
1355 
1356 
1357 void
1358 MainWindow::_SpinUntilProcessCoordinatorComplete()
1359 {
1360 	while (true) {
1361 		if (acquire_sem(fCoordinatorRunningSem) != B_OK)
1362 			debugger("unable to acquire the process coordinator sem");
1363 		if (release_sem(fCoordinatorRunningSem) != B_OK)
1364 			debugger("unable to release the process coordinator sem");
1365 		{
1366 			AutoLocker<BLocker> lock(&fCoordinatorLock);
1367 			if (fCoordinator.Get() == NULL)
1368 				return;
1369 		}
1370 	}
1371 }
1372 
1373 
1374 void
1375 MainWindow::_StopProcessCoordinators()
1376 {
1377 	if (Logger::IsInfoEnabled())
1378 		printf("will stop all process coordinators\n");
1379 
1380 	{
1381 		AutoLocker<BLocker> lock(&fCoordinatorLock);
1382 
1383 		while (!fCoordinatorQueue.empty()) {
1384 			BReference<ProcessCoordinator> processCoordinator = fCoordinatorQueue.front();
1385 			if (Logger::IsInfoEnabled()) {
1386 				printf("will drop queued process coordinator [%s]\n",
1387 					processCoordinator->Name().String());
1388 			}
1389 			fCoordinatorQueue.pop();
1390 		}
1391 
1392 		if (fCoordinator.Get() != NULL) {
1393 			fCoordinator->Stop();
1394 		}
1395 	}
1396 
1397 	if (Logger::IsInfoEnabled())
1398 		printf("will wait until the process coordinator has stopped\n");
1399 
1400 	_SpinUntilProcessCoordinatorComplete();
1401 
1402 	if (Logger::IsInfoEnabled())
1403 		printf("did stop all process coordinators\n");
1404 }
1405 
1406 
1407 /*! This method is called when there is some change in the bulk load process
1408 	or other process coordinator.
1409 	A change may mean that a new process has started / stopped etc... or it
1410 	may mean that the entire coordinator has finished.
1411 */
1412 
1413 void
1414 MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
1415 {
1416 	AutoLocker<BLocker> lock(&fCoordinatorLock);
1417 
1418 	if (fCoordinator.Get() == coordinatorState.Coordinator()) {
1419 		if (!coordinatorState.IsRunning()) {
1420 			if (release_sem(fCoordinatorRunningSem) != B_OK)
1421 				debugger("unable to release the process coordinator sem");
1422 			if (Logger::IsInfoEnabled()) {
1423 				printf("process coordinator [%s] did complete\n",
1424 					fCoordinator->Name().String());
1425 			}
1426 			// complete the last one that just finished
1427 			BMessage* message = fCoordinator->Message();
1428 
1429 			if (message != NULL) {
1430 				BMessenger messenger(this);
1431 				message->AddInt64(KEY_ERROR_STATUS,
1432 					(int64) fCoordinator->ErrorStatus());
1433 				messenger.SendMessage(message);
1434 			}
1435 
1436 			fCoordinator = BReference<ProcessCoordinator>(NULL);
1437 				// will delete the old process coordinator if it is not used
1438 				// elsewhere.
1439 
1440 			// now schedule the next one.
1441 			if (!fCoordinatorQueue.empty()) {
1442 				if (acquire_sem(fCoordinatorRunningSem) != B_OK)
1443 					debugger("unable to acquire the process coordinator sem");
1444 				fCoordinator = fCoordinatorQueue.front();
1445 				if (Logger::IsInfoEnabled()) {
1446 					printf("starting next process coordinator [%s]\n",
1447 						fCoordinator->Name().String());
1448 				}
1449 				fCoordinatorQueue.pop();
1450 				fCoordinator->Start();
1451 			}
1452 			else {
1453 				_NotifyWorkStatusClear();
1454 			}
1455 		}
1456 		else {
1457 			_NotifyWorkStatusChange(coordinatorState.Message(),
1458 				coordinatorState.Progress());
1459 				// show the progress to the user.
1460 		}
1461 	} else {
1462 		if (Logger::IsInfoEnabled())
1463 			printf("! unknown process coordinator changed\n");
1464 	}
1465 }
1466 
1467 
1468 static package_list_view_mode
1469 main_window_tab_to_package_list_view_mode(int32 tab)
1470 {
1471 	if (tab == TAB_PROMINENT_PACKAGES)
1472 		return PROMINENT;
1473 	return ALL;
1474 }
1475 
1476 
1477 void
1478 MainWindow::_HandleChangePackageListViewMode()
1479 {
1480 	package_list_view_mode tabMode = main_window_tab_to_package_list_view_mode(
1481 		fListTabs->Selection());
1482 	package_list_view_mode modelMode = fModel.PackageListViewMode();
1483 
1484 	if (tabMode != modelMode) {
1485 		BAutolock locker(fModel.Lock());
1486 		fModel.SetPackageListViewMode(tabMode);
1487 	}
1488 }