Add the new Repositories preflet.
Also includes changes to HaikuDepot to change wording and add the menu item to open the Repositories preflet. Signed-off-by: Augustin Cavalier <[email protected]> Closes #13147.
This commit is contained in:
committed by
Augustin Cavalier
parent
564aac4209
commit
5bf2b6eb74
Binary file not shown.
@@ -46,8 +46,8 @@ FilterView::FilterView()
|
|||||||
fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
|
fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
|
||||||
|
|
||||||
// Construct repository popup
|
// Construct repository popup
|
||||||
BPopUpMenu* repositoryMenu = new BPopUpMenu(B_TRANSLATE("Depot"));
|
BPopUpMenu* repositoryMenu = new BPopUpMenu(B_TRANSLATE("Repository"));
|
||||||
fRepositoryField = new BMenuField("repository", B_TRANSLATE("Depot:"),
|
fRepositoryField = new BMenuField("repository", B_TRANSLATE("Repository:"),
|
||||||
repositoryMenu);
|
repositoryMenu);
|
||||||
|
|
||||||
// Construct search terms field
|
// Construct search terms field
|
||||||
@@ -125,7 +125,7 @@ FilterView::AdoptModel(const Model& model)
|
|||||||
BMenu* repositoryMenu = fRepositoryField->Menu();
|
BMenu* repositoryMenu = fRepositoryField->Menu();
|
||||||
repositoryMenu->RemoveItems(0, repositoryMenu->CountItems(), true);
|
repositoryMenu->RemoveItems(0, repositoryMenu->CountItems(), true);
|
||||||
|
|
||||||
repositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All depots"),
|
repositoryMenu->AddItem(new BMenuItem(B_TRANSLATE("All repositories"),
|
||||||
new BMessage(MSG_DEPOT_SELECTED)));
|
new BMessage(MSG_DEPOT_SELECTED)));
|
||||||
|
|
||||||
repositoryMenu->AddItem(new BSeparatorItem());
|
repositoryMenu->AddItem(new BSeparatorItem());
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <MenuBar.h>
|
#include <MenuBar.h>
|
||||||
#include <MenuItem.h>
|
#include <MenuItem.h>
|
||||||
#include <Messenger.h>
|
#include <Messenger.h>
|
||||||
|
#include <Roster.h>
|
||||||
#include <Screen.h>
|
#include <Screen.h>
|
||||||
#include <ScrollView.h>
|
#include <ScrollView.h>
|
||||||
#include <StringList.h>
|
#include <StringList.h>
|
||||||
@@ -63,7 +64,8 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
MSG_MODEL_WORKER_DONE = 'mmwd',
|
MSG_MODEL_WORKER_DONE = 'mmwd',
|
||||||
MSG_REFRESH_DEPOTS = 'mrdp',
|
MSG_REFRESH_REPOS = 'mrrp',
|
||||||
|
MSG_MANAGE_REPOS = 'mmrp',
|
||||||
MSG_LOG_IN = 'lgin',
|
MSG_LOG_IN = 'lgin',
|
||||||
MSG_LOG_OUT = 'lgot',
|
MSG_LOG_OUT = 'lgot',
|
||||||
MSG_AUTHORIZATION_CHANGED = 'athc',
|
MSG_AUTHORIZATION_CHANGED = 'athc',
|
||||||
@@ -316,10 +318,14 @@ MainWindow::MessageReceived(BMessage* message)
|
|||||||
_StartRefreshWorker(false);
|
_StartRefreshWorker(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MSG_REFRESH_DEPOTS:
|
case MSG_REFRESH_REPOS:
|
||||||
_StartRefreshWorker(true);
|
_StartRefreshWorker(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MSG_MANAGE_REPOS:
|
||||||
|
be_roster->Launch("application/x-vnd.Haiku-Repositories");
|
||||||
|
break;
|
||||||
|
|
||||||
case MSG_LOG_IN:
|
case MSG_LOG_IN:
|
||||||
_OpenLoginWindow(BMessage());
|
_OpenLoginWindow(BMessage());
|
||||||
break;
|
break;
|
||||||
@@ -567,8 +573,10 @@ void
|
|||||||
MainWindow::_BuildMenu(BMenuBar* menuBar)
|
MainWindow::_BuildMenu(BMenuBar* menuBar)
|
||||||
{
|
{
|
||||||
BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
|
BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
|
||||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"),
|
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh repositories"),
|
||||||
new BMessage(MSG_REFRESH_DEPOTS)));
|
new BMessage(MSG_REFRESH_REPOS)));
|
||||||
|
menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"),
|
||||||
|
new BMessage(MSG_MANAGE_REPOS)));
|
||||||
|
|
||||||
menuBar->AddItem(menu);
|
menuBar->AddItem(menu);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ SubInclude HAIKU_TOP src preferences mouse ;
|
|||||||
SubInclude HAIKU_TOP src preferences network ;
|
SubInclude HAIKU_TOP src preferences network ;
|
||||||
SubInclude HAIKU_TOP src preferences notifications ;
|
SubInclude HAIKU_TOP src preferences notifications ;
|
||||||
SubInclude HAIKU_TOP src preferences printers ;
|
SubInclude HAIKU_TOP src preferences printers ;
|
||||||
|
SubInclude HAIKU_TOP src preferences repositories ;
|
||||||
SubInclude HAIKU_TOP src preferences screen ;
|
SubInclude HAIKU_TOP src preferences screen ;
|
||||||
SubInclude HAIKU_TOP src preferences screensaver ;
|
SubInclude HAIKU_TOP src preferences screensaver ;
|
||||||
SubInclude HAIKU_TOP src preferences shortcuts ;
|
SubInclude HAIKU_TOP src preferences shortcuts ;
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "AddRepoWindow.h"
|
||||||
|
|
||||||
|
#include <Alert.h>
|
||||||
|
#include <Application.h>
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <Clipboard.h>
|
||||||
|
#include <LayoutBuilder.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "AddRepoWindow"
|
||||||
|
|
||||||
|
static float sAddWindowWidth = 500.0;
|
||||||
|
|
||||||
|
|
||||||
|
AddRepoWindow::AddRepoWindow(BRect size, const BMessenger& messenger)
|
||||||
|
:
|
||||||
|
BWindow(BRect(0, 0, sAddWindowWidth, 10), "AddWindow", B_MODAL_WINDOW,
|
||||||
|
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
|
||||||
|
fReplyMessenger(messenger)
|
||||||
|
{
|
||||||
|
fText = new BTextControl("text", B_TRANSLATE_COMMENT("Repository URL:",
|
||||||
|
"Text box label"), "", new BMessage(ADD_BUTTON_PRESSED));
|
||||||
|
fAddButton = new BButton(B_TRANSLATE_COMMENT("Add", "Button label"),
|
||||||
|
new BMessage(ADD_BUTTON_PRESSED));
|
||||||
|
fAddButton->MakeDefault(true);
|
||||||
|
fCancelButton = new BButton(kCancelLabel,
|
||||||
|
new BMessage(CANCEL_BUTTON_PRESSED));
|
||||||
|
|
||||||
|
BLayoutBuilder::Group<>(this, B_VERTICAL)
|
||||||
|
.SetInsets(B_USE_WINDOW_SPACING)
|
||||||
|
.Add(fText)
|
||||||
|
.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
|
||||||
|
.AddGlue()
|
||||||
|
.Add(fCancelButton)
|
||||||
|
.Add(fAddButton)
|
||||||
|
.End()
|
||||||
|
.End();
|
||||||
|
_GetClipboardData();
|
||||||
|
fText->MakeFocus();
|
||||||
|
|
||||||
|
// Move to the center of the preflet window
|
||||||
|
CenterIn(size);
|
||||||
|
float widthDifference = size.Width() - Frame().Width();
|
||||||
|
if (widthDifference < 0)
|
||||||
|
MoveBy(widthDifference / 2.0, 0);
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRepoWindow::Quit()
|
||||||
|
{
|
||||||
|
fReplyMessenger.SendMessage(ADD_WINDOW_CLOSED);
|
||||||
|
BWindow::Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRepoWindow::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what)
|
||||||
|
{
|
||||||
|
case CANCEL_BUTTON_PRESSED:
|
||||||
|
if (QuitRequested())
|
||||||
|
Quit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ADD_BUTTON_PRESSED: {
|
||||||
|
BString url(fText->Text());
|
||||||
|
if (url != "") {
|
||||||
|
// URL must have a protocol
|
||||||
|
if (url.FindFirst("://") == B_ERROR) {
|
||||||
|
BAlert* alert = new BAlert("error",
|
||||||
|
B_TRANSLATE_COMMENT("The URL must start with a "
|
||||||
|
"protocol, for example http:// or https://",
|
||||||
|
"Add URL error message"),
|
||||||
|
kOKLabel, NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||||
|
alert->SetFeel(B_MODAL_APP_WINDOW_FEEL);
|
||||||
|
alert->Go(NULL);
|
||||||
|
// Center the alert to this window and move down some
|
||||||
|
alert->CenterIn(Frame());
|
||||||
|
alert->MoveBy(0, kAddWindowOffset);
|
||||||
|
} else {
|
||||||
|
BMessage* addMessage = new BMessage(ADD_REPO_URL);
|
||||||
|
addMessage->AddString(key_url, url);
|
||||||
|
fReplyMessenger.SendMessage(addMessage);
|
||||||
|
Quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BWindow::MessageReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRepoWindow::FrameResized(float newWidth, float newHeight)
|
||||||
|
{
|
||||||
|
sAddWindowWidth = newWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
AddRepoWindow::_GetClipboardData()
|
||||||
|
{
|
||||||
|
if (be_clipboard->Lock()) {
|
||||||
|
const char* string;
|
||||||
|
ssize_t stringLen;
|
||||||
|
BMessage* clip = be_clipboard->Data();
|
||||||
|
clip->FindData("text/plain", B_MIME_TYPE, (const void **)&string,
|
||||||
|
&stringLen);
|
||||||
|
be_clipboard->Unlock();
|
||||||
|
|
||||||
|
// The string must contain a web protocol
|
||||||
|
BString clipString(string, stringLen);
|
||||||
|
int32 ww = clipString.FindFirst("://");
|
||||||
|
if (ww == B_ERROR)
|
||||||
|
return B_ERROR;
|
||||||
|
else
|
||||||
|
fText->SetText(clipString);
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef ADD_REPO_WINDOW_H
|
||||||
|
#define ADD_REPO_WINDOW_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Button.h>
|
||||||
|
#include <TextControl.h>
|
||||||
|
#include <View.h>
|
||||||
|
#include <Window.h>
|
||||||
|
|
||||||
|
|
||||||
|
class AddRepoWindow : public BWindow {
|
||||||
|
public:
|
||||||
|
AddRepoWindow(BRect size,
|
||||||
|
const BMessenger& messenger);
|
||||||
|
virtual void MessageReceived(BMessage*);
|
||||||
|
virtual void Quit();
|
||||||
|
virtual void FrameResized(float newWidth, float newHeight);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BTextControl* fText;
|
||||||
|
BButton* fAddButton;
|
||||||
|
BButton* fCancelButton;
|
||||||
|
BMessenger fReplyMessenger;
|
||||||
|
status_t _GetClipboardData();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
SubDir HAIKU_TOP src preferences repositories ;
|
||||||
|
|
||||||
|
UsePrivateHeaders interface ;
|
||||||
|
UsePrivateHeaders package ;
|
||||||
|
|
||||||
|
Preference Repositories :
|
||||||
|
AddRepoWindow.cpp
|
||||||
|
RepoRow.cpp
|
||||||
|
Repositories.cpp
|
||||||
|
RepositoriesView.cpp
|
||||||
|
RepositoriesWindow.cpp
|
||||||
|
RepositoriesSettings.cpp
|
||||||
|
TaskLooper.cpp
|
||||||
|
TaskTimer.cpp
|
||||||
|
: be package libcolumnlistview.a [ TargetLibstdc++ ] localestub
|
||||||
|
: Repositories.rdef
|
||||||
|
;
|
||||||
|
|
||||||
|
Depends Repositories : libcolumnlistview.a ;
|
||||||
|
|
||||||
|
DoCatalogs Repositories :
|
||||||
|
x-vnd.Haiku-Repositories
|
||||||
|
:
|
||||||
|
AddRepoWindow.cpp
|
||||||
|
constants.h
|
||||||
|
Repositories.cpp
|
||||||
|
RepositoriesView.cpp
|
||||||
|
RepositoriesWindow.cpp
|
||||||
|
TaskLooper.cpp
|
||||||
|
TaskTimer.cpp
|
||||||
|
;
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "RepoRow.h"
|
||||||
|
|
||||||
|
#include <ColumnTypes.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
|
||||||
|
RepoRow::RepoRow(const char* repo_name, const char* repo_url, bool enabled)
|
||||||
|
:
|
||||||
|
BRow(),
|
||||||
|
fName(repo_name),
|
||||||
|
fUrl(repo_url),
|
||||||
|
fEnabled(enabled),
|
||||||
|
fTaskState(STATE_NOT_IN_QUEUE)
|
||||||
|
{
|
||||||
|
SetField(new BStringField(""), kEnabledColumn);
|
||||||
|
SetField(new BStringField(fName.String()), kNameColumn);
|
||||||
|
SetField(new BStringField(fUrl.String()), kUrlColumn);
|
||||||
|
if (enabled)
|
||||||
|
SetEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepoRow::SetName(const char* name)
|
||||||
|
{
|
||||||
|
BStringField* field = (BStringField*)GetField(kNameColumn);
|
||||||
|
field->SetString(name);
|
||||||
|
fName.SetTo(name);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepoRow::SetEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
fEnabled = enabled;
|
||||||
|
RefreshEnabledField();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepoRow::RefreshEnabledField()
|
||||||
|
{
|
||||||
|
BStringField* field = (BStringField*)GetField(kEnabledColumn);
|
||||||
|
if (fTaskState == STATE_NOT_IN_QUEUE)
|
||||||
|
field->SetString(fEnabled ? "\xE2\x9C\x94" : "");
|
||||||
|
else
|
||||||
|
field->SetString(B_UTF8_ELLIPSIS);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepoRow::SetTaskState(uint32 state)
|
||||||
|
{
|
||||||
|
fTaskState = state;
|
||||||
|
RefreshEnabledField();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPO_ROW_H
|
||||||
|
#define REPO_ROW_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <ColumnListView.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kEnabledColumn,
|
||||||
|
kNameColumn,
|
||||||
|
kUrlColumn
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RepoRow : public BRow {
|
||||||
|
public:
|
||||||
|
RepoRow(const char* repo_name,
|
||||||
|
const char* repo_url, bool enabled);
|
||||||
|
|
||||||
|
const char* Name() const { return fName.String(); }
|
||||||
|
void SetName(const char* name);
|
||||||
|
const char* Url() const { return fUrl.String(); }
|
||||||
|
void SetEnabled(bool enabled);
|
||||||
|
void RefreshEnabledField();
|
||||||
|
bool IsEnabled() { return fEnabled; }
|
||||||
|
void SetTaskState(uint32 state);
|
||||||
|
uint32 TaskState() { return fTaskState; }
|
||||||
|
void SetHasSiblings(bool hasSiblings)
|
||||||
|
{ fHasSiblings = hasSiblings; }
|
||||||
|
bool HasSiblings() { return fHasSiblings; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BString fName;
|
||||||
|
BString fUrl;
|
||||||
|
bool fEnabled;
|
||||||
|
uint32 fTaskState;
|
||||||
|
bool fHasSiblings;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Repositories.h"
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "RepositoriesApplication"
|
||||||
|
|
||||||
|
const char* kAppSignature = "application/x-vnd.Haiku-Repositories";
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesApplication::RepositoriesApplication()
|
||||||
|
:
|
||||||
|
BApplication(kAppSignature)
|
||||||
|
{
|
||||||
|
fWindow = new RepositoriesWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
RepositoriesApplication myApp;
|
||||||
|
myApp.Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPOSITORIES_H
|
||||||
|
#define REPOSITORIES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
|
||||||
|
#include "RepositoriesWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesApplication : public BApplication {
|
||||||
|
public:
|
||||||
|
RepositoriesApplication();
|
||||||
|
|
||||||
|
private:
|
||||||
|
RepositoriesWindow* fWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
resource app_signature "application/x-vnd.Haiku-Repositories";
|
||||||
|
|
||||||
|
resource app_name_catalog_entry "x-vnd.Haiku-Repositories:System name:Repositories";
|
||||||
|
|
||||||
|
resource app_flags B_SINGLE_LAUNCH;
|
||||||
|
|
||||||
|
resource app_version {
|
||||||
|
major = 1,
|
||||||
|
middle = 0,
|
||||||
|
minor = 0,
|
||||||
|
|
||||||
|
/* 0 = development 1 = alpha 2 = beta
|
||||||
|
3 = gamma 4 = golden master 5 = final */
|
||||||
|
variety = 2,
|
||||||
|
|
||||||
|
internal = 0,
|
||||||
|
|
||||||
|
short_info = "Repositories",
|
||||||
|
long_info = "Repositories ©2017 Haiku"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
resource vector_icon {
|
||||||
|
$"6E6369660C03010000020006023B9FE037664CBA16573E39B04A01E3449F7E00"
|
||||||
|
$"FFFFFFFFEBEFFF020006023C96323A4D3FBAFC013D5A974B57A549844D00C1CC"
|
||||||
|
$"FFFFFFFFFF02000602BA40DA3C98EBBD5E1FBAEBF04A3DF04AD89600C1CCFFFF"
|
||||||
|
$"FDFDFD0401800500020006023C43C6B9E5E23A85A83CEE414268F44A445900C6"
|
||||||
|
$"D7F5FF6B94DD020006023C71E33A0C78BA15E43C7D2149055549455700E3EDFF"
|
||||||
|
$"FF9EC2FF03003CB0030D29640401740401D30B0A062235224044525A3A5A3139"
|
||||||
|
$"250A04223544465A3139250A04444644525A3A5A310A0422352240445244460A"
|
||||||
|
$"0544544955603C593A593C0A05305E376046513B4E3E510A0622422254325C3E"
|
||||||
|
$"513E402E3A0A0422422254325C32490A04224232493E402E3A0A043249325C3E"
|
||||||
|
$"513E400604672645264426464B284C46120A04010420202B0A00010030202B01"
|
||||||
|
$"178300040A01010120202B0A02010220202B0A03010320202B0A0A010502403F"
|
||||||
|
$"3500000000000040268D4707E6C919420A0501061A403F350000000000004026"
|
||||||
|
$"8D4707E6C9194215FF01178400040A0501061A403F3500000000000040268D47"
|
||||||
|
$"07E6C91942001501178600040A06010702403F3500000000000040268D4707E6"
|
||||||
|
$"C919420A08010902403F3500000000000040268D4707E6C919420A0701080240"
|
||||||
|
$"3F3500000000000040268D4707E6C919420A0B010A000A0B010A2024220A0B01"
|
||||||
|
$"0A2028240A0B010A202C260A0B010A2030280A0B010A20342A0A0B010A20382C"
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "RepositoriesSettings.h"
|
||||||
|
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <StringList.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
const char* settingsFilename = "Repositories_settings";
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesSettings::RepositoriesSettings()
|
||||||
|
{
|
||||||
|
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &fFilePath);
|
||||||
|
if (status == B_OK)
|
||||||
|
status = fFilePath.Append(settingsFilename);
|
||||||
|
BEntry fileEntry(fFilePath.Path());
|
||||||
|
if (!fileEntry.Exists()) {
|
||||||
|
// Create default repos
|
||||||
|
BStringList nameList, urlList;
|
||||||
|
int32 count = (sizeof(kDefaultRepos) / sizeof(Repository));
|
||||||
|
for (int16 index = 0; index < count; index++) {
|
||||||
|
nameList.Add(kDefaultRepos[index].name);
|
||||||
|
urlList.Add(kDefaultRepos[index].url);
|
||||||
|
}
|
||||||
|
SetRepositories(nameList, urlList);
|
||||||
|
}
|
||||||
|
fInitStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BRect
|
||||||
|
RepositoriesSettings::GetFrame()
|
||||||
|
{
|
||||||
|
BMessage settings(_ReadFromFile());
|
||||||
|
BRect frame;
|
||||||
|
status_t status = settings.FindRect(key_frame, &frame);
|
||||||
|
// Set default off screen so it will center itself
|
||||||
|
if (status != B_OK)
|
||||||
|
frame.Set(-10, -10, 750, 300);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesSettings::SetFrame(BRect frame)
|
||||||
|
{
|
||||||
|
BMessage settings(_ReadFromFile());
|
||||||
|
settings.RemoveData(key_frame);
|
||||||
|
settings.AddRect(key_frame, frame);
|
||||||
|
_SaveToFile(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RepositoriesSettings::GetRepositories(int32& repoCount, BStringList& nameList,
|
||||||
|
BStringList& urlList)
|
||||||
|
{
|
||||||
|
BMessage settings(_ReadFromFile());
|
||||||
|
type_code type;
|
||||||
|
int32 count;
|
||||||
|
settings.GetInfo(key_name, &type, &count);
|
||||||
|
|
||||||
|
status_t result = B_OK;
|
||||||
|
int32 index, total = 0;
|
||||||
|
BString foundName, foundUrl;
|
||||||
|
// get each repository and add to lists
|
||||||
|
for (index = 0; index < count; index++) {
|
||||||
|
status_t result1 = settings.FindString(key_name, index, &foundName);
|
||||||
|
status_t result2 = settings.FindString(key_url, index, &foundUrl);
|
||||||
|
if (result1 == B_OK && result2 == B_OK) {
|
||||||
|
nameList.Add(foundName);
|
||||||
|
urlList.Add(foundUrl);
|
||||||
|
total++;
|
||||||
|
} else
|
||||||
|
result = B_ERROR;
|
||||||
|
}
|
||||||
|
repoCount = total;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesSettings::SetRepositories(BStringList& nameList, BStringList& urlList)
|
||||||
|
{
|
||||||
|
BMessage settings(_ReadFromFile());
|
||||||
|
settings.RemoveName(key_name);
|
||||||
|
settings.RemoveName(key_url);
|
||||||
|
|
||||||
|
int32 index, count = nameList.CountStrings();
|
||||||
|
for (index = 0; index < count; index++) {
|
||||||
|
settings.AddString(key_name, nameList.StringAt(index));
|
||||||
|
settings.AddString(key_url, urlList.StringAt(index));
|
||||||
|
}
|
||||||
|
_SaveToFile(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BMessage
|
||||||
|
RepositoriesSettings::_ReadFromFile()
|
||||||
|
{
|
||||||
|
BMessage settings;
|
||||||
|
status_t status = fFile.SetTo(fFilePath.Path(), B_READ_ONLY);
|
||||||
|
if (status == B_OK)
|
||||||
|
status = settings.Unflatten(&fFile);
|
||||||
|
fFile.Unset();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RepositoriesSettings::_SaveToFile(BMessage settings)
|
||||||
|
{
|
||||||
|
status_t status = fFile.SetTo(fFilePath.Path(),
|
||||||
|
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||||
|
if (status == B_OK)
|
||||||
|
status = settings.Flatten(&fFile);
|
||||||
|
fFile.Unset();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPOSITORIES_SETTINGS_H
|
||||||
|
#define REPOSITORIES_SETTINGS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <File.h>
|
||||||
|
#include <Message.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <Point.h>
|
||||||
|
#include <Rect.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <StringList.h>
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesSettings {
|
||||||
|
public:
|
||||||
|
RepositoriesSettings();
|
||||||
|
BRect GetFrame();
|
||||||
|
void SetFrame(BRect frame);
|
||||||
|
status_t GetRepositories(int32& repoCount,
|
||||||
|
BStringList& nameList, BStringList& urlList);
|
||||||
|
void SetRepositories(BStringList& nameList,
|
||||||
|
BStringList& urlList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BPath fFilePath;
|
||||||
|
BFile fFile;
|
||||||
|
status_t fInitStatus;
|
||||||
|
BMessage _ReadFromFile();
|
||||||
|
status_t _SaveToFile(BMessage settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,758 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "RepositoriesView.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <Alert.h>
|
||||||
|
#include <Button.h>
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <ColumnTypes.h>
|
||||||
|
#include <LayoutBuilder.h>
|
||||||
|
#include <MessageRunner.h>
|
||||||
|
#include <ScrollBar.h>
|
||||||
|
#include <SeparatorView.h>
|
||||||
|
#include <package/PackageRoster.h>
|
||||||
|
#include <package/RepositoryConfig.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "RepositoriesView"
|
||||||
|
|
||||||
|
|
||||||
|
static const BString kTitleEnabled =
|
||||||
|
B_TRANSLATE_COMMENT("Enabled", "Column title");
|
||||||
|
static const BString kTitleName = B_TRANSLATE_COMMENT("Name", "Column title");
|
||||||
|
static const BString kTitleUrl = B_TRANSLATE_COMMENT("URL", "Column title");
|
||||||
|
static const BString kLabelRemove =
|
||||||
|
B_TRANSLATE_COMMENT("Remove", "Button label");
|
||||||
|
static const BString kLabelRemoveAll =
|
||||||
|
B_TRANSLATE_COMMENT("Remove All", "Button label");
|
||||||
|
static const BString kLabelEnable =
|
||||||
|
B_TRANSLATE_COMMENT("Enable", "Button label");
|
||||||
|
static const BString kLabelEnableAll =
|
||||||
|
B_TRANSLATE_COMMENT("Enable All", "Button label");
|
||||||
|
static const BString kLabelDisable =
|
||||||
|
B_TRANSLATE_COMMENT("Disable", "Button label");
|
||||||
|
static const BString kLabelDisableAll =
|
||||||
|
B_TRANSLATE_COMMENT("Disable All", "Button label");
|
||||||
|
static const BString kStatusViewText =
|
||||||
|
B_TRANSLATE_COMMENT("Changes pending:", "Status view text");
|
||||||
|
static const BString kStatusCompletedText =
|
||||||
|
B_TRANSLATE_COMMENT("Changes completed", "Status view text");
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesListView::RepositoriesListView(const char* name)
|
||||||
|
:
|
||||||
|
BColumnListView(name, B_NAVIGABLE, B_PLAIN_BORDER)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesListView::KeyDown(const char* bytes, int32 numBytes)
|
||||||
|
{
|
||||||
|
switch (bytes[0]) {
|
||||||
|
case B_DELETE:
|
||||||
|
Window()->PostMessage(DELETE_KEY_PRESSED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BColumnListView::KeyDown(bytes, numBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesView::RepositoriesView()
|
||||||
|
:
|
||||||
|
BGroupView("RepositoriesView"),
|
||||||
|
fTaskLooper(NULL),
|
||||||
|
fShowCompletedStatus(false),
|
||||||
|
fRunningTaskCount(0),
|
||||||
|
fLastCompletedTimerId(0)
|
||||||
|
{
|
||||||
|
// Column list view with 3 columns
|
||||||
|
fListView = new RepositoriesListView("list");
|
||||||
|
fListView->SetSelectionMessage(new BMessage(LIST_SELECTION_CHANGED));
|
||||||
|
float col0width = be_plain_font->StringWidth(kTitleEnabled) + 15;
|
||||||
|
float col1width = be_plain_font->StringWidth(kTitleName) + 15;
|
||||||
|
float col2width = be_plain_font->StringWidth(kTitleUrl) + 15;
|
||||||
|
fListView->AddColumn(new BStringColumn(kTitleEnabled, col0width, col0width,
|
||||||
|
col0width, B_TRUNCATE_END, B_ALIGN_CENTER), kEnabledColumn);
|
||||||
|
fListView->AddColumn(new BStringColumn(kTitleName, 90, col1width, 300,
|
||||||
|
B_TRUNCATE_END), kNameColumn);
|
||||||
|
fListView->AddColumn(new BStringColumn(kTitleUrl, 500, col2width, 5000,
|
||||||
|
B_TRUNCATE_END), kUrlColumn);
|
||||||
|
fListView->SetInvocationMessage(new BMessage(ITEM_INVOKED));
|
||||||
|
|
||||||
|
// Repository list status view
|
||||||
|
fStatusContainerView = new BView("status", B_SUPPORTS_LAYOUT);
|
||||||
|
BString templateText(kStatusViewText);
|
||||||
|
templateText.Append(" 88");
|
||||||
|
// Simulate a status text with two digit queue count
|
||||||
|
fListStatusView = new BStringView("status", templateText);
|
||||||
|
|
||||||
|
// Set a smaller fixed font size and slightly lighten text color
|
||||||
|
BFont font(be_plain_font);
|
||||||
|
font.SetSize(10.0f);
|
||||||
|
fListStatusView->SetFont(&font, B_FONT_SIZE);
|
||||||
|
fListStatusView->SetHighUIColor(fListStatusView->HighUIColor(), .9f);
|
||||||
|
|
||||||
|
// Set appropriate explicit view sizes
|
||||||
|
float viewWidth = max(fListStatusView->StringWidth(templateText),
|
||||||
|
fListStatusView->StringWidth(kStatusCompletedText));
|
||||||
|
BSize statusViewSize(viewWidth + 3, B_H_SCROLL_BAR_HEIGHT - 2);
|
||||||
|
fListStatusView->SetExplicitSize(statusViewSize);
|
||||||
|
statusViewSize.height += 1;
|
||||||
|
fStatusContainerView->SetExplicitSize(statusViewSize);
|
||||||
|
BLayoutBuilder::Group<>(fStatusContainerView, B_HORIZONTAL, 0)
|
||||||
|
.Add(new BSeparatorView(B_VERTICAL))
|
||||||
|
.AddGroup(B_VERTICAL, 0)
|
||||||
|
.AddGlue()
|
||||||
|
.AddGroup(B_HORIZONTAL, 0)
|
||||||
|
.SetInsets(2, 0, 0, 0)
|
||||||
|
.Add(fListStatusView)
|
||||||
|
.AddGlue()
|
||||||
|
.End()
|
||||||
|
.Add(new BSeparatorView(B_HORIZONTAL))
|
||||||
|
.End()
|
||||||
|
.End();
|
||||||
|
fListView->AddStatusView(fStatusContainerView);
|
||||||
|
|
||||||
|
// Standard buttons
|
||||||
|
fEnableButton = new BButton(kLabelEnable,
|
||||||
|
new BMessage(ENABLE_BUTTON_PRESSED));
|
||||||
|
fDisableButton = new BButton(kLabelDisable,
|
||||||
|
new BMessage(DISABLE_BUTTON_PRESSED));
|
||||||
|
|
||||||
|
// Create buttons with fixed size
|
||||||
|
font_height fontHeight;
|
||||||
|
GetFontHeight(&fontHeight);
|
||||||
|
int16 buttonHeight = int16(fontHeight.ascent + fontHeight.descent + 12);
|
||||||
|
// button size determined by font size
|
||||||
|
BSize btnSize(buttonHeight, buttonHeight);
|
||||||
|
|
||||||
|
fAddButton = new BButton("plus", "+", new BMessage(ADD_REPO_WINDOW));
|
||||||
|
fAddButton->SetExplicitSize(btnSize);
|
||||||
|
fRemoveButton = new BButton("minus", "-", new BMessage(REMOVE_REPOS));
|
||||||
|
fRemoveButton->SetExplicitSize(btnSize);
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
int16 buttonSpacing = 1;
|
||||||
|
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
|
||||||
|
.SetInsets(B_USE_WINDOW_SPACING)
|
||||||
|
.AddGroup(B_HORIZONTAL, 0, 0.0)
|
||||||
|
.Add(new BStringView("instruction", B_TRANSLATE_COMMENT("Select"
|
||||||
|
" repositories to use with Haiku package management:",
|
||||||
|
"Label text")), 0.0)
|
||||||
|
.AddGlue()
|
||||||
|
.End()
|
||||||
|
.AddStrut(B_USE_DEFAULT_SPACING)
|
||||||
|
.Add(fListView, 1)
|
||||||
|
.AddGroup(B_HORIZONTAL, 0, 0.0)
|
||||||
|
// Add and Remove buttons
|
||||||
|
.AddGroup(B_VERTICAL, 0, 0.0)
|
||||||
|
.AddGroup(B_HORIZONTAL, 0, 0.0)
|
||||||
|
.Add(new BSeparatorView(B_VERTICAL))
|
||||||
|
.AddGroup(B_VERTICAL, 0, 0.0)
|
||||||
|
.AddGroup(B_HORIZONTAL, buttonSpacing, 0.0)
|
||||||
|
.SetInsets(buttonSpacing)
|
||||||
|
.Add(fAddButton)
|
||||||
|
.Add(fRemoveButton)
|
||||||
|
.End()
|
||||||
|
.Add(new BSeparatorView(B_HORIZONTAL))
|
||||||
|
.End()
|
||||||
|
.Add(new BSeparatorView(B_VERTICAL))
|
||||||
|
.End()
|
||||||
|
.AddGlue()
|
||||||
|
.End()
|
||||||
|
// Enable and Disable buttons
|
||||||
|
.AddGroup(B_HORIZONTAL)
|
||||||
|
.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
|
||||||
|
B_USE_DEFAULT_SPACING, 0)
|
||||||
|
.AddGlue()
|
||||||
|
.Add(fEnableButton)
|
||||||
|
.Add(fDisableButton)
|
||||||
|
.End()
|
||||||
|
.End()
|
||||||
|
.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesView::~RepositoriesView()
|
||||||
|
{
|
||||||
|
if (fTaskLooper) {
|
||||||
|
fTaskLooper->Lock();
|
||||||
|
fTaskLooper->Quit();
|
||||||
|
}
|
||||||
|
_EmptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::AllAttached()
|
||||||
|
{
|
||||||
|
BView::AllAttached();
|
||||||
|
fRemoveButton->SetTarget(this);
|
||||||
|
fEnableButton->SetTarget(this);
|
||||||
|
fDisableButton->SetTarget(this);
|
||||||
|
fListView->SetTarget(this);
|
||||||
|
fRemoveButton->SetEnabled(false);
|
||||||
|
fEnableButton->SetEnabled(false);
|
||||||
|
fDisableButton->SetEnabled(false);
|
||||||
|
_UpdateStatusView();
|
||||||
|
_InitList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::AttachedToWindow()
|
||||||
|
{
|
||||||
|
fTaskLooper = new TaskLooper(BMessenger(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what)
|
||||||
|
{
|
||||||
|
case REMOVE_REPOS: {
|
||||||
|
RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
if (!rowItem || !fRemoveButton->IsEnabled())
|
||||||
|
break;
|
||||||
|
|
||||||
|
BString text;
|
||||||
|
// More than one selected row
|
||||||
|
if (fListView->CurrentSelection(rowItem)) {
|
||||||
|
text.SetTo(B_TRANSLATE_COMMENT("Remove these repositories?",
|
||||||
|
"Removal alert confirmation message"));
|
||||||
|
text.Append("\n");
|
||||||
|
}
|
||||||
|
// Only one selected row
|
||||||
|
else {
|
||||||
|
text.SetTo(B_TRANSLATE_COMMENT("Remove this repository?",
|
||||||
|
"Removal alert confirmation message"));
|
||||||
|
text.Append("\n");
|
||||||
|
}
|
||||||
|
float minWidth = 0;
|
||||||
|
while (rowItem) {
|
||||||
|
BString repoText;
|
||||||
|
repoText.Append("\n").Append(rowItem->Name())
|
||||||
|
.Append(" (").Append(rowItem->Url()).Append(")");
|
||||||
|
minWidth = max(minWidth, StringWidth(repoText.String()));
|
||||||
|
text.Append(repoText);
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
|
||||||
|
}
|
||||||
|
minWidth = min(minWidth, Frame().Width());
|
||||||
|
// Ensure alert window isn't much larger than the main window
|
||||||
|
BAlert* alert = new BAlert("confirm", text, kRemoveLabel,
|
||||||
|
kCancelLabel, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||||
|
alert->TextView()->SetExplicitMinSize(BSize(minWidth, B_SIZE_UNSET));
|
||||||
|
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
|
||||||
|
int32 answer = alert->Go();
|
||||||
|
// User presses Cancel button
|
||||||
|
if (answer)
|
||||||
|
break;
|
||||||
|
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
while (rowItem) {
|
||||||
|
RepoRow* oldRow = rowItem;
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
|
||||||
|
fListView->RemoveRow(oldRow);
|
||||||
|
delete oldRow;
|
||||||
|
}
|
||||||
|
_SaveList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LIST_SELECTION_CHANGED:
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ITEM_INVOKED: {
|
||||||
|
// Simulates pressing whichever is the enabled button
|
||||||
|
if (fEnableButton->IsEnabled()) {
|
||||||
|
BMessage invokeMessage(ENABLE_BUTTON_PRESSED);
|
||||||
|
MessageReceived(&invokeMessage);
|
||||||
|
} else if (fDisableButton->IsEnabled()) {
|
||||||
|
BMessage invokeMessage(DISABLE_BUTTON_PRESSED);
|
||||||
|
MessageReceived(&invokeMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ENABLE_BUTTON_PRESSED: {
|
||||||
|
BStringList names;
|
||||||
|
bool paramsOK = true;
|
||||||
|
// Check if there are multiple selections of the same repository,
|
||||||
|
// pkgman won't like that
|
||||||
|
RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
while (rowItem) {
|
||||||
|
if (names.HasString(rowItem->Name())
|
||||||
|
&& kNewRepoDefaultName.Compare(rowItem->Name()) != 0) {
|
||||||
|
(new BAlert("duplicate",
|
||||||
|
B_TRANSLATE_COMMENT("Only one URL for each repository can "
|
||||||
|
"be enabled. Please change your selections.",
|
||||||
|
"Error message"),
|
||||||
|
kOKLabel, NULL, NULL,
|
||||||
|
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(NULL);
|
||||||
|
paramsOK = false;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
names.Add(rowItem->Name());
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
|
||||||
|
}
|
||||||
|
if (paramsOK) {
|
||||||
|
_AddSelectedRowsToQueue();
|
||||||
|
_UpdateButtons();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DISABLE_BUTTON_PRESSED:
|
||||||
|
_AddSelectedRowsToQueue();
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TASK_STARTED: {
|
||||||
|
int16 count;
|
||||||
|
status_t result1 = message->FindInt16(key_count, &count);
|
||||||
|
RepoRow* rowItem;
|
||||||
|
status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem);
|
||||||
|
if (result1 == B_OK && result2 == B_OK)
|
||||||
|
_TaskStarted(rowItem, count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TASK_COMPLETED_WITH_ERRORS: {
|
||||||
|
BString errorDetails;
|
||||||
|
status_t result = message->FindString(key_details, &errorDetails);
|
||||||
|
if (result == B_OK) {
|
||||||
|
(new BAlert("error", errorDetails, kOKLabel, NULL, NULL,
|
||||||
|
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(NULL);
|
||||||
|
}
|
||||||
|
BString repoName = message->GetString(key_name,
|
||||||
|
kNewRepoDefaultName.String());
|
||||||
|
int16 count;
|
||||||
|
status_t result1 = message->FindInt16(key_count, &count);
|
||||||
|
RepoRow* rowItem;
|
||||||
|
status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem);
|
||||||
|
if (result1 == B_OK && result2 == B_OK) {
|
||||||
|
_TaskCompleted(rowItem, count, repoName);
|
||||||
|
// Refresh the enabled status of each row since it is unsure what
|
||||||
|
// caused the error
|
||||||
|
_RefreshList();
|
||||||
|
}
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TASK_COMPLETED: {
|
||||||
|
BString repoName = message->GetString(key_name,
|
||||||
|
kNewRepoDefaultName.String());
|
||||||
|
int16 count;
|
||||||
|
status_t result1 = message->FindInt16(key_count, &count);
|
||||||
|
RepoRow* rowItem;
|
||||||
|
status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem);
|
||||||
|
if (result1 == B_OK && result2 == B_OK) {
|
||||||
|
_TaskCompleted(rowItem, count, repoName);
|
||||||
|
// If the completed row has siblings then enabling this row may
|
||||||
|
// have disabled one of the other siblings, do full refresh.
|
||||||
|
if (rowItem->HasSiblings() && rowItem->IsEnabled())
|
||||||
|
_RefreshList();
|
||||||
|
}
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TASK_CANCELED: {
|
||||||
|
int16 count;
|
||||||
|
status_t result1 = message->FindInt16(key_count, &count);
|
||||||
|
RepoRow* rowItem;
|
||||||
|
status_t result2 = message->FindPointer(key_rowptr, (void**)&rowItem);
|
||||||
|
if (result1 == B_OK && result2 == B_OK)
|
||||||
|
_TaskCanceled(rowItem, count);
|
||||||
|
// Refresh the enabled status of each row since it is unsure what
|
||||||
|
// caused the cancelation
|
||||||
|
_RefreshList();
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UPDATE_LIST:
|
||||||
|
_RefreshList();
|
||||||
|
_UpdateButtons();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATUS_VIEW_COMPLETED_TIMEOUT: {
|
||||||
|
int32 timerID;
|
||||||
|
status_t result = message->FindInt32(key_ID, &timerID);
|
||||||
|
if (result == B_OK && timerID == fLastCompletedTimerId)
|
||||||
|
_UpdateStatusView();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BView::MessageReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_AddSelectedRowsToQueue()
|
||||||
|
{
|
||||||
|
RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
while (rowItem) {
|
||||||
|
rowItem->SetTaskState(STATE_IN_QUEUE_WAITING);
|
||||||
|
BMessage taskMessage(DO_TASK);
|
||||||
|
taskMessage.AddPointer(key_rowptr, rowItem);
|
||||||
|
fTaskLooper->PostMessage(&taskMessage);
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_TaskStarted(RepoRow* rowItem, int16 count)
|
||||||
|
{
|
||||||
|
fRunningTaskCount = count;
|
||||||
|
rowItem->SetTaskState(STATE_IN_QUEUE_RUNNING);
|
||||||
|
// Only present a status count if there is more than one task in queue
|
||||||
|
if (count > 1) {
|
||||||
|
_UpdateStatusView();
|
||||||
|
fShowCompletedStatus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_TaskCompleted(RepoRow* rowItem, int16 count, BString& newName)
|
||||||
|
{
|
||||||
|
fRunningTaskCount = count;
|
||||||
|
_ShowCompletedStatusIfDone();
|
||||||
|
|
||||||
|
// Update row state and values
|
||||||
|
rowItem->SetTaskState(STATE_NOT_IN_QUEUE);
|
||||||
|
if (kNewRepoDefaultName.Compare(rowItem->Name()) == 0
|
||||||
|
&& newName.Compare("") != 0)
|
||||||
|
rowItem->SetName(newName.String());
|
||||||
|
_UpdateFromRepoConfig(rowItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_TaskCanceled(RepoRow* rowItem, int16 count)
|
||||||
|
{
|
||||||
|
fRunningTaskCount = count;
|
||||||
|
_ShowCompletedStatusIfDone();
|
||||||
|
|
||||||
|
// Update row state and values
|
||||||
|
rowItem->SetTaskState(STATE_NOT_IN_QUEUE);
|
||||||
|
_UpdateFromRepoConfig(rowItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_ShowCompletedStatusIfDone()
|
||||||
|
{
|
||||||
|
// If this is the last task show completed status text for 3 seconds
|
||||||
|
if (fRunningTaskCount == 0 && fShowCompletedStatus) {
|
||||||
|
fListStatusView->SetText(kStatusCompletedText);
|
||||||
|
fLastCompletedTimerId = rand();
|
||||||
|
BMessage timerMessage(STATUS_VIEW_COMPLETED_TIMEOUT);
|
||||||
|
timerMessage.AddInt32(key_ID, fLastCompletedTimerId);
|
||||||
|
new BMessageRunner(this, &timerMessage, 3000000, 1);
|
||||||
|
fShowCompletedStatus = false;
|
||||||
|
} else
|
||||||
|
_UpdateStatusView();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_UpdateFromRepoConfig(RepoRow* rowItem)
|
||||||
|
{
|
||||||
|
BPackageKit::BPackageRoster pRoster;
|
||||||
|
BPackageKit::BRepositoryConfig repoConfig;
|
||||||
|
BString repoName(rowItem->Name());
|
||||||
|
status_t result = pRoster.GetRepositoryConfig(repoName, &repoConfig);
|
||||||
|
// Repo name was found and the URL matches
|
||||||
|
if (result == B_OK && repoConfig.BaseURL() == rowItem->Url())
|
||||||
|
rowItem->SetEnabled(true);
|
||||||
|
else
|
||||||
|
rowItem->SetEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::AddManualRepository(BString url)
|
||||||
|
{
|
||||||
|
BString name(kNewRepoDefaultName);
|
||||||
|
BString rootUrl = _GetRootUrl(url);
|
||||||
|
bool foundRoot = false;
|
||||||
|
int32 index;
|
||||||
|
int32 listCount = fListView->CountRows();
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
const char* urlPtr = repoItem->Url();
|
||||||
|
// Find an already existing URL
|
||||||
|
if (url.ICompare(urlPtr) == 0) {
|
||||||
|
(new BAlert("duplicate",
|
||||||
|
B_TRANSLATE_COMMENT("This repository URL already exists.",
|
||||||
|
"Error message"),
|
||||||
|
kOKLabel))->Go(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Use the same name from another repo with the same root url
|
||||||
|
if (foundRoot == false && rootUrl.ICompare(urlPtr,
|
||||||
|
rootUrl.Length()) == 0) {
|
||||||
|
foundRoot = true;
|
||||||
|
name = repoItem->Name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepoRow* newRepo = _AddRepo(name, url, false);
|
||||||
|
_FindSiblings();
|
||||||
|
fListView->DeselectAll();
|
||||||
|
fListView->AddToSelection(newRepo);
|
||||||
|
_UpdateButtons();
|
||||||
|
_SaveList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
RepositoriesView::_GetRootUrl(BString url)
|
||||||
|
{
|
||||||
|
// Find the protocol if it exists
|
||||||
|
int32 ww = url.FindFirst("://");
|
||||||
|
if (ww == B_ERROR)
|
||||||
|
ww = 0;
|
||||||
|
else
|
||||||
|
ww += 3;
|
||||||
|
// Find second /
|
||||||
|
int32 rootEnd = url.FindFirst("/", ww + 1);
|
||||||
|
if (rootEnd == B_ERROR)
|
||||||
|
return url;
|
||||||
|
rootEnd = url.FindFirst("/", rootEnd + 1);
|
||||||
|
if (rootEnd == B_ERROR)
|
||||||
|
return url;
|
||||||
|
else
|
||||||
|
return url.Truncate(rootEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RepositoriesView::_EmptyList()
|
||||||
|
{
|
||||||
|
BRow* row;
|
||||||
|
while ((row = fListView->RowAt((int32)0, NULL)) != NULL) {
|
||||||
|
fListView->RemoveRow(row);
|
||||||
|
delete row;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_InitList()
|
||||||
|
{
|
||||||
|
// Get list of known repositories from the settings file
|
||||||
|
int32 index, repoCount;
|
||||||
|
BStringList nameList, urlList;
|
||||||
|
status_t result = fSettings.GetRepositories(repoCount, nameList, urlList);
|
||||||
|
if (result == B_OK) {
|
||||||
|
BString name, url;
|
||||||
|
for (index = 0; index < repoCount; index++) {
|
||||||
|
name = nameList.StringAt(index);
|
||||||
|
url = urlList.StringAt(index);
|
||||||
|
_AddRepo(name, url, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_UpdateListFromRoster();
|
||||||
|
fListView->SetSortColumn(fListView->ColumnAt(kUrlColumn), false, true);
|
||||||
|
fListView->ResizeAllColumnsToPreferred();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_RefreshList()
|
||||||
|
{
|
||||||
|
// Clear enabled status on all rows
|
||||||
|
int32 index, listCount = fListView->CountRows();
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
if (repoItem->TaskState() == STATE_NOT_IN_QUEUE)
|
||||||
|
repoItem->SetEnabled(false);
|
||||||
|
}
|
||||||
|
// Get current list of enabled repositories
|
||||||
|
_UpdateListFromRoster();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_UpdateListFromRoster()
|
||||||
|
{
|
||||||
|
// Get list of currently enabled repositories
|
||||||
|
BStringList repositoryNames;
|
||||||
|
BPackageKit::BPackageRoster pRoster;
|
||||||
|
status_t result = pRoster.GetRepositoryNames(repositoryNames);
|
||||||
|
if (result != B_OK) {
|
||||||
|
(new BAlert("error",
|
||||||
|
B_TRANSLATE_COMMENT("Repositories could not retrieve the names of "
|
||||||
|
"the currently enabled repositories.", "Alert error message"),
|
||||||
|
kOKLabel, NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BPackageKit::BRepositoryConfig repoConfig;
|
||||||
|
int16 index, count = repositoryNames.CountStrings();
|
||||||
|
for (index = 0; index < count; index++) {
|
||||||
|
const BString& repoName = repositoryNames.StringAt(index);
|
||||||
|
result = pRoster.GetRepositoryConfig(repoName, &repoConfig);
|
||||||
|
if (result == B_OK)
|
||||||
|
_AddRepo(repoName, repoConfig.BaseURL(), true);
|
||||||
|
else {
|
||||||
|
BString text(B_TRANSLATE_COMMENT("Error getting repository"
|
||||||
|
" configuration for %name%.", "Alert error message, "
|
||||||
|
"do not translate %name%"));
|
||||||
|
text.ReplaceFirst("%name%", repoName);
|
||||||
|
(new BAlert("error", text, kOKLabel))->Go(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_FindSiblings();
|
||||||
|
_SaveList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_SaveList()
|
||||||
|
{
|
||||||
|
BStringList nameList, urlList;
|
||||||
|
int32 index;
|
||||||
|
int32 listCount = fListView->CountRows();
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
nameList.Add(repoItem->Name());
|
||||||
|
urlList.Add(repoItem->Url());
|
||||||
|
}
|
||||||
|
fSettings.SetRepositories(nameList, urlList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RepoRow*
|
||||||
|
RepositoriesView::_AddRepo(BString name, BString url, bool enabled)
|
||||||
|
{
|
||||||
|
// URL must have a protocol
|
||||||
|
if (url.FindFirst("://") == B_ERROR)
|
||||||
|
return NULL;
|
||||||
|
RepoRow* addedRow = NULL;
|
||||||
|
int32 index;
|
||||||
|
int32 listCount = fListView->CountRows();
|
||||||
|
// Find if the repo already exists in list
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
if (url.ICompare(repoItem->Url()) == 0) {
|
||||||
|
// update name and enabled values
|
||||||
|
if (name.Compare(repoItem->Name()) != 0)
|
||||||
|
repoItem->SetName(name.String());
|
||||||
|
repoItem->SetEnabled(enabled);
|
||||||
|
addedRow = repoItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addedRow == NULL) {
|
||||||
|
addedRow = new RepoRow(name, url, enabled);
|
||||||
|
fListView->AddRow(addedRow);
|
||||||
|
}
|
||||||
|
return addedRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_FindSiblings()
|
||||||
|
{
|
||||||
|
BStringList namesFound, namesWithSiblings;
|
||||||
|
int32 index, listCount = fListView->CountRows();
|
||||||
|
// Find repository names that are duplicated
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
BString name = repoItem->Name();
|
||||||
|
// Ignore newly added repos since we don't know the real name yet
|
||||||
|
if (name.Compare(kNewRepoDefaultName)==0)
|
||||||
|
continue;
|
||||||
|
// First time a name is found- no sibling (yet)
|
||||||
|
if (!namesFound.HasString(name))
|
||||||
|
namesFound.Add(name);
|
||||||
|
// Name was already found once so this name has 2 or more siblings
|
||||||
|
else if (!namesWithSiblings.HasString(name))
|
||||||
|
namesWithSiblings.Add(name);
|
||||||
|
}
|
||||||
|
// Set sibling values for each row
|
||||||
|
for (index = 0; index < listCount; index++) {
|
||||||
|
RepoRow* repoItem = dynamic_cast<RepoRow*>(fListView->RowAt(index));
|
||||||
|
BString name = repoItem->Name();
|
||||||
|
repoItem->SetHasSiblings(namesWithSiblings.HasString(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_UpdateButtons()
|
||||||
|
{
|
||||||
|
RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
// At least one row is selected
|
||||||
|
if (rowItem) {
|
||||||
|
bool someAreEnabled = false,
|
||||||
|
someAreDisabled = false,
|
||||||
|
someAreInQueue = false;
|
||||||
|
int32 selectedCount = 0;
|
||||||
|
RepoRow* rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection());
|
||||||
|
while (rowItem) {
|
||||||
|
selectedCount++;
|
||||||
|
uint32 taskState = rowItem->TaskState();
|
||||||
|
if ( taskState == STATE_IN_QUEUE_WAITING
|
||||||
|
|| taskState == STATE_IN_QUEUE_RUNNING)
|
||||||
|
someAreInQueue = true;
|
||||||
|
if (rowItem->IsEnabled())
|
||||||
|
someAreEnabled = true;
|
||||||
|
else
|
||||||
|
someAreDisabled = true;
|
||||||
|
rowItem = dynamic_cast<RepoRow*>(fListView->CurrentSelection(rowItem));
|
||||||
|
}
|
||||||
|
// Change button labels depending on which rows are selected
|
||||||
|
if (selectedCount > 1) {
|
||||||
|
fEnableButton->SetLabel(kLabelEnableAll);
|
||||||
|
fDisableButton->SetLabel(kLabelDisableAll);
|
||||||
|
} else {
|
||||||
|
fEnableButton->SetLabel(kLabelEnable);
|
||||||
|
fDisableButton->SetLabel(kLabelDisable);
|
||||||
|
}
|
||||||
|
// Set which buttons should be enabled
|
||||||
|
fRemoveButton->SetEnabled(!someAreEnabled && !someAreInQueue);
|
||||||
|
if ((someAreEnabled && someAreDisabled) || someAreInQueue) {
|
||||||
|
// there are a mix of enabled and disabled repositories selected
|
||||||
|
fEnableButton->SetEnabled(false);
|
||||||
|
fDisableButton->SetEnabled(false);
|
||||||
|
} else {
|
||||||
|
fEnableButton->SetEnabled(someAreDisabled);
|
||||||
|
fDisableButton->SetEnabled(someAreEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// No selected rows
|
||||||
|
fEnableButton->SetLabel(kLabelEnable);
|
||||||
|
fDisableButton->SetLabel(kLabelDisable);
|
||||||
|
fEnableButton->SetEnabled(false);
|
||||||
|
fDisableButton->SetEnabled(false);
|
||||||
|
fRemoveButton->SetEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesView::_UpdateStatusView()
|
||||||
|
{
|
||||||
|
if (fRunningTaskCount) {
|
||||||
|
BString text(kStatusViewText);
|
||||||
|
text.Append(" ");
|
||||||
|
text << fRunningTaskCount;
|
||||||
|
fListStatusView->SetText(text);
|
||||||
|
} else
|
||||||
|
fListStatusView->SetText("");
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPOSITORIES_VIEW_H
|
||||||
|
#define REPOSITORIES_VIEW_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <ColumnListView.h>
|
||||||
|
#include <GroupView.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <StringView.h>
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
#include "RepositoriesSettings.h"
|
||||||
|
#include "RepoRow.h"
|
||||||
|
#include "TaskLooper.h"
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesListView : public BColumnListView {
|
||||||
|
public:
|
||||||
|
RepositoriesListView(const char* name);
|
||||||
|
virtual void KeyDown(const char* bytes, int32 numBytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesView : public BGroupView {
|
||||||
|
public:
|
||||||
|
RepositoriesView();
|
||||||
|
~RepositoriesView();
|
||||||
|
virtual void AllAttached();
|
||||||
|
virtual void AttachedToWindow();
|
||||||
|
virtual void MessageReceived(BMessage*);
|
||||||
|
void AddManualRepository(BString url);
|
||||||
|
bool IsTaskRunning() { return fRunningTaskCount > 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RepositoriesSettings fSettings;
|
||||||
|
RepositoriesListView* fListView;
|
||||||
|
BView* fStatusContainerView;
|
||||||
|
BStringView* fListStatusView;
|
||||||
|
TaskLooper* fTaskLooper;
|
||||||
|
bool fShowCompletedStatus;
|
||||||
|
int fRunningTaskCount, fLastCompletedTimerId;
|
||||||
|
BButton* fAddButton;
|
||||||
|
BButton* fRemoveButton;
|
||||||
|
BButton* fEnableButton;
|
||||||
|
BButton* fDisableButton;
|
||||||
|
|
||||||
|
// Message helpers
|
||||||
|
void _AddSelectedRowsToQueue();
|
||||||
|
void _TaskStarted(RepoRow* rowItem, int16 count);
|
||||||
|
void _TaskCompleted(RepoRow* rowItem, int16 count,
|
||||||
|
BString& newName);
|
||||||
|
void _TaskCanceled(RepoRow* rowItem, int16 count);
|
||||||
|
void _ShowCompletedStatusIfDone();
|
||||||
|
void _UpdateFromRepoConfig(RepoRow* rowItem);
|
||||||
|
|
||||||
|
// GUI functions
|
||||||
|
BString _GetRootUrl(BString url);
|
||||||
|
status_t _EmptyList();
|
||||||
|
void _InitList();
|
||||||
|
void _RefreshList();
|
||||||
|
void _UpdateListFromRoster();
|
||||||
|
void _SaveList();
|
||||||
|
RepoRow* _AddRepo(BString name, BString url, bool enabled);
|
||||||
|
void _FindSiblings();
|
||||||
|
void _UpdateButtons();
|
||||||
|
void _UpdateStatusView();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "RepositoriesWindow.h"
|
||||||
|
|
||||||
|
#include <Alert.h>
|
||||||
|
#include <Application.h>
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <LayoutBuilder.h>
|
||||||
|
#include <NodeMonitor.h>
|
||||||
|
#include <Region.h>
|
||||||
|
#include <Screen.h>
|
||||||
|
|
||||||
|
#include "AddRepoWindow.h"
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "RepositoriesWindow"
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesWindow::RepositoriesWindow()
|
||||||
|
:
|
||||||
|
BWindow(BRect(50, 50, 500, 400), B_TRANSLATE_SYSTEM_NAME("Repositories"),
|
||||||
|
B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
|
||||||
|
| B_AUTO_UPDATE_SIZE_LIMITS),
|
||||||
|
fAddWindow(NULL),
|
||||||
|
fPackageNodeStatus(B_ERROR)
|
||||||
|
{
|
||||||
|
fView = new RepositoriesView();
|
||||||
|
BLayoutBuilder::Group<>(this, B_VERTICAL).Add(fView).End();
|
||||||
|
|
||||||
|
// Size and location on screen
|
||||||
|
BRect frame = fSettings.GetFrame();
|
||||||
|
ResizeTo(frame.Width(), frame.Height());
|
||||||
|
BScreen screen;
|
||||||
|
BRect screenFrame = screen.Frame();
|
||||||
|
if (screenFrame.right < frame.right || screenFrame.left > frame.left
|
||||||
|
|| screenFrame.top > frame.top || screenFrame.bottom < frame.bottom)
|
||||||
|
CenterOnScreen();
|
||||||
|
else
|
||||||
|
MoveTo(frame.left, frame.top);
|
||||||
|
Show();
|
||||||
|
|
||||||
|
fMessenger.SetTo(this);
|
||||||
|
|
||||||
|
// Find the pkgman settings or cache directory
|
||||||
|
BPath packagePath;
|
||||||
|
// /boot/system/settings/package-repositories
|
||||||
|
status_t status = find_directory(B_SYSTEM_SETTINGS_DIRECTORY,
|
||||||
|
&packagePath);
|
||||||
|
if (status == B_OK)
|
||||||
|
status = packagePath.Append("package-repositories");
|
||||||
|
else {
|
||||||
|
// /boot/system/cache/package-repositories
|
||||||
|
status = find_directory(B_SYSTEM_CACHE_DIRECTORY, &packagePath);
|
||||||
|
if (status == B_OK)
|
||||||
|
status = packagePath.Append("package-repositories");
|
||||||
|
}
|
||||||
|
if (status == B_OK) {
|
||||||
|
BNode packageNode(packagePath.Path());
|
||||||
|
if (packageNode.InitCheck()==B_OK && packageNode.IsDirectory())
|
||||||
|
fPackageNodeStatus = packageNode.GetNodeRef(&fPackageNodeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch the pkgman settings or cache directory for changes
|
||||||
|
_StartWatching();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RepositoriesWindow::~RepositoriesWindow()
|
||||||
|
{
|
||||||
|
_StopWatching();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesWindow::_StartWatching()
|
||||||
|
{
|
||||||
|
if (fPackageNodeStatus == B_OK) {
|
||||||
|
status_t result = watch_node(&fPackageNodeRef, B_WATCH_DIRECTORY, this);
|
||||||
|
fWatchingPackageNode = (result == B_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesWindow::_StopWatching()
|
||||||
|
{
|
||||||
|
if (fPackageNodeStatus == B_OK && fWatchingPackageNode) {
|
||||||
|
watch_node(&fPackageNodeRef, B_STOP_WATCHING, this);
|
||||||
|
fWatchingPackageNode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RepositoriesWindow::QuitRequested()
|
||||||
|
{
|
||||||
|
if (fView->IsTaskRunning()) {
|
||||||
|
BAlert *alert = new BAlert("tasks",
|
||||||
|
B_TRANSLATE_COMMENT("Some tasks are still running. Stop these "
|
||||||
|
"tasks and quit?", "Application quit alert message"),
|
||||||
|
B_TRANSLATE_COMMENT("Stop and quit", "Button label"),
|
||||||
|
kCancelLabel, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||||
|
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
|
||||||
|
int32 result = alert->Go();
|
||||||
|
if (result != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fSettings.SetFrame(Frame());
|
||||||
|
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||||
|
return BWindow::QuitRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RepositoriesWindow::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what)
|
||||||
|
{
|
||||||
|
case ADD_REPO_WINDOW: {
|
||||||
|
BRect frame = Frame();
|
||||||
|
fAddWindow = new AddRepoWindow(frame, fMessenger);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ADD_REPO_URL: {
|
||||||
|
BString url;
|
||||||
|
status_t result = message->FindString(key_url, &url);
|
||||||
|
if (result == B_OK)
|
||||||
|
fView->AddManualRepository(url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ADD_WINDOW_CLOSED: {
|
||||||
|
fAddWindow = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DELETE_KEY_PRESSED: {
|
||||||
|
BMessage message(REMOVE_REPOS);
|
||||||
|
fView->MessageReceived(&message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// captures pkgman changes while the Repositories application is running
|
||||||
|
case B_NODE_MONITOR: {
|
||||||
|
// This preflet is making the changes, so ignore this message
|
||||||
|
if (fView->IsTaskRunning())
|
||||||
|
break;
|
||||||
|
|
||||||
|
int32 opcode;
|
||||||
|
if (message->FindInt32("opcode", &opcode) == B_OK) {
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case B_ATTR_CHANGED:
|
||||||
|
case B_ENTRY_CREATED:
|
||||||
|
case B_ENTRY_REMOVED: {
|
||||||
|
PostMessage(UPDATE_LIST, fView);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BWindow::MessageReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPOSITORIES_WINDOW_H
|
||||||
|
#define REPOSITORIES_WINDOW_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Node.h>
|
||||||
|
#include <Window.h>
|
||||||
|
|
||||||
|
#include "AddRepoWindow.h"
|
||||||
|
#include "RepositoriesSettings.h"
|
||||||
|
#include "RepositoriesView.h"
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesWindow : public BWindow {
|
||||||
|
public:
|
||||||
|
RepositoriesWindow();
|
||||||
|
~RepositoriesWindow();
|
||||||
|
virtual bool QuitRequested();
|
||||||
|
virtual void MessageReceived(BMessage*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RepositoriesSettings fSettings;
|
||||||
|
RepositoriesView* fView;
|
||||||
|
AddRepoWindow* fAddWindow;
|
||||||
|
BMessenger fMessenger;
|
||||||
|
node_ref fPackageNodeRef;
|
||||||
|
// node_ref to watch for changes to package-repositories directory
|
||||||
|
status_t fPackageNodeStatus;
|
||||||
|
bool fWatchingPackageNode;
|
||||||
|
// true when package-repositories directory is being watched
|
||||||
|
void _StartWatching();
|
||||||
|
void _StopWatching();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "TaskLooper.h"
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <MessageQueue.h>
|
||||||
|
#include <package/AddRepositoryRequest.h>
|
||||||
|
#include <package/DropRepositoryRequest.h>
|
||||||
|
#include <package/RefreshRepositoryRequest.h>
|
||||||
|
#include <package/PackageRoster.h>
|
||||||
|
#include <package/RepositoryConfig.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#define DEBUGTASK 0
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "TaskLooper"
|
||||||
|
|
||||||
|
static const BString kLogResultIndicator = "***";
|
||||||
|
static const BString kCompletedText =
|
||||||
|
B_TRANSLATE_COMMENT("Completed", "Completed task status message");
|
||||||
|
static const BString kFailedText =
|
||||||
|
B_TRANSLATE_COMMENT("Failed", "Failed task status message");
|
||||||
|
static const BString kAbortedText =
|
||||||
|
B_TRANSLATE_COMMENT("Aborted", "Aborted task status message");
|
||||||
|
static const BString kDescriptionText =
|
||||||
|
B_TRANSLATE_COMMENT("Description", "Failed task error description");
|
||||||
|
static const BString kDetailsText =
|
||||||
|
B_TRANSLATE_COMMENT("Details", "Job log details header");
|
||||||
|
|
||||||
|
using BSupportKit::BJob;
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JobStateListener::JobStarted(BJob* job)
|
||||||
|
{
|
||||||
|
fJobLog.Add(job->Title());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JobStateListener::JobSucceeded(BJob* job)
|
||||||
|
{
|
||||||
|
BString resultText(kLogResultIndicator);
|
||||||
|
fJobLog.Add(resultText.Append(kCompletedText));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JobStateListener::JobFailed(BJob* job)
|
||||||
|
{
|
||||||
|
BString resultText(kLogResultIndicator);
|
||||||
|
resultText.Append(kFailedText).Append(": ")
|
||||||
|
.Append(strerror(job->Result()));
|
||||||
|
fJobLog.Add(resultText);
|
||||||
|
if (job->ErrorString().Length() > 0) {
|
||||||
|
resultText.SetTo(kLogResultIndicator);
|
||||||
|
resultText.Append(kDescriptionText).Append(": ")
|
||||||
|
.Append(job->ErrorString());
|
||||||
|
fJobLog.Add(resultText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
JobStateListener::JobAborted(BJob* job)
|
||||||
|
{
|
||||||
|
BString resultText(kLogResultIndicator);
|
||||||
|
resultText.Append(kAbortedText).Append(": ")
|
||||||
|
.Append(strerror(job->Result()));
|
||||||
|
fJobLog.Add(resultText);
|
||||||
|
if (job->ErrorString().Length() > 0) {
|
||||||
|
resultText.SetTo(kLogResultIndicator);
|
||||||
|
resultText.Append(kDescriptionText).Append(": ")
|
||||||
|
.Append(job->ErrorString());
|
||||||
|
fJobLog.Add(resultText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
JobStateListener::GetJobLog()
|
||||||
|
{
|
||||||
|
return fJobLog.Join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TaskLooper::TaskLooper(const BMessenger& target)
|
||||||
|
:
|
||||||
|
BLooper("TaskLooper"),
|
||||||
|
fReplyTarget(target)
|
||||||
|
{
|
||||||
|
Run();
|
||||||
|
fMessenger.SetTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
TaskLooper::QuitRequested()
|
||||||
|
{
|
||||||
|
return MessageQueue()->IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskLooper::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what)
|
||||||
|
{
|
||||||
|
case DO_TASK: {
|
||||||
|
RepoRow* rowItem;
|
||||||
|
status_t result = message->FindPointer(key_rowptr, (void**)&rowItem);
|
||||||
|
if (result == B_OK) {
|
||||||
|
// Check to make sure there isn't already an existing task for this
|
||||||
|
int16 queueCount = fTaskQueue.CountItems();
|
||||||
|
for (int16 index = 0; index<queueCount; index++) {
|
||||||
|
Task* task = fTaskQueue.ItemAt(index);
|
||||||
|
if (rowItem == task->rowItem)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize task
|
||||||
|
Task* newTask = new Task();
|
||||||
|
newTask->rowItem = rowItem;
|
||||||
|
newTask->name = rowItem->Name();
|
||||||
|
newTask->resultName = newTask->name;
|
||||||
|
if (rowItem->IsEnabled()) {
|
||||||
|
newTask->taskType = DISABLE_REPO;
|
||||||
|
newTask->taskParam = newTask->name;
|
||||||
|
} else {
|
||||||
|
newTask->taskType = ENABLE_REPO;
|
||||||
|
newTask->taskParam = rowItem->Url();
|
||||||
|
}
|
||||||
|
newTask->owner = this;
|
||||||
|
newTask->fTimer = NULL;
|
||||||
|
|
||||||
|
// Add to queue and start
|
||||||
|
fTaskQueue.AddItem(newTask);
|
||||||
|
BString threadName(newTask->taskType == ENABLE_REPO ?
|
||||||
|
"enable_task" : "disable_task");
|
||||||
|
newTask->threadId = spawn_thread(_DoTask, threadName.String(),
|
||||||
|
B_NORMAL_PRIORITY, (void*)newTask);
|
||||||
|
status_t threadResult;
|
||||||
|
if (newTask->threadId < B_OK)
|
||||||
|
threadResult = B_ERROR;
|
||||||
|
else {
|
||||||
|
threadResult = resume_thread(newTask->threadId);
|
||||||
|
if (threadResult == B_OK) {
|
||||||
|
newTask->fTimer = new TaskTimer(fMessenger, newTask);
|
||||||
|
newTask->fTimer->Start(newTask->name);
|
||||||
|
// Reply to view
|
||||||
|
BMessage reply(*message);
|
||||||
|
reply.what = TASK_STARTED;
|
||||||
|
reply.AddInt16(key_count, fTaskQueue.CountItems());
|
||||||
|
fReplyTarget.SendMessage(&reply);
|
||||||
|
} else
|
||||||
|
kill_thread(newTask->threadId);
|
||||||
|
}
|
||||||
|
if (threadResult != B_OK) {
|
||||||
|
_RemoveAndDelete(newTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TASK_COMPLETED:
|
||||||
|
case TASK_COMPLETED_WITH_ERRORS:
|
||||||
|
case TASK_CANCELED: {
|
||||||
|
Task* task;
|
||||||
|
status_t result = message->FindPointer(key_taskptr, (void**)&task);
|
||||||
|
if (result == B_OK && fTaskQueue.HasItem(task)) {
|
||||||
|
task->fTimer->Stop(task->resultName);
|
||||||
|
BMessage reply(message->what);
|
||||||
|
reply.AddInt16(key_count, fTaskQueue.CountItems()-1);
|
||||||
|
reply.AddPointer(key_rowptr, task->rowItem);
|
||||||
|
if (message->what == TASK_COMPLETED_WITH_ERRORS)
|
||||||
|
reply.AddString(key_details, task->resultErrorDetails);
|
||||||
|
if (task->taskType == ENABLE_REPO
|
||||||
|
&& task->name.Compare(task->resultName) != 0)
|
||||||
|
reply.AddString(key_name, task->resultName);
|
||||||
|
fReplyTarget.SendMessage(&reply);
|
||||||
|
_RemoveAndDelete(task);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TASK_KILL_REQUEST: {
|
||||||
|
Task* task;
|
||||||
|
status_t result = message->FindPointer(key_taskptr, (void**)&task);
|
||||||
|
if (result == B_OK && fTaskQueue.HasItem(task)) {
|
||||||
|
kill_thread(task->threadId);
|
||||||
|
BMessage reply(TASK_CANCELED);
|
||||||
|
reply.AddInt16(key_count, fTaskQueue.CountItems()-1);
|
||||||
|
reply.AddPointer(key_rowptr, task->rowItem);
|
||||||
|
fReplyTarget.SendMessage(&reply);
|
||||||
|
_RemoveAndDelete(task);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskLooper::_RemoveAndDelete(Task* task)
|
||||||
|
{
|
||||||
|
fTaskQueue.RemoveItem(task);
|
||||||
|
if (task->fTimer) {
|
||||||
|
task->fTimer->Lock();
|
||||||
|
task->fTimer->Quit();
|
||||||
|
task->fTimer = NULL;
|
||||||
|
}
|
||||||
|
delete task;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TaskLooper::_DoTask(void* data)
|
||||||
|
{
|
||||||
|
Task* task = (Task*)data;
|
||||||
|
BString errorDetails, repoName("");
|
||||||
|
status_t returnResult = B_OK;
|
||||||
|
DecisionProvider decisionProvider;
|
||||||
|
JobStateListener listener;
|
||||||
|
switch (task->taskType)
|
||||||
|
{
|
||||||
|
case DISABLE_REPO: {
|
||||||
|
BString nameParam(task->taskParam);
|
||||||
|
BPackageKit::BContext context(decisionProvider, listener);
|
||||||
|
BPackageKit::DropRepositoryRequest dropRequest(context, nameParam);
|
||||||
|
status_t result = dropRequest.Process();
|
||||||
|
if (result != B_OK) {
|
||||||
|
returnResult = result;
|
||||||
|
if (result != B_CANCELED) {
|
||||||
|
errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
|
||||||
|
"error disabling the repository %name%",
|
||||||
|
"Error message, do not translate %name%"));
|
||||||
|
BString nameString("\"");
|
||||||
|
nameString.Append(nameParam).Append("\"");
|
||||||
|
errorDetails.ReplaceFirst("%name%", nameString);
|
||||||
|
_AppendErrorDetails(errorDetails, &listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ENABLE_REPO: {
|
||||||
|
BString urlParam(task->taskParam);
|
||||||
|
BPackageKit::BContext context(decisionProvider, listener);
|
||||||
|
// Add repository
|
||||||
|
bool asUserRepository = false;
|
||||||
|
// TODO does this ever change?
|
||||||
|
BPackageKit::AddRepositoryRequest addRequest(context, urlParam,
|
||||||
|
asUserRepository);
|
||||||
|
status_t result = addRequest.Process();
|
||||||
|
if (result != B_OK) {
|
||||||
|
returnResult = result;
|
||||||
|
if (result != B_CANCELED) {
|
||||||
|
errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
|
||||||
|
"error enabling the repository %url%",
|
||||||
|
"Error message, do not translate %url%"));
|
||||||
|
errorDetails.ReplaceFirst("%url%", urlParam);
|
||||||
|
_AppendErrorDetails(errorDetails, &listener);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Continue on to refresh repo cache
|
||||||
|
repoName = addRequest.RepositoryName();
|
||||||
|
BPackageKit::BPackageRoster roster;
|
||||||
|
BPackageKit::BRepositoryConfig repoConfig;
|
||||||
|
roster.GetRepositoryConfig(repoName, &repoConfig);
|
||||||
|
BPackageKit::BRefreshRepositoryRequest refreshRequest(context,
|
||||||
|
repoConfig);
|
||||||
|
result = refreshRequest.Process();
|
||||||
|
if (result != B_OK) {
|
||||||
|
returnResult = result;
|
||||||
|
if (result != B_CANCELED) {
|
||||||
|
errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
|
||||||
|
"error refreshing the repository cache for %name%",
|
||||||
|
"Error message, do not translate %name%"));
|
||||||
|
BString nameString("\"");
|
||||||
|
nameString.Append(repoName).Append("\"");
|
||||||
|
errorDetails.ReplaceFirst("%name%", nameString);
|
||||||
|
_AppendErrorDetails(errorDetails, &listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Report completion status
|
||||||
|
BMessage reply;
|
||||||
|
if (returnResult == B_OK) {
|
||||||
|
reply.what = TASK_COMPLETED;
|
||||||
|
// Add the repo name if we need to update the list row value
|
||||||
|
if (task->taskType == ENABLE_REPO)
|
||||||
|
task->resultName = repoName;
|
||||||
|
} else if (returnResult == B_CANCELED)
|
||||||
|
reply.what = TASK_CANCELED;
|
||||||
|
else {
|
||||||
|
reply.what = TASK_COMPLETED_WITH_ERRORS;
|
||||||
|
task->resultErrorDetails = errorDetails;
|
||||||
|
if (task->taskType == ENABLE_REPO)
|
||||||
|
task->resultName = repoName;
|
||||||
|
}
|
||||||
|
reply.AddPointer(key_taskptr, task);
|
||||||
|
task->owner->PostMessage(&reply);
|
||||||
|
#if DEBUGTASK
|
||||||
|
if (returnResult == B_OK || returnResult == B_CANCELED) {
|
||||||
|
BString degubDetails("Debug info:\n");
|
||||||
|
degubDetails.Append(listener.GetJobLog());
|
||||||
|
(new BAlert("debug", degubDetails, "OK"))->Go(NULL);
|
||||||
|
}
|
||||||
|
#endif // DEBUGTASK
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskLooper::_AppendErrorDetails(BString& details, JobStateListener* listener)
|
||||||
|
{
|
||||||
|
details.Append("\n\n").Append(kDetailsText).Append(":\n");
|
||||||
|
details.Append(listener->GetJobLog());
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef TASK_LOOPER_H
|
||||||
|
#define TASK_LOOPER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Job.h>
|
||||||
|
#include <Looper.h>
|
||||||
|
#include <ObjectList.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <StringList.h>
|
||||||
|
#include <package/Context.h>
|
||||||
|
|
||||||
|
#include "TaskTimer.h"
|
||||||
|
|
||||||
|
|
||||||
|
class DecisionProvider : public BPackageKit::BDecisionProvider {
|
||||||
|
public:
|
||||||
|
DecisionProvider() {}
|
||||||
|
|
||||||
|
virtual bool YesNoDecisionNeeded(const BString& description,
|
||||||
|
const BString& question,
|
||||||
|
const BString& yes,
|
||||||
|
const BString& no,
|
||||||
|
const BString& defaultChoice)
|
||||||
|
{ return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class JobStateListener : public BSupportKit::BJobStateListener {
|
||||||
|
public:
|
||||||
|
JobStateListener() {}
|
||||||
|
|
||||||
|
virtual void JobStarted(BSupportKit::BJob* job);
|
||||||
|
virtual void JobSucceeded(BSupportKit::BJob* job);
|
||||||
|
virtual void JobFailed(BSupportKit::BJob* job);
|
||||||
|
virtual void JobAborted(BSupportKit::BJob* job);
|
||||||
|
BString GetJobLog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BStringList fJobLog;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TaskLooper : public BLooper {
|
||||||
|
public:
|
||||||
|
TaskLooper(const BMessenger& target);
|
||||||
|
virtual bool QuitRequested();
|
||||||
|
virtual void MessageReceived(BMessage*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BObjectList<Task> fTaskQueue;
|
||||||
|
void _RemoveAndDelete(Task* task);
|
||||||
|
static status_t _DoTask(void* data);
|
||||||
|
static void _AppendErrorDetails(BString& details,
|
||||||
|
JobStateListener* listener);
|
||||||
|
BMessenger fReplyTarget;
|
||||||
|
BMessenger fMessenger;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "TaskTimer.h"
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
#include <Catalog.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "TaskTimer"
|
||||||
|
|
||||||
|
static int32 sAlertStackCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
TaskTimer::TaskTimer(const BMessenger& target, Task* owner)
|
||||||
|
:
|
||||||
|
BLooper(),
|
||||||
|
fTimeoutMicroSeconds(kTimerTimeoutSeconds * 1000000),
|
||||||
|
fTimerIsRunning(false),
|
||||||
|
fReplyTarget(target),
|
||||||
|
fMessageRunner(NULL),
|
||||||
|
fTimeoutMessage(TASK_TIMEOUT),
|
||||||
|
fTimeoutAlert(NULL),
|
||||||
|
fOwner(owner)
|
||||||
|
{
|
||||||
|
Run();
|
||||||
|
|
||||||
|
// Messenger for the Message Runner to use to send its message to the timer
|
||||||
|
fMessenger.SetTo(this);
|
||||||
|
// Invoker for the Alerts to use to send their messages to the timer
|
||||||
|
fTimeoutAlertInvoker.SetMessage(
|
||||||
|
new BMessage(TIMEOUT_ALERT_BUTTON_SELECTION));
|
||||||
|
fTimeoutAlertInvoker.SetTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TaskTimer::~TaskTimer()
|
||||||
|
{
|
||||||
|
if (fTimeoutAlert) {
|
||||||
|
fTimeoutAlert->Lock();
|
||||||
|
fTimeoutAlert->Quit();
|
||||||
|
}
|
||||||
|
if (fMessageRunner)
|
||||||
|
fMessageRunner->SetCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
TaskTimer::QuitRequested()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskTimer::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what)
|
||||||
|
{
|
||||||
|
case TASK_TIMEOUT: {
|
||||||
|
fMessageRunner = NULL;
|
||||||
|
if (fTimerIsRunning) {
|
||||||
|
BString text(B_TRANSLATE_COMMENT("The task for repository"
|
||||||
|
" %name% is taking a long time to complete.",
|
||||||
|
"Alert message. Do not translate %name%"));
|
||||||
|
BString nameString("\"");
|
||||||
|
nameString.Append(fRepositoryName).Append("\"");
|
||||||
|
text.ReplaceFirst("%name%", nameString);
|
||||||
|
fTimeoutAlert = new BAlert("timeout", text,
|
||||||
|
B_TRANSLATE_COMMENT("Keep trying", "Button label"),
|
||||||
|
B_TRANSLATE_COMMENT("Cancel task", "Button label"),
|
||||||
|
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||||
|
fTimeoutAlert->SetShortcut(0, B_ESCAPE);
|
||||||
|
// Calculate the position to correctly stack this alert
|
||||||
|
BRect windowFrame = be_app->WindowAt(0)->Frame();
|
||||||
|
int32 stackPos = _NextAlertStackCount();
|
||||||
|
float xPos = windowFrame.left
|
||||||
|
+ windowFrame.Width()/2 + stackPos * kTimerAlertOffset;
|
||||||
|
float yPos = windowFrame.top
|
||||||
|
+ (stackPos + 1) * kTimerAlertOffset;
|
||||||
|
fTimeoutAlert->Go(&fTimeoutAlertInvoker);
|
||||||
|
xPos -= fTimeoutAlert->Frame().Width()/2;
|
||||||
|
// The correct frame for the alert is not available until
|
||||||
|
// after Go is called
|
||||||
|
fTimeoutAlert->MoveTo(xPos, yPos);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TIMEOUT_ALERT_BUTTON_SELECTION: {
|
||||||
|
fTimeoutAlert = NULL;
|
||||||
|
// Timeout alert was invoked by user and timer still has not
|
||||||
|
// been stopped
|
||||||
|
if (fTimerIsRunning) {
|
||||||
|
// find which button was pressed
|
||||||
|
int32 selection = -1;
|
||||||
|
message->FindInt32("which", &selection);
|
||||||
|
if (selection == 1) {
|
||||||
|
BMessage reply(TASK_KILL_REQUEST);
|
||||||
|
reply.AddPointer(key_taskptr, fOwner);
|
||||||
|
fReplyTarget.SendMessage(&reply);
|
||||||
|
} else if (selection == 0) {
|
||||||
|
// Create new timer
|
||||||
|
fMessageRunner = new BMessageRunner(fMessenger,
|
||||||
|
&fTimeoutMessage, kTimerRetrySeconds * 1000000, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskTimer::Start(const char* name)
|
||||||
|
{
|
||||||
|
fTimerIsRunning = true;
|
||||||
|
fRepositoryName.SetTo(name);
|
||||||
|
|
||||||
|
// Create a message runner that will send a TASK_TIMEOUT message if the
|
||||||
|
// timer is not stopped
|
||||||
|
if (fMessageRunner == NULL)
|
||||||
|
fMessageRunner = new BMessageRunner(fMessenger, &fTimeoutMessage,
|
||||||
|
fTimeoutMicroSeconds, 1);
|
||||||
|
else
|
||||||
|
fMessageRunner->SetInterval(fTimeoutMicroSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskTimer::Stop(const char* name)
|
||||||
|
{
|
||||||
|
fTimerIsRunning = false;
|
||||||
|
|
||||||
|
// Reset max timeout so we can reuse the runner at the next Start call
|
||||||
|
if (fMessageRunner != NULL)
|
||||||
|
fMessageRunner->SetInterval(LLONG_MAX);
|
||||||
|
|
||||||
|
// If timeout alert is showing replace it
|
||||||
|
if (fTimeoutAlert) {
|
||||||
|
// Remove current alert
|
||||||
|
BRect frame = fTimeoutAlert->Frame();
|
||||||
|
fTimeoutAlert->Quit();
|
||||||
|
fTimeoutAlert = NULL;
|
||||||
|
|
||||||
|
// Display new alert that won't send a message
|
||||||
|
BString text(B_TRANSLATE_COMMENT("Good news! The task for repository "
|
||||||
|
"%name% completed.", "Alert message. Do not translate %name%"));
|
||||||
|
BString nameString("\"");
|
||||||
|
nameString.Append(name).Append("\"");
|
||||||
|
text.ReplaceFirst("%name%", nameString);
|
||||||
|
BAlert* newAlert = new BAlert("timeout", text, kOKLabel, NULL, NULL,
|
||||||
|
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||||
|
newAlert->SetShortcut(0, B_ESCAPE);
|
||||||
|
newAlert->MoveTo(frame.left, frame.top);
|
||||||
|
newAlert->Go(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
TaskTimer::_NextAlertStackCount()
|
||||||
|
{
|
||||||
|
if (sAlertStackCount > 9)
|
||||||
|
sAlertStackCount = 0;
|
||||||
|
return sAlertStackCount++;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef TASKTIMER_H
|
||||||
|
#define TASKTIMER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Alert.h>
|
||||||
|
#include <Invoker.h>
|
||||||
|
#include <Looper.h>
|
||||||
|
#include <Message.h>
|
||||||
|
#include <MessageRunner.h>
|
||||||
|
#include <Messenger.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
#include "RepoRow.h"
|
||||||
|
|
||||||
|
class TaskTimer;
|
||||||
|
class TaskLooper;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
RepoRow* rowItem;
|
||||||
|
int32 taskType;
|
||||||
|
BString name, taskParam;
|
||||||
|
thread_id threadId;
|
||||||
|
TaskLooper* owner;
|
||||||
|
BString resultName, resultErrorDetails;
|
||||||
|
TaskTimer* fTimer;
|
||||||
|
} Task;
|
||||||
|
|
||||||
|
|
||||||
|
class TaskTimer : public BLooper {
|
||||||
|
public:
|
||||||
|
TaskTimer(const BMessenger& target, Task* owner);
|
||||||
|
~TaskTimer();
|
||||||
|
virtual bool QuitRequested();
|
||||||
|
virtual void MessageReceived(BMessage*);
|
||||||
|
void Start(const char* name);
|
||||||
|
void Stop(const char* name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32 fTimeoutMicroSeconds;
|
||||||
|
bool fTimerIsRunning;
|
||||||
|
BString fRepositoryName;
|
||||||
|
BMessenger fReplyTarget;
|
||||||
|
BMessenger fMessenger;
|
||||||
|
BMessageRunner* fMessageRunner;
|
||||||
|
BMessage fTimeoutMessage;
|
||||||
|
BAlert* fTimeoutAlert;
|
||||||
|
BInvoker fTimeoutAlertInvoker;
|
||||||
|
Task* fOwner;
|
||||||
|
int32 _NextAlertStackCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Brian Hill
|
||||||
|
*/
|
||||||
|
#ifndef REPOSITORIES_CONSTANTS_H
|
||||||
|
#define REPOSITORIES_CONSTANTS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Catalog.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
#undef B_TRANSLATION_CONTEXT
|
||||||
|
#define B_TRANSLATION_CONTEXT "Constants"
|
||||||
|
|
||||||
|
static const float kAddWindowOffset = 10.0;
|
||||||
|
static const int16 kTimerAlertOffset = 15;
|
||||||
|
static const int16 kTimerTimeoutSeconds = 10;
|
||||||
|
static const int16 kTimerRetrySeconds = 20;
|
||||||
|
|
||||||
|
static const BString kOKLabel = B_TRANSLATE_COMMENT("OK", "Button label");
|
||||||
|
static const BString kCancelLabel = B_TRANSLATE_COMMENT("Cancel",
|
||||||
|
"Button label");
|
||||||
|
static const BString kRemoveLabel = B_TRANSLATE_COMMENT("Remove",
|
||||||
|
"Button label");
|
||||||
|
static const BString kNewRepoDefaultName = B_TRANSLATE_COMMENT("Unknown",
|
||||||
|
"Unknown repository name");
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
const char* url;
|
||||||
|
} Repository;
|
||||||
|
|
||||||
|
|
||||||
|
static const Repository kDefaultRepos[] = {
|
||||||
|
{ "Haiku", "https://packages.haiku-os.org/haiku/master/"B_HAIKU_ABI_NAME
|
||||||
|
"/current"},
|
||||||
|
{ "HaikuPorts", "https://packages.haiku-os.org/haikuports/master/repo/"
|
||||||
|
B_HAIKU_ABI_NAME"/current" }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Message keys
|
||||||
|
#define key_frame "frame"
|
||||||
|
#define key_name "repo_name"
|
||||||
|
#define key_url "repo_url"
|
||||||
|
#define key_text "text"
|
||||||
|
#define key_details "details"
|
||||||
|
#define key_rowptr "row_ptr"
|
||||||
|
#define key_taskptr "task_ptr"
|
||||||
|
#define key_count "count"
|
||||||
|
#define key_ID "ID"
|
||||||
|
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
enum {
|
||||||
|
ADD_REPO_WINDOW = 'BHRa',
|
||||||
|
ADD_BUTTON_PRESSED,
|
||||||
|
CANCEL_BUTTON_PRESSED,
|
||||||
|
ADD_REPO_URL,
|
||||||
|
ADD_WINDOW_CLOSED,
|
||||||
|
REMOVE_REPOS,
|
||||||
|
LIST_SELECTION_CHANGED,
|
||||||
|
ENABLE_BUTTON_PRESSED,
|
||||||
|
DISABLE_BUTTON_PRESSED,
|
||||||
|
ITEM_INVOKED,
|
||||||
|
DELETE_KEY_PRESSED,
|
||||||
|
DO_TASK,
|
||||||
|
STATUS_VIEW_COMPLETED_TIMEOUT,
|
||||||
|
TASK_STARTED,
|
||||||
|
TASK_COMPLETED,
|
||||||
|
TASK_COMPLETED_WITH_ERRORS,
|
||||||
|
TASK_CANCELED,
|
||||||
|
UPDATE_LIST,
|
||||||
|
NO_TASKS,
|
||||||
|
ENABLE_REPO,
|
||||||
|
DISABLE_REPO,
|
||||||
|
TASK_TIMEOUT,
|
||||||
|
TIMEOUT_ALERT_BUTTON_SELECTION,
|
||||||
|
TASK_KILL_REQUEST
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Repo row task state
|
||||||
|
enum {
|
||||||
|
STATE_NOT_IN_QUEUE = 0,
|
||||||
|
STATE_IN_QUEUE_WAITING,
|
||||||
|
STATE_IN_QUEUE_RUNNING
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user