xref: /haiku/src/servers/package/ProblemWindow.cpp (revision 6889394848e2dc9f41ff53b12141d572822ca0c6)
1 /*
2  * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ProblemWindow.h"
8 
9 #include <Button.h>
10 #include <Catalog.h>
11 #include <GroupView.h>
12 #include <LayoutBuilder.h>
13 #include <RadioButton.h>
14 #include <ScrollView.h>
15 #include <StringView.h>
16 #include <package/solver/Solver.h>
17 #include <package/solver/SolverPackage.h>
18 #include <package/solver/SolverProblem.h>
19 #include <package/solver/SolverProblemSolution.h>
20 
21 #include <AutoLocker.h>
22 #include <package/manager/Exceptions.h>
23 #include <ViewPort.h>
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "PackageProblem"
28 
29 using namespace BPackageKit;
30 
31 using BPackageKit::BManager::BPrivate::BFatalErrorException;
32 
33 
34 static const uint32 kRetryMessage = 'rtry';
35 static const uint32 kUpdateRetryButtonMessage = 'uprt';
36 
37 
38 struct ProblemWindow::Solution {
39 	BSolverProblem*					fProblem;
40 	const BSolverProblemSolution*	fSolution;
41 
42 	Solution()
43 		:
44 		fProblem(NULL),
45 		fSolution(NULL)
46 	{
47 	}
48 
49 	Solution(BSolverProblem* problem, const BSolverProblemSolution* solution)
50 		:
51 		fProblem(problem),
52 		fSolution(solution)
53 	{
54 	}
55 };
56 
57 
58 ProblemWindow::ProblemWindow()
59 	:
60 	BWindow(BRect(0, 0, 400, 300), B_TRANSLATE_COMMENT("Package problems",
61 			"Window title"), B_TITLED_WINDOW_LOOK,
62 		B_MODAL_APP_WINDOW_FEEL,
63 		B_ASYNCHRONOUS_CONTROLS | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS,
64 		B_ALL_WORKSPACES),
65 	fDoneSemaphore(-1),
66 	fClientWaiting(false),
67 	fAccepted(false),
68 	fContainerView(NULL),
69 	fCancelButton(NULL),
70 	fRetryButton(NULL),
71 	fSolutions(),
72 	fPackagesAddedByUser(NULL),
73 	fPackagesRemovedByUser(NULL)
74 
75 {
76 	fDoneSemaphore = create_sem(0, "package problems");
77 	if (fDoneSemaphore < 0)
78 		throw std::bad_alloc();
79 
80 	BStringView* topTextView = NULL;
81 	BViewPort* viewPort = NULL;
82 
83 	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
84 		.SetInsets(B_USE_SMALL_INSETS)
85 		.Add(topTextView = new BStringView(NULL, B_TRANSLATE(
86 				"The following problems have been encountered. Please select "
87 				"a solution for each:")))
88 		.Add(new BScrollView(NULL, viewPort = new BViewPort(), 0, false, true))
89 		.AddGroup(B_HORIZONTAL)
90 			.AddGlue()
91 			.Add(fCancelButton = new BButton(B_TRANSLATE("Cancel"),
92 				new BMessage(B_CANCEL)))
93 			.Add(fRetryButton = new BButton(B_TRANSLATE("Retry"),
94 				new BMessage(kRetryMessage)))
95 		.End();
96 
97 	topTextView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
98 
99 	viewPort->SetChildView(fContainerView = new BGroupView(B_VERTICAL, 0));
100 
101 	// set small scroll step (large step will be set by the view port)
102 	font_height fontHeight;
103 	topTextView->GetFontHeight(&fontHeight);
104 	float smallStep = ceilf(fontHeight.ascent + fontHeight.descent);
105 	viewPort->ScrollBar(B_VERTICAL)->SetSteps(smallStep, smallStep);
106 }
107 
108 
109 ProblemWindow::~ProblemWindow()
110 {
111 	if (fDoneSemaphore >= 0)
112 		delete_sem(fDoneSemaphore);
113 }
114 
115 
116 bool
117 ProblemWindow::Go(BSolver* solver, const SolverPackageSet& packagesAddedByUser,
118 	const SolverPackageSet& packagesRemovedByUser)
119 {
120 	AutoLocker<ProblemWindow> locker(this);
121 
122 	fPackagesAddedByUser = &packagesAddedByUser;
123 	fPackagesRemovedByUser = &packagesRemovedByUser;
124 
125 	_ClearProblemsGui();
126 	_AddProblemsGui(solver);
127 
128 	fCancelButton->SetEnabled(true);
129 	fRetryButton->SetEnabled(false);
130 
131 	if (IsHidden()) {
132 		CenterOnScreen();
133 		Show();
134 	}
135 
136 	fAccepted = false;
137 	fClientWaiting = true;
138 
139 	locker.Unlock();
140 
141 	while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) {
142 	}
143 
144 	locker.Lock();
145 	if (!locker.IsLocked() || !fAccepted || !_AnySolutionSelected())
146 		return false;
147 
148 	// set the solutions
149 	for (SolutionMap::const_iterator it = fSolutions.begin();
150 		it != fSolutions.end(); ++it) {
151 		BRadioButton* button = it->first;
152 		if (button->Value() == B_CONTROL_ON) {
153 			const Solution& solution = it->second;
154 			status_t error = solver->SelectProblemSolution(solution.fProblem,
155 				solution.fSolution);
156 			if (error != B_OK)
157 				throw BFatalErrorException(error, "failed to set solution");
158 		}
159 	}
160 
161 	return true;
162 }
163 
164 
165 bool
166 ProblemWindow::QuitRequested()
167 {
168 	if (fClientWaiting) {
169 		fClientWaiting = false;
170 		release_sem(fDoneSemaphore);
171 	}
172 	return true;
173 }
174 
175 
176 void
177 ProblemWindow::MessageReceived(BMessage* message)
178 {
179 	switch (message->what) {
180 		case B_CANCEL:
181 			Hide();
182 			fClientWaiting = false;
183 			release_sem(fDoneSemaphore);
184 			break;
185 		case kRetryMessage:
186 			fCancelButton->SetEnabled(false);
187 			fRetryButton->SetEnabled(false);
188 			fAccepted = true;
189 			fClientWaiting = false;
190 			release_sem(fDoneSemaphore);
191 			break;
192 		case kUpdateRetryButtonMessage:
193 			fRetryButton->SetEnabled(_AnySolutionSelected());
194 			break;
195 		default:
196 			BWindow::MessageReceived(message);
197 			break;
198 	}
199 }
200 
201 
202 void
203 ProblemWindow::_ClearProblemsGui()
204 {
205 	fSolutions.clear();
206 
207 	int32 count = fContainerView->CountChildren();
208 	for (int32 i = count - 1; i >= 0; i--) {
209 		BView* child = fContainerView->ChildAt(i);
210 		fContainerView->RemoveChild(child);
211 		delete child;
212 	}
213 }
214 
215 
216 void
217 ProblemWindow::_AddProblemsGui(BSolver* solver)
218 {
219 	int32 problemCount = solver->CountProblems();
220 	for (int32 i = 0; i < problemCount; i++) {
221 		_AddProblem(solver->ProblemAt(i),
222 			(i & 1) == 0 ? B_NO_TINT : 1.04);
223 	}
224 }
225 
226 
227 void
228 ProblemWindow::_AddProblem(BSolverProblem* problem,
229 	const float backgroundTint)
230 {
231 	BGroupView* problemGroup = new BGroupView(B_VERTICAL);
232 	fContainerView->AddChild(problemGroup);
233 	problemGroup->GroupLayout()->SetInsets(B_USE_SMALL_INSETS);
234 	problemGroup->SetViewUIColor(B_LIST_BACKGROUND_COLOR, backgroundTint);
235 
236 	BStringView* problemView = new BStringView(NULL, problem->ToString());
237 	problemGroup->AddChild(problemView);
238 	BFont problemFont;
239 	problemView->GetFont(&problemFont);
240 	problemFont.SetFace(B_BOLD_FACE);
241 	problemView->SetFont(&problemFont);
242 	problemView->AdoptParentColors();
243 
244 	int32 solutionCount = problem->CountSolutions();
245 	for (int k = 0; k < solutionCount; k++) {
246 		const BSolverProblemSolution* solution = problem->SolutionAt(k);
247 		BRadioButton* solutionButton = new BRadioButton(
248 			BString().SetToFormat(B_TRANSLATE_COMMENT("solution %d:",
249 				"Don't change the %d variable"), k + 1),
250 			new BMessage(kUpdateRetryButtonMessage));
251 		problemGroup->AddChild(solutionButton);
252 
253 		BGroupLayout* elementsGroup = new BGroupLayout(B_VERTICAL);
254 		problemGroup->AddChild(elementsGroup);
255 		elementsGroup->SetInsets(20, 0, 0, 0);
256 
257 		int32 elementCount = solution->CountElements();
258 		for (int32 l = 0; l < elementCount; l++) {
259 			const BSolverProblemSolutionElement* element
260 				= solution->ElementAt(l);
261 			BStringView* elementView = new BStringView(NULL,
262 				BString().SetToFormat("- %s",
263 					_SolutionElementText(element).String()));
264 			elementsGroup->AddView(elementView);
265 			elementView->AdoptParentColors();
266 		}
267 
268 		fSolutions[solutionButton] = Solution(problem, solution);
269 	}
270 
271 	BRadioButton* ignoreButton = new BRadioButton(B_TRANSLATE(
272 		"ignore problem for now"), new BMessage(kUpdateRetryButtonMessage));
273 	problemGroup->AddChild(ignoreButton);
274 	ignoreButton->SetValue(B_CONTROL_ON);
275 }
276 
277 
278 BString
279 ProblemWindow::_SolutionElementText(
280 	const BSolverProblemSolutionElement* element) const
281 {
282 	// Reword text for B_ALLOW_DEINSTALLATION, if the package has been added
283 	// by the user.
284 	BSolverPackage* package = element->SourcePackage();
285 	if (element->Type() == BSolverProblemSolutionElement::B_ALLOW_DEINSTALLATION
286 		&& package != NULL
287 		&& fPackagesAddedByUser->find(package) != fPackagesAddedByUser->end()) {
288 		return BString(B_TRANSLATE_COMMENT("don't activate package %source%",
289 				"don't change '%source%")).ReplaceAll(
290 				"%source%", package->VersionedName());
291 	}
292 
293 	return element->ToString();
294 }
295 
296 
297 bool
298 ProblemWindow::_AnySolutionSelected() const
299 {
300 	for (SolutionMap::const_iterator it = fSolutions.begin();
301 		it != fSolutions.end(); ++it) {
302 		BRadioButton* button = it->first;
303 		if (button->Value() == B_CONTROL_ON)
304 			return true;
305 	}
306 
307 	return false;
308 }
309