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 31static const BString kTitleEnabled = 32 B_TRANSLATE_COMMENT("Status", "Column title"); 33static const BString kTitleName = B_TRANSLATE_COMMENT("Name", "Column title"); 34static const BString kTitleUrl = B_TRANSLATE_COMMENT("URL", "Column title"); 35static const BString kLabelRemove = 36 B_TRANSLATE_COMMENT("Remove", "Button label"); 37static const BString kLabelRemoveAll = 38 B_TRANSLATE_COMMENT("Remove all", "Button label"); 39static const BString kLabelEnable = 40 B_TRANSLATE_COMMENT("Enable", "Button label"); 41static const BString kLabelEnableAll = 42 B_TRANSLATE_COMMENT("Enable all", "Button label"); 43static const BString kLabelDisable = 44 B_TRANSLATE_COMMENT("Disable", "Button label"); 45static const BString kLabelDisableAll = 46 B_TRANSLATE_COMMENT("Disable all", "Button label"); 47static const BString kStatusViewText = 48 B_TRANSLATE_COMMENT("Changes pending:", "Status view text"); 49static const BString kStatusCompletedText = 50 B_TRANSLATE_COMMENT("Changes completed", "Status view text"); 51 52 53RepositoriesListView::RepositoriesListView(const char* name) 54 : 55 BColumnListView(name, B_NAVIGABLE, B_PLAIN_BORDER) 56{ 57} 58 59 60void 61RepositoriesListView::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 74RepositoriesView::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 190RepositoriesView::~RepositoriesView() 191{ 192 if (fTaskLooper) { 193 fTaskLooper->Lock(); 194 fTaskLooper->Quit(); 195 } 196 _EmptyList(); 197} 198 199 200void 201RepositoriesView::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 216void 217RepositoriesView::AttachedToWindow() 218{ 219 fTaskLooper = new TaskLooper(BMessenger(this)); 220} 221 222 223void 224RepositoriesView::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 418void 419RepositoriesView::_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 432void 433RepositoriesView::_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 445void 446RepositoriesView::_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 462void 463RepositoriesView::_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 474void 475RepositoriesView::_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 490void 491RepositoriesView::_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 505void 506RepositoriesView::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 538status_t 539RepositoriesView::_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 551void 552RepositoriesView::_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 572void 573RepositoriesView::_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 587void 588RepositoriesView::_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 621void 622RepositoriesView::_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 636RepoRow* 637RepositoriesView::_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 663void 664RepositoriesView::_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 691void 692RepositoriesView::_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 745void 746RepositoriesView::_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