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 _SaveList(); 457 } 458 _UpdateFromRepoConfig(rowItem); 459 } 460 461 462 void 463 RepositoriesView::_TaskCanceled(RepoRow* rowItem, int16 count) 464 { 465 fRunningTaskCount = count; 466 _ShowCompletedStatusIfDone(); 467 468 // Update row state and values 469 rowItem->SetTaskState(STATE_NOT_IN_QUEUE); 470 _UpdateFromRepoConfig(rowItem); 471 } 472 473 474 void 475 RepositoriesView::_ShowCompletedStatusIfDone() 476 { 477 // If this is the last task show completed status text for 3 seconds 478 if (fRunningTaskCount == 0 && fShowCompletedStatus) { 479 fListStatusView->SetText(kStatusCompletedText); 480 fLastCompletedTimerId = rand(); 481 BMessage timerMessage(STATUS_VIEW_COMPLETED_TIMEOUT); 482 timerMessage.AddInt32(key_ID, fLastCompletedTimerId); 483 new BMessageRunner(this, &timerMessage, 3000000, 1); 484 fShowCompletedStatus = false; 485 } else 486 _UpdateStatusView(); 487 } 488 489 490 void 491 RepositoriesView::_UpdateFromRepoConfig(RepoRow* rowItem) 492 { 493 BPackageKit::BPackageRoster pRoster; 494 BPackageKit::BRepositoryConfig repoConfig; 495 BString repoName(rowItem->Name()); 496 status_t result = pRoster.GetRepositoryConfig(repoName, &repoConfig); 497 // Repo name was found and the URL matches 498 if (result == B_OK && repoConfig.BaseURL() == rowItem->Url()) 499 rowItem->SetEnabled(true); 500 else 501 rowItem->SetEnabled(false); 502 } 503 504 505 void 506 RepositoriesView::AddManualRepository(BString url) 507 { 508 BUrl newRepoUrl(url); 509 if (!newRepoUrl.IsValid()) 510 return; 511 512 BString name(kNewRepoDefaultName); 513 int32 index; 514 int32 listCount = fListView->CountRows(); 515 for (index = 0; index < listCount; index++) { 516 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 517 BUrl rowRepoUrl(repoItem->Url()); 518 // Find an already existing URL 519 if (newRepoUrl == rowRepoUrl) { 520 (new BAlert("duplicate", 521 B_TRANSLATE_COMMENT("This repository URL already exists.", 522 "Error message"), 523 kOKLabel))->Go(NULL); 524 return; 525 } 526 } 527 RepoRow* newRepo = _AddRepo(name, url, false); 528 _FindSiblings(); 529 fListView->DeselectAll(); 530 fListView->AddToSelection(newRepo); 531 _UpdateButtons(); 532 _SaveList(); 533 if (fEnableButton->IsEnabled()) 534 fEnableButton->Invoke(); 535 } 536 537 538 status_t 539 RepositoriesView::_EmptyList() 540 { 541 BRow* row = fListView->RowAt((int32)0, NULL); 542 while (row != NULL) { 543 fListView->RemoveRow(row); 544 delete row; 545 row = fListView->RowAt((int32)0, NULL); 546 } 547 return B_OK; 548 } 549 550 551 void 552 RepositoriesView::_InitList() 553 { 554 // Get list of known repositories from the settings file 555 int32 index, repoCount; 556 BStringList nameList, urlList; 557 status_t result = fSettings.GetRepositories(repoCount, nameList, urlList); 558 if (result == B_OK) { 559 BString name, url; 560 for (index = 0; index < repoCount; index++) { 561 name = nameList.StringAt(index); 562 url = urlList.StringAt(index); 563 _AddRepo(name, url, false); 564 } 565 } 566 _UpdateListFromRoster(); 567 fListView->SetSortColumn(fListView->ColumnAt(kUrlColumn), false, true); 568 fListView->ResizeAllColumnsToPreferred(); 569 } 570 571 572 void 573 RepositoriesView::_RefreshList() 574 { 575 // Clear enabled status on all rows 576 int32 index, listCount = fListView->CountRows(); 577 for (index = 0; index < listCount; index++) { 578 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 579 if (repoItem->TaskState() == STATE_NOT_IN_QUEUE) 580 repoItem->SetEnabled(false); 581 } 582 // Get current list of enabled repositories 583 _UpdateListFromRoster(); 584 } 585 586 587 void 588 RepositoriesView::_UpdateListFromRoster() 589 { 590 // Get list of currently enabled repositories 591 BStringList repositoryNames; 592 BPackageKit::BPackageRoster pRoster; 593 status_t result = pRoster.GetRepositoryNames(repositoryNames); 594 if (result != B_OK) { 595 (new BAlert("error", 596 B_TRANSLATE_COMMENT("Repositories could not retrieve the names of " 597 "the currently enabled repositories.", "Alert error message"), 598 kOKLabel, NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 599 return; 600 } 601 BPackageKit::BRepositoryConfig repoConfig; 602 int16 index, count = repositoryNames.CountStrings(); 603 for (index = 0; index < count; index++) { 604 const BString& repoName = repositoryNames.StringAt(index); 605 result = pRoster.GetRepositoryConfig(repoName, &repoConfig); 606 if (result == B_OK) 607 _AddRepo(repoName, repoConfig.BaseURL(), true); 608 else { 609 BString text(B_TRANSLATE_COMMENT("Error getting repository" 610 " configuration for %name%.", "Alert error message, " 611 "do not translate %name%")); 612 text.ReplaceFirst("%name%", repoName); 613 (new BAlert("error", text, kOKLabel))->Go(NULL); 614 } 615 } 616 _FindSiblings(); 617 _SaveList(); 618 } 619 620 621 void 622 RepositoriesView::_SaveList() 623 { 624 BStringList nameList, urlList; 625 int32 index; 626 int32 listCount = fListView->CountRows(); 627 for (index = 0; index < listCount; index++) { 628 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 629 nameList.Add(repoItem->Name()); 630 urlList.Add(repoItem->Url()); 631 } 632 fSettings.SetRepositories(nameList, urlList); 633 } 634 635 636 RepoRow* 637 RepositoriesView::_AddRepo(BString name, BString url, bool enabled) 638 { 639 // URL must be valid 640 BUrl repoUrl(url); 641 if (!repoUrl.IsValid()) 642 return NULL; 643 int32 index; 644 int32 listCount = fListView->CountRows(); 645 // Find if the repo already exists in list 646 for (index = 0; index < listCount; index++) { 647 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 648 BUrl itemUrl(repoItem->Url()); 649 if (repoUrl == itemUrl) { 650 // update name and enabled values 651 if (name != repoItem->Name()) 652 repoItem->SetName(name.String()); 653 repoItem->SetEnabled(enabled); 654 return repoItem; 655 } 656 } 657 RepoRow* addedRow = new RepoRow(name, url, enabled); 658 fListView->AddRow(addedRow); 659 return addedRow; 660 } 661 662 663 void 664 RepositoriesView::_FindSiblings() 665 { 666 BStringList namesFound, namesWithSiblings; 667 int32 index, listCount = fListView->CountRows(); 668 // Find repository names that are duplicated 669 for (index = 0; index < listCount; index++) { 670 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 671 BString name = repoItem->Name(); 672 // Ignore newly added repos since we don't know the real name yet 673 if (name.Compare(kNewRepoDefaultName)==0) 674 continue; 675 // First time a name is found- no sibling (yet) 676 if (!namesFound.HasString(name)) 677 namesFound.Add(name); 678 // Name was already found once so this name has 2 or more siblings 679 else if (!namesWithSiblings.HasString(name)) 680 namesWithSiblings.Add(name); 681 } 682 // Set sibling values for each row 683 for (index = 0; index < listCount; index++) { 684 RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index)); 685 BString name = repoItem->Name(); 686 repoItem->SetHasSiblings(namesWithSiblings.HasString(name)); 687 } 688 } 689 690 691 void 692 RepositoriesView::_UpdateButtons() 693 { 694 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 695 // At least one row is selected 696 if (rowItem) { 697 bool someAreEnabled = false; 698 bool someAreDisabled = false; 699 bool someAreInQueue = false; 700 int32 selectedCount = 0; 701 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection()); 702 while (rowItem) { 703 selectedCount++; 704 uint32 taskState = rowItem->TaskState(); 705 if ( taskState == STATE_IN_QUEUE_WAITING 706 || taskState == STATE_IN_QUEUE_RUNNING) { 707 someAreInQueue = true; 708 } 709 if (rowItem->IsEnabled()) 710 someAreEnabled = true; 711 else 712 someAreDisabled = true; 713 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem)); 714 } 715 // Change button labels depending on which rows are selected 716 if (selectedCount > 1) { 717 fEnableButton->SetLabel(kLabelEnableAll); 718 fDisableButton->SetLabel(kLabelDisableAll); 719 } else { 720 fEnableButton->SetLabel(kLabelEnable); 721 fDisableButton->SetLabel(kLabelDisable); 722 } 723 // Set which buttons should be enabled 724 fRemoveButton->SetEnabled(!someAreEnabled && !someAreInQueue); 725 if ((someAreEnabled && someAreDisabled) || someAreInQueue) { 726 // there are a mix of enabled and disabled repositories selected 727 fEnableButton->SetEnabled(false); 728 fDisableButton->SetEnabled(false); 729 } else { 730 fEnableButton->SetEnabled(someAreDisabled); 731 fDisableButton->SetEnabled(someAreEnabled); 732 } 733 734 } else { 735 // No selected rows 736 fEnableButton->SetLabel(kLabelEnable); 737 fDisableButton->SetLabel(kLabelDisable); 738 fEnableButton->SetEnabled(false); 739 fDisableButton->SetEnabled(false); 740 fRemoveButton->SetEnabled(false); 741 } 742 } 743 744 745 void 746 RepositoriesView::_UpdateStatusView() 747 { 748 if (fRunningTaskCount) { 749 BString text(kStatusViewText); 750 text.Append(" "); 751 text << fRunningTaskCount; 752 fListStatusView->SetText(text); 753 } else 754 fListStatusView->SetText(""); 755 } 756