1 /* 2 * Copyright 2017 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Brian Hill 7 */ 8 9 10 #include "RepositoriesView.h" 11 12 #include <stdlib.h> 13 #include <Alert.h> 14 #include <Button.h> 15 #include <Catalog.h> 16 #include <ColumnTypes.h> 17 #include <LayoutBuilder.h> 18 #include <MessageRunner.h> 19 #include <ScrollBar.h> 20 #include <SeparatorView.h> 21 #include <Url.h> 22 #include <package/PackageRoster.h> 23 #include <package/RepositoryConfig.h> 24 25 #include "constants.h" 26 27 #undef B_TRANSLATION_CONTEXT 28 #define B_TRANSLATION_CONTEXT "RepositoriesView" 29 30 31 static const BString kTitleEnabled = 32 B_TRANSLATE_COMMENT("Status", "Column title"); 33 static const BString kTitleName = B_TRANSLATE_COMMENT("Name", "Column title"); 34 static const BString kTitleUrl = B_TRANSLATE_COMMENT("URL", "Column title"); 35 static const BString kLabelRemove = 36 B_TRANSLATE_COMMENT("Remove", "Button label"); 37 static const BString kLabelRemoveAll = 38 B_TRANSLATE_COMMENT("Remove All", "Button label"); 39 static const BString kLabelEnable = 40 B_TRANSLATE_COMMENT("Enable", "Button label"); 41 static const BString kLabelEnableAll = 42 B_TRANSLATE_COMMENT("Enable All", "Button label"); 43 static const BString kLabelDisable = 44 B_TRANSLATE_COMMENT("Disable", "Button label"); 45 static const BString kLabelDisableAll = 46 B_TRANSLATE_COMMENT("Disable All", "Button label"); 47 static const BString kStatusViewText = 48 B_TRANSLATE_COMMENT("Changes pending:", "Status view text"); 49 static const BString kStatusCompletedText = 50 B_TRANSLATE_COMMENT("Changes completed", "Status view text"); 51 52 53 RepositoriesListView::RepositoriesListView(const char* name) 54 : 55 BColumnListView(name, B_NAVIGABLE, B_PLAIN_BORDER) 56 { 57 } 58 59 60 void 61 RepositoriesListView::KeyDown(const char* bytes, int32 numBytes) 62 { 63 switch (bytes[0]) { 64 case B_DELETE: 65 Window()->PostMessage(DELETE_KEY_PRESSED); 66 break; 67 68 default: 69 BColumnListView::KeyDown(bytes, numBytes); 70 } 71 } 72 73 74 RepositoriesView::RepositoriesView() 75 : 76 BGroupView("RepositoriesView"), 77 fTaskLooper(NULL), 78 fShowCompletedStatus(false), 79 fRunningTaskCount(0), 80 fLastCompletedTimerId(0) 81 { 82 // Column list view with 3 columns 83 fListView = new RepositoriesListView("list"); 84 fListView->SetSelectionMessage(new BMessage(LIST_SELECTION_CHANGED)); 85 float col0width = be_plain_font->StringWidth(kTitleEnabled) + 15; 86 float col1width = be_plain_font->StringWidth(kTitleName) + 15; 87 float col2width = be_plain_font->StringWidth(kTitleUrl) + 15; 88 fListView->AddColumn(new BStringColumn(kTitleEnabled, col0width, col0width, 89 2 * col0width, B_TRUNCATE_END), kEnabledColumn); 90 fListView->AddColumn(new BStringColumn(kTitleName, 90, col1width, 300, 91 B_TRUNCATE_END), kNameColumn); 92 fListView->AddColumn(new BStringColumn(kTitleUrl, 500, col2width, 5000, 93 B_TRUNCATE_END), kUrlColumn); 94 fListView->SetInvocationMessage(new BMessage(ITEM_INVOKED)); 95 96 // Repository list status view 97 fStatusContainerView = new BView("status", B_SUPPORTS_LAYOUT); 98 BString templateText(kStatusViewText); 99 templateText.Append(" 88"); 100 // Simulate a status text with two digit queue count 101 fListStatusView = new BStringView("status", templateText); 102 103 // Set a smaller fixed font size and slightly lighten text color 104 BFont font(be_plain_font); 105 font.SetSize(10.0f); 106 fListStatusView->SetFont(&font, B_FONT_SIZE); 107 fListStatusView->SetHighUIColor(fListStatusView->HighUIColor(), .9f); 108 109 // Set appropriate explicit view sizes 110 float viewWidth = std::max(fListStatusView->StringWidth(templateText), 111 fListStatusView->StringWidth(kStatusCompletedText)); 112 BSize statusViewSize(viewWidth + 3, B_H_SCROLL_BAR_HEIGHT - 2); 113 fListStatusView->SetExplicitSize(statusViewSize); 114 statusViewSize.height += 1; 115 fStatusContainerView->SetExplicitSize(statusViewSize); 116 BLayoutBuilder::Group<>(fStatusContainerView, B_HORIZONTAL, 0) 117 .Add(new BSeparatorView(B_VERTICAL)) 118 .AddGroup(B_VERTICAL, 0) 119 .AddGlue() 120 .AddGroup(B_HORIZONTAL, 0) 121 .SetInsets(2, 0, 0, 0) 122 .Add(fListStatusView) 123 .AddGlue() 124 .End() 125 .Add(new BSeparatorView(B_HORIZONTAL)) 126 .End() 127 .End(); 128 fListView->AddStatusView(fStatusContainerView); 129 130 // Standard buttons 131 fEnableButton = new BButton(kLabelEnable, 132 new BMessage(ENABLE_BUTTON_PRESSED)); 133 fDisableButton = new BButton(kLabelDisable, 134 new BMessage(DISABLE_BUTTON_PRESSED)); 135 136 // Create buttons with fixed size 137 font_height fontHeight; 138 GetFontHeight(&fontHeight); 139 int16 buttonHeight = int16(fontHeight.ascent + fontHeight.descent + 12); 140 // button size determined by font size 141 BSize btnSize(buttonHeight, buttonHeight); 142 143 fAddButton = new BButton("plus", "+", new BMessage(ADD_REPO_WINDOW)); 144 fAddButton->SetExplicitSize(btnSize); 145 fRemoveButton = new BButton("minus", "-", new BMessage(REMOVE_REPOS)); 146 fRemoveButton->SetExplicitSize(btnSize); 147 148 // Layout 149 int16 buttonSpacing = 1; 150 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 151 .SetInsets(B_USE_WINDOW_SPACING) 152 .AddGroup(B_HORIZONTAL, 0, 0.0) 153 .Add(new BStringView("instruction", B_TRANSLATE_COMMENT("Enable" 154 " repositories to use with package management:", 155 "Label text")), 0.0) 156 .AddGlue() 157 .End() 158 .AddStrut(B_USE_DEFAULT_SPACING) 159 .Add(fListView, 1) 160 .AddGroup(B_HORIZONTAL, 0, 0.0) 161 // Add and Remove buttons 162 .AddGroup(B_VERTICAL, 0, 0.0) 163 .AddGroup(B_HORIZONTAL, 0, 0.0) 164 .Add(new BSeparatorView(B_VERTICAL)) 165 .AddGroup(B_VERTICAL, 0, 0.0) 166 .AddGroup(B_HORIZONTAL, buttonSpacing, 0.0) 167 .SetInsets(buttonSpacing) 168 .Add(fAddButton) 169 .Add(fRemoveButton) 170 .End() 171 .Add(new BSeparatorView(B_HORIZONTAL)) 172 .End() 173 .Add(new BSeparatorView(B_VERTICAL)) 174 .End() 175 .AddGlue() 176 .End() 177 // Enable and Disable buttons 178 .AddGroup(B_HORIZONTAL) 179 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 180 B_USE_DEFAULT_SPACING, 0) 181 .AddGlue() 182 .Add(fEnableButton) 183 .Add(fDisableButton) 184 .End() 185 .End() 186 .End(); 187 } 188 189 190 RepositoriesView::~RepositoriesView() 191 { 192 if (fTaskLooper) { 193 fTaskLooper->Lock(); 194 fTaskLooper->Quit(); 195 } 196 _EmptyList(); 197 } 198 199 200 void 201 RepositoriesView::AllAttached() 202 { 203 BView::AllAttached(); 204 fRemoveButton->SetTarget(this); 205 fEnableButton->SetTarget(this); 206 fDisableButton->SetTarget(this); 207 fListView->SetTarget(this); 208 fRemoveButton->SetEnabled(false); 209 fEnableButton->SetEnabled(false); 210 fDisableButton->SetEnabled(false); 211 _UpdateStatusView(); 212 _InitList(); 213 } 214 215 216 void 217 RepositoriesView::AttachedToWindow() 218 { 219 fTaskLooper = new TaskLooper(BMessenger(this)); 220 } 221 222 223 void 224 RepositoriesView::MessageReceived(BMessage* message) 225 { 226 switch (message->what) 227 { 228 case REMOVE_REPOS: 229 { 230 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 231 if (!rowItem || !fRemoveButton->IsEnabled()) 232 break; 233 234 BString text; 235 // More than one selected row 236 if (fListView->CurrentSelection(rowItem)) { 237 text.SetTo(B_TRANSLATE_COMMENT("Remove these repositories?", 238 "Removal alert confirmation message")); 239 text.Append("\n"); 240 } 241 // Only one selected row 242 else { 243 text.SetTo(B_TRANSLATE_COMMENT("Remove this repository?", 244 "Removal alert confirmation message")); 245 text.Append("\n"); 246 } 247 float minWidth = 0; 248 while (rowItem) { 249 BString repoText; 250 repoText.Append("\n").Append(rowItem->Name()) 251 .Append(" (").Append(rowItem->Url()).Append(")"); 252 minWidth = std::max(minWidth, StringWidth(repoText.String())); 253 text.Append(repoText); 254 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 255 } 256 minWidth = std::min(minWidth, Frame().Width()); 257 // Ensure alert window isn't much larger than the main window 258 BAlert* alert = new BAlert("confirm", text, kRemoveLabel, 259 kCancelLabel, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 260 alert->TextView()->SetExplicitMinSize(BSize(minWidth, B_SIZE_UNSET)); 261 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 262 int32 answer = alert->Go(); 263 // User presses Cancel button 264 if (answer) 265 break; 266 267 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 268 while (rowItem) { 269 RepoRow* oldRow = rowItem; 270 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 271 fListView->RemoveRow(oldRow); 272 delete oldRow; 273 } 274 _SaveList(); 275 break; 276 } 277 278 case LIST_SELECTION_CHANGED: 279 _UpdateButtons(); 280 break; 281 282 case ITEM_INVOKED: 283 { 284 // Simulates pressing whichever is the enabled button 285 if (fEnableButton->IsEnabled()) { 286 BMessage invokeMessage(ENABLE_BUTTON_PRESSED); 287 MessageReceived(&invokeMessage); 288 } else if (fDisableButton->IsEnabled()) { 289 BMessage invokeMessage(DISABLE_BUTTON_PRESSED); 290 MessageReceived(&invokeMessage); 291 } 292 break; 293 } 294 295 case ENABLE_BUTTON_PRESSED: 296 { 297 BStringList names; 298 bool paramsOK = true; 299 // Check if there are multiple selections of the same repository, 300 // pkgman won't like that 301 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 302 while (rowItem) { 303 if (names.HasString(rowItem->Name()) 304 && kNewRepoDefaultName.Compare(rowItem->Name()) != 0) { 305 (new BAlert("duplicate", 306 B_TRANSLATE_COMMENT("Only one URL for each repository can " 307 "be enabled. Please change your selections.", 308 "Error message"), 309 kOKLabel, NULL, NULL, 310 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(NULL); 311 paramsOK = false; 312 break; 313 } else 314 names.Add(rowItem->Name()); 315 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 316 } 317 if (paramsOK) { 318 _AddSelectedRowsToQueue(); 319 _UpdateButtons(); 320 } 321 break; 322 } 323 324 case DISABLE_BUTTON_PRESSED: 325 _AddSelectedRowsToQueue(); 326 _UpdateButtons(); 327 break; 328 329 case TASK_STARTED: 330 { 331 int16 count; 332 status_t result1 = message->FindInt16(key_count, &count); 333 RepoRow* rowItem; 334 status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem); 335 if (result1 == B_OK && result2 == B_OK) 336 _TaskStarted(rowItem, count); 337 break; 338 } 339 340 case TASK_COMPLETED_WITH_ERRORS: 341 { 342 BString errorDetails; 343 status_t result = message->FindString(key_details, &errorDetails); 344 if (result == B_OK) { 345 (new BAlert("error", errorDetails, kOKLabel, NULL, NULL, 346 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(NULL); 347 } 348 BString repoName = message->GetString(key_name, 349 kNewRepoDefaultName.String()); 350 int16 count; 351 status_t result1 = message->FindInt16(key_count, &count); 352 RepoRow* rowItem; 353 status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem); 354 if (result1 == B_OK && result2 == B_OK) { 355 _TaskCompleted(rowItem, count, repoName); 356 // Refresh the enabled status of each row since it is unsure what 357 // caused the error 358 _RefreshList(); 359 } 360 _UpdateButtons(); 361 break; 362 } 363 364 case TASK_COMPLETED: 365 { 366 BString repoName = message->GetString(key_name, 367 kNewRepoDefaultName.String()); 368 int16 count; 369 status_t result1 = message->FindInt16(key_count, &count); 370 RepoRow* rowItem; 371 status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem); 372 if (result1 == B_OK && result2 == B_OK) { 373 _TaskCompleted(rowItem, count, repoName); 374 // If the completed row has siblings then enabling this row may 375 // have disabled one of the other siblings, do full refresh. 376 if (rowItem->HasSiblings() && rowItem->IsEnabled()) 377 _RefreshList(); 378 } 379 _UpdateButtons(); 380 break; 381 } 382 383 case TASK_CANCELED: 384 { 385 int16 count; 386 status_t result1 = message->FindInt16(key_count, &count); 387 RepoRow* rowItem; 388 status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem); 389 if (result1 == B_OK && result2 == B_OK) 390 _TaskCanceled(rowItem, count); 391 // Refresh the enabled status of each row since it is unsure what 392 // caused the cancelation 393 _RefreshList(); 394 _UpdateButtons(); 395 break; 396 } 397 398 case UPDATE_LIST: 399 _RefreshList(); 400 _UpdateButtons(); 401 break; 402 403 case STATUS_VIEW_COMPLETED_TIMEOUT: 404 { 405 int32 timerID; 406 status_t result = message->FindInt32(key_ID, &timerID); 407 if (result == B_OK && timerID == fLastCompletedTimerId) 408 _UpdateStatusView(); 409 break; 410 } 411 412 default: 413 BView::MessageReceived(message); 414 } 415 } 416 417 418 void 419 RepositoriesView::_AddSelectedRowsToQueue() 420 { 421 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 422 while (rowItem) { 423 rowItem->SetTaskState(STATE_IN_QUEUE_WAITING); 424 BMessage taskMessage(DO_TASK); 425 taskMessage.AddPointer(key_rowptr, rowItem); 426 fTaskLooper->PostMessage(&taskMessage); 427 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 428 } 429 } 430 431 432 void 433 RepositoriesView::_TaskStarted(RepoRow* rowItem, int16 count) 434 { 435 fRunningTaskCount = count; 436 rowItem->SetTaskState(STATE_IN_QUEUE_RUNNING); 437 // Only present a status count if there is more than one task in queue 438 if (count > 1) { 439 _UpdateStatusView(); 440 fShowCompletedStatus = true; 441 } 442 } 443 444 445 void 446 RepositoriesView::_TaskCompleted(RepoRow* rowItem, int16 count, BString& newName) 447 { 448 fRunningTaskCount = count; 449 _ShowCompletedStatusIfDone(); 450 451 // Update row state and values 452 rowItem->SetTaskState(STATE_NOT_IN_QUEUE); 453 if (kNewRepoDefaultName.Compare(rowItem->Name()) == 0 454 && newName.Compare("") != 0) { 455 rowItem->SetName(newName.String()); 456 } 457 _UpdateFromRepoConfig(rowItem); 458 } 459 460 461 void 462 RepositoriesView::_TaskCanceled(RepoRow* rowItem, int16 count) 463 { 464 fRunningTaskCount = count; 465 _ShowCompletedStatusIfDone(); 466 467 // Update row state and values 468 rowItem->SetTaskState(STATE_NOT_IN_QUEUE); 469 _UpdateFromRepoConfig(rowItem); 470 } 471 472 473 void 474 RepositoriesView::_ShowCompletedStatusIfDone() 475 { 476 // If this is the last task show completed status text for 3 seconds 477 if (fRunningTaskCount == 0 && fShowCompletedStatus) { 478 fListStatusView->SetText(kStatusCompletedText); 479 fLastCompletedTimerId = rand(); 480 BMessage timerMessage(STATUS_VIEW_COMPLETED_TIMEOUT); 481 timerMessage.AddInt32(key_ID, fLastCompletedTimerId); 482 new BMessageRunner(this, &timerMessage, 3000000, 1); 483 fShowCompletedStatus = false; 484 } else 485 _UpdateStatusView(); 486 } 487 488 489 void 490 RepositoriesView::_UpdateFromRepoConfig(RepoRow* rowItem) 491 { 492 BPackageKit::BPackageRoster pRoster; 493 BPackageKit::BRepositoryConfig repoConfig; 494 BString repoName(rowItem->Name()); 495 status_t result = pRoster.GetRepositoryConfig(repoName, &repoConfig); 496 // Repo name was found and the URL matches 497 if (result == B_OK && repoConfig.BaseURL() == rowItem->Url()) 498 rowItem->SetEnabled(true); 499 else 500 rowItem->SetEnabled(false); 501 } 502 503 504 void 505 RepositoriesView::AddManualRepository(BString url) 506 { 507 BUrl newRepoUrl(url); 508 if (!newRepoUrl.IsValid()) 509 return; 510 511 BString name(kNewRepoDefaultName); 512 BString newPathIdentifier = _GetPathIdentifier(newRepoUrl.Path()); 513 bool foundMatchingRoot = false; 514 int32 index; 515 int32 listCount = fListView->CountRows(); 516 for (index = 0; index < listCount; index++) { 517 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 518 BUrl rowRepoUrl(repoItem->Url()); 519 // Find an already existing URL 520 if (newRepoUrl == rowRepoUrl) { 521 (new BAlert("duplicate", 522 B_TRANSLATE_COMMENT("This repository URL already exists.", 523 "Error message"), 524 kOKLabel))->Go(NULL); 525 return; 526 } 527 // Predict the repo name from another url with matching path root 528 if (!foundMatchingRoot) { 529 BString rowPathIdentifier = _GetPathIdentifier(rowRepoUrl.Path()); 530 if (newPathIdentifier.ICompare(rowPathIdentifier) == 0) { 531 foundMatchingRoot = true; 532 name = repoItem->Name(); 533 } 534 } 535 } 536 RepoRow* newRepo = _AddRepo(name, url, false); 537 _FindSiblings(); 538 fListView->DeselectAll(); 539 fListView->AddToSelection(newRepo); 540 _UpdateButtons(); 541 _SaveList(); 542 } 543 544 545 BString 546 RepositoriesView::_GetPathIdentifier(BString urlPath) 547 { 548 // Find second / 549 int32 index = urlPath.FindFirst("/"); 550 if (index == B_ERROR) 551 return urlPath; 552 index = urlPath.FindFirst("/", index + 1); 553 if (index == B_ERROR) 554 return urlPath; 555 else 556 return urlPath.Truncate(index); 557 } 558 559 560 status_t 561 RepositoriesView::_EmptyList() 562 { 563 BRow* row = fListView->RowAt((int32)0, NULL); 564 while (row != NULL) { 565 fListView->RemoveRow(row); 566 delete row; 567 row = fListView->RowAt((int32)0, NULL); 568 } 569 return B_OK; 570 } 571 572 573 void 574 RepositoriesView::_InitList() 575 { 576 // Get list of known repositories from the settings file 577 int32 index, repoCount; 578 BStringList nameList, urlList; 579 status_t result = fSettings.GetRepositories(repoCount, nameList, urlList); 580 if (result == B_OK) { 581 BString name, url; 582 for (index = 0; index < repoCount; index++) { 583 name = nameList.StringAt(index); 584 url = urlList.StringAt(index); 585 _AddRepo(name, url, false); 586 } 587 } 588 _UpdateListFromRoster(); 589 fListView->SetSortColumn(fListView->ColumnAt(kUrlColumn), false, true); 590 fListView->ResizeAllColumnsToPreferred(); 591 } 592 593 594 void 595 RepositoriesView::_RefreshList() 596 { 597 // Clear enabled status on all rows 598 int32 index, listCount = fListView->CountRows(); 599 for (index = 0; index < listCount; index++) { 600 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 601 if (repoItem->TaskState() == STATE_NOT_IN_QUEUE) 602 repoItem->SetEnabled(false); 603 } 604 // Get current list of enabled repositories 605 _UpdateListFromRoster(); 606 } 607 608 609 void 610 RepositoriesView::_UpdateListFromRoster() 611 { 612 // Get list of currently enabled repositories 613 BStringList repositoryNames; 614 BPackageKit::BPackageRoster pRoster; 615 status_t result = pRoster.GetRepositoryNames(repositoryNames); 616 if (result != B_OK) { 617 (new BAlert("error", 618 B_TRANSLATE_COMMENT("Repositories could not retrieve the names of " 619 "the currently enabled repositories.", "Alert error message"), 620 kOKLabel, NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 621 return; 622 } 623 BPackageKit::BRepositoryConfig repoConfig; 624 int16 index, count = repositoryNames.CountStrings(); 625 for (index = 0; index < count; index++) { 626 const BString& repoName = repositoryNames.StringAt(index); 627 result = pRoster.GetRepositoryConfig(repoName, &repoConfig); 628 if (result == B_OK) 629 _AddRepo(repoName, repoConfig.BaseURL(), true); 630 else { 631 BString text(B_TRANSLATE_COMMENT("Error getting repository" 632 " configuration for %name%.", "Alert error message, " 633 "do not translate %name%")); 634 text.ReplaceFirst("%name%", repoName); 635 (new BAlert("error", text, kOKLabel))->Go(NULL); 636 } 637 } 638 _FindSiblings(); 639 _SaveList(); 640 } 641 642 643 void 644 RepositoriesView::_SaveList() 645 { 646 BStringList nameList, urlList; 647 int32 index; 648 int32 listCount = fListView->CountRows(); 649 for (index = 0; index < listCount; index++) { 650 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 651 nameList.Add(repoItem->Name()); 652 urlList.Add(repoItem->Url()); 653 } 654 fSettings.SetRepositories(nameList, urlList); 655 } 656 657 658 RepoRow* 659 RepositoriesView::_AddRepo(BString name, BString url, bool enabled) 660 { 661 // URL must be valid 662 BUrl repoUrl(url); 663 if (!repoUrl.IsValid()) 664 return NULL; 665 int32 index; 666 int32 listCount = fListView->CountRows(); 667 // Find if the repo already exists in list 668 for (index = 0; index < listCount; index++) { 669 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 670 BUrl itemUrl(repoItem->Url()); 671 if (repoUrl == itemUrl) { 672 // update name and enabled values 673 if (name != repoItem->Name()) 674 repoItem->SetName(name.String()); 675 repoItem->SetEnabled(enabled); 676 return repoItem; 677 } 678 } 679 RepoRow* addedRow = new RepoRow(name, url, enabled); 680 fListView->AddRow(addedRow); 681 return addedRow; 682 } 683 684 685 void 686 RepositoriesView::_FindSiblings() 687 { 688 BStringList namesFound, namesWithSiblings; 689 int32 index, listCount = fListView->CountRows(); 690 // Find repository names that are duplicated 691 for (index = 0; index < listCount; index++) { 692 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 693 BString name = repoItem->Name(); 694 // Ignore newly added repos since we don't know the real name yet 695 if (name.Compare(kNewRepoDefaultName)==0) 696 continue; 697 // First time a name is found- no sibling (yet) 698 if (!namesFound.HasString(name)) 699 namesFound.Add(name); 700 // Name was already found once so this name has 2 or more siblings 701 else if (!namesWithSiblings.HasString(name)) 702 namesWithSiblings.Add(name); 703 } 704 // Set sibling values for each row 705 for (index = 0; index < listCount; index++) { 706 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 707 BString name = repoItem->Name(); 708 repoItem->SetHasSiblings(namesWithSiblings.HasString(name)); 709 } 710 } 711 712 713 void 714 RepositoriesView::_UpdateButtons() 715 { 716 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 717 // At least one row is selected 718 if (rowItem) { 719 bool someAreEnabled = false; 720 bool someAreDisabled = false; 721 bool someAreInQueue = false; 722 int32 selectedCount = 0; 723 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 724 while (rowItem) { 725 selectedCount++; 726 uint32 taskState = rowItem->TaskState(); 727 if ( taskState == STATE_IN_QUEUE_WAITING 728 || taskState == STATE_IN_QUEUE_RUNNING) { 729 someAreInQueue = true; 730 } 731 if (rowItem->IsEnabled()) 732 someAreEnabled = true; 733 else 734 someAreDisabled = true; 735 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 736 } 737 // Change button labels depending on which rows are selected 738 if (selectedCount > 1) { 739 fEnableButton->SetLabel(kLabelEnableAll); 740 fDisableButton->SetLabel(kLabelDisableAll); 741 } else { 742 fEnableButton->SetLabel(kLabelEnable); 743 fDisableButton->SetLabel(kLabelDisable); 744 } 745 // Set which buttons should be enabled 746 fRemoveButton->SetEnabled(!someAreEnabled && !someAreInQueue); 747 if ((someAreEnabled && someAreDisabled) || someAreInQueue) { 748 // there are a mix of enabled and disabled repositories selected 749 fEnableButton->SetEnabled(false); 750 fDisableButton->SetEnabled(false); 751 } else { 752 fEnableButton->SetEnabled(someAreDisabled); 753 fDisableButton->SetEnabled(someAreEnabled); 754 } 755 756 } else { 757 // No selected rows 758 fEnableButton->SetLabel(kLabelEnable); 759 fDisableButton->SetLabel(kLabelDisable); 760 fEnableButton->SetEnabled(false); 761 fDisableButton->SetEnabled(false); 762 fRemoveButton->SetEnabled(false); 763 } 764 } 765 766 767 void 768 RepositoriesView::_UpdateStatusView() 769 { 770 if (fRunningTaskCount) { 771 BString text(kStatusViewText); 772 text.Append(" "); 773 text << fRunningTaskCount; 774 fListStatusView->SetText(text); 775 } else 776 fListStatusView->SetText(""); 777 } 778