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 146 Hide(); 147 148 if (!locker.IsLocked() || !fAccepted || !_AnySolutionSelected()) 149 return false; 150 151 // set the solutions 152 for (SolutionMap::const_iterator it = fSolutions.begin(); 153 it != fSolutions.end(); ++it) { 154 BRadioButton* button = it->first; 155 if (button->Value() == B_CONTROL_ON) { 156 const Solution& solution = it->second; 157 status_t error = solver->SelectProblemSolution(solution.fProblem, 158 solution.fSolution); 159 if (error != B_OK) 160 throw BFatalErrorException(error, "failed to set solution"); 161 } 162 } 163 164 return true; 165 } 166 167 168 bool 169 ProblemWindow::QuitRequested() 170 { 171 if (fClientWaiting) { 172 fClientWaiting = false; 173 release_sem(fDoneSemaphore); 174 } 175 return true; 176 } 177 178 179 void 180 ProblemWindow::MessageReceived(BMessage* message) 181 { 182 switch (message->what) { 183 case B_CANCEL: 184 Hide(); 185 fClientWaiting = false; 186 release_sem(fDoneSemaphore); 187 break; 188 case kRetryMessage: 189 fCancelButton->SetEnabled(false); 190 fRetryButton->SetEnabled(false); 191 fAccepted = true; 192 fClientWaiting = false; 193 release_sem(fDoneSemaphore); 194 break; 195 case kUpdateRetryButtonMessage: 196 fRetryButton->SetEnabled(_AnySolutionSelected()); 197 break; 198 default: 199 BWindow::MessageReceived(message); 200 break; 201 } 202 } 203 204 205 void 206 ProblemWindow::_ClearProblemsGui() 207 { 208 fSolutions.clear(); 209 210 int32 count = fContainerView->CountChildren(); 211 for (int32 i = count - 1; i >= 0; i--) { 212 BView* child = fContainerView->ChildAt(i); 213 fContainerView->RemoveChild(child); 214 delete child; 215 } 216 } 217 218 219 void 220 ProblemWindow::_AddProblemsGui(BSolver* solver) 221 { 222 int32 problemCount = solver->CountProblems(); 223 for (int32 i = 0; i < problemCount; i++) { 224 _AddProblem(solver->ProblemAt(i), 225 (i & 1) == 0 ? B_NO_TINT : 1.04); 226 } 227 } 228 229 230 void 231 ProblemWindow::_AddProblem(BSolverProblem* problem, const float backgroundTint) 232 { 233 BGroupView* problemGroup = new BGroupView(B_VERTICAL); 234 fContainerView->AddChild(problemGroup); 235 problemGroup->GroupLayout()->SetInsets(B_USE_SMALL_INSETS); 236 237 problemGroup->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR, backgroundTint); 238 problemGroup->SetHighUIColor(B_DOCUMENT_TEXT_COLOR); 239 240 BStringView* problemView = new BStringView(NULL, problem->ToString()); 241 problemGroup->AddChild(problemView); 242 problemView->AdoptParentColors(); 243 244 BFont problemFont; 245 problemView->GetFont(&problemFont); 246 problemFont.SetFace(B_BOLD_FACE); 247 problemView->SetFont(&problemFont); 248 249 int32 solutionCount = problem->CountSolutions(); 250 for (int k = 0; k < solutionCount; k++) { 251 const BSolverProblemSolution* solution = problem->SolutionAt(k); 252 BRadioButton* solutionButton = new BRadioButton( 253 BString().SetToFormat(B_TRANSLATE_COMMENT("solution %d:", 254 "Don't change the %d variable"), k + 1), 255 new BMessage(kUpdateRetryButtonMessage)); 256 problemGroup->AddChild(solutionButton); 257 258 BGroupLayout* elementsGroup = new BGroupLayout(B_VERTICAL); 259 problemGroup->AddChild(elementsGroup); 260 elementsGroup->SetInsets(20, 0, 0, 0); 261 262 int32 elementCount = solution->CountElements(); 263 for (int32 l = 0; l < elementCount; l++) { 264 const BSolverProblemSolutionElement* element = solution->ElementAt(l); 265 BStringView* elementView = new BStringView(NULL, 266 BString().SetToFormat("- %s", _SolutionElementText(element).String())); 267 elementsGroup->AddView(elementView); 268 elementView->AdoptParentColors(); 269 } 270 271 fSolutions[solutionButton] = Solution(problem, solution); 272 } 273 274 BRadioButton* ignoreButton = new BRadioButton(B_TRANSLATE( 275 "ignore problem for now"), new BMessage(kUpdateRetryButtonMessage)); 276 problemGroup->AddChild(ignoreButton); 277 ignoreButton->SetValue(B_CONTROL_ON); 278 } 279 280 281 BString 282 ProblemWindow::_SolutionElementText( 283 const BSolverProblemSolutionElement* element) const 284 { 285 // Reword text for B_ALLOW_DEINSTALLATION, if the package has been added 286 // by the user. 287 BSolverPackage* package = element->SourcePackage(); 288 if (element->Type() == BSolverProblemSolutionElement::B_ALLOW_DEINSTALLATION 289 && package != NULL 290 && fPackagesAddedByUser->find(package) != fPackagesAddedByUser->end()) { 291 return BString(B_TRANSLATE_COMMENT("don't activate package %source%", 292 "don't change '%source%")).ReplaceAll( 293 "%source%", package->VersionedName()); 294 } 295 296 return element->ToString(); 297 } 298 299 300 bool 301 ProblemWindow::_AnySolutionSelected() const 302 { 303 for (SolutionMap::const_iterator it = fSolutions.begin(); 304 it != fSolutions.end(); ++it) { 305 BRadioButton* button = it->first; 306 if (button->Value() == B_CONTROL_ON) 307 return true; 308 } 309 310 return false; 311 } 312