vfs: check userland buffers before reading them.
[haiku.git] / src / preferences / repositories / RepositoriesView.cpp
blobaa586b86686490e946189bf4a8e6d171ee9a4fa1
1 /*
2 * Copyright 2017 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Brian Hill
7 */
10 #include "RepositoriesView.h"
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>
25 #include "constants.h"
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "RepositoriesView"
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");
53 RepositoriesListView::RepositoriesListView(const char* name)
55 BColumnListView(name, B_NAVIGABLE, B_PLAIN_BORDER)
60 void
61 RepositoriesListView::KeyDown(const char* bytes, int32 numBytes)
63 switch (bytes[0]) {
64 case B_DELETE:
65 Window()->PostMessage(DELETE_KEY_PRESSED);
66 break;
68 default:
69 BColumnListView::KeyDown(bytes, numBytes);
74 RepositoriesView::RepositoriesView()
76 BGroupView("RepositoriesView"),
77 fTaskLooper(NULL),
78 fShowCompletedStatus(false),
79 fRunningTaskCount(0),
80 fLastCompletedTimerId(0)
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));
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);
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);
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);
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));
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);
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);
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();
190 RepositoriesView::~RepositoriesView()
192 if (fTaskLooper) {
193 fTaskLooper->Lock();
194 fTaskLooper->Quit();
196 _EmptyList();
200 void
201 RepositoriesView::AllAttached()
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();
216 void
217 RepositoriesView::AttachedToWindow()
219 fTaskLooper = new TaskLooper(BMessenger(this));
223 void
224 RepositoriesView::MessageReceived(BMessage* message)
226 switch (message->what)
228 case REMOVE_REPOS:
230 RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
231 if (!rowItem || !fRemoveButton->IsEnabled())
232 break;
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");
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");
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));
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;
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;
274 _SaveList();
275 break;
278 case LIST_SELECTION_CHANGED:
279 _UpdateButtons();
280 break;
282 case ITEM_INVOKED:
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);
292 break;
295 case ENABLE_BUTTON_PRESSED:
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));
317 if (paramsOK) {
318 _AddSelectedRowsToQueue();
319 _UpdateButtons();
321 break;
324 case DISABLE_BUTTON_PRESSED:
325 _AddSelectedRowsToQueue();
326 _UpdateButtons();
327 break;
329 case TASK_STARTED:
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;
340 case TASK_COMPLETED_WITH_ERRORS:
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);
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();
360 _UpdateButtons();
361 break;
364 case TASK_COMPLETED:
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();
379 _UpdateButtons();
380 break;
383 case TASK_CANCELED:
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;
398 case UPDATE_LIST:
399 _RefreshList();
400 _UpdateButtons();
401 break;
403 case STATUS_VIEW_COMPLETED_TIMEOUT:
405 int32 timerID;
406 status_t result = message->FindInt32(key_ID, &timerID);
407 if (result == B_OK && timerID == fLastCompletedTimerId)
408 _UpdateStatusView();
409 break;
412 default:
413 BView::MessageReceived(message);
418 void
419 RepositoriesView::_AddSelectedRowsToQueue()
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));
432 void
433 RepositoriesView::_TaskStarted(RepoRow* rowItem, int16 count)
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;
445 void
446 RepositoriesView::_TaskCompleted(RepoRow* rowItem, int16 count, BString& newName)
448 fRunningTaskCount = count;
449 _ShowCompletedStatusIfDone();
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();
458 _UpdateFromRepoConfig(rowItem);
462 void
463 RepositoriesView::_TaskCanceled(RepoRow* rowItem, int16 count)
465 fRunningTaskCount = count;
466 _ShowCompletedStatusIfDone();
468 // Update row state and values
469 rowItem->SetTaskState(STATE_NOT_IN_QUEUE);
470 _UpdateFromRepoConfig(rowItem);
474 void
475 RepositoriesView::_ShowCompletedStatusIfDone()
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();
490 void
491 RepositoriesView::_UpdateFromRepoConfig(RepoRow* rowItem)
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);
505 void
506 RepositoriesView::AddManualRepository(BString url)
508 BUrl newRepoUrl(url);
509 if (!newRepoUrl.IsValid())
510 return;
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;
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();
538 status_t
539 RepositoriesView::_EmptyList()
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);
547 return B_OK;
551 void
552 RepositoriesView::_InitList()
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);
566 _UpdateListFromRoster();
567 fListView->SetSortColumn(fListView->ColumnAt(kUrlColumn), false, true);
568 fListView->ResizeAllColumnsToPreferred();
572 void
573 RepositoriesView::_RefreshList()
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);
582 // Get current list of enabled repositories
583 _UpdateListFromRoster();
587 void
588 RepositoriesView::_UpdateListFromRoster()
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;
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);
616 _FindSiblings();
617 _SaveList();
621 void
622 RepositoriesView::_SaveList()
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());
632 fSettings.SetRepositories(nameList, urlList);
636 RepoRow*
637 RepositoriesView::_AddRepo(BString name, BString url, bool enabled)
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;
657 RepoRow* addedRow = new RepoRow(name, url, enabled);
658 fListView->AddRow(addedRow);
659 return addedRow;
663 void
664 RepositoriesView::_FindSiblings()
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);
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));
691 void
692 RepositoriesView::_UpdateButtons()
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;
709 if (rowItem->IsEnabled())
710 someAreEnabled = true;
711 else
712 someAreDisabled = true;
713 rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
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);
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);
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);
745 void
746 RepositoriesView::_UpdateStatusView()
748 if (fRunningTaskCount) {
749 BString text(kStatusViewText);
750 text.Append(" ");
751 text << fRunningTaskCount;
752 fListStatusView->SetText(text);
753 } else
754 fListStatusView->SetText("");