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);
|
||||
|
||||
// Construct repository popup
|
||||
BPopUpMenu* repositoryMenu = new BPopUpMenu(B_TRANSLATE("Depot"));
|
||||
fRepositoryField = new BMenuField("repository", B_TRANSLATE("Depot:"),
|
||||
BPopUpMenu* repositoryMenu = new BPopUpMenu(B_TRANSLATE("Repository"));
|
||||
fRepositoryField = new BMenuField("repository", B_TRANSLATE("Repository:"),
|
||||
repositoryMenu);
|
||||
|
||||
// Construct search terms field
|
||||
@@ -125,7 +125,7 @@ FilterView::AdoptModel(const Model& model)
|
||||
BMenu* repositoryMenu = fRepositoryField->Menu();
|
||||
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)));
|
||||
|
||||
repositoryMenu->AddItem(new BSeparatorItem());
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <MenuBar.h>
|
||||
#include <MenuItem.h>
|
||||
#include <Messenger.h>
|
||||
#include <Roster.h>
|
||||
#include <Screen.h>
|
||||
#include <ScrollView.h>
|
||||
#include <StringList.h>
|
||||
@@ -63,7 +64,8 @@
|
||||
|
||||
enum {
|
||||
MSG_MODEL_WORKER_DONE = 'mmwd',
|
||||
MSG_REFRESH_DEPOTS = 'mrdp',
|
||||
MSG_REFRESH_REPOS = 'mrrp',
|
||||
MSG_MANAGE_REPOS = 'mmrp',
|
||||
MSG_LOG_IN = 'lgin',
|
||||
MSG_LOG_OUT = 'lgot',
|
||||
MSG_AUTHORIZATION_CHANGED = 'athc',
|
||||
@@ -316,10 +318,14 @@ MainWindow::MessageReceived(BMessage* message)
|
||||
_StartRefreshWorker(false);
|
||||
break;
|
||||
|
||||
case MSG_REFRESH_DEPOTS:
|
||||
case MSG_REFRESH_REPOS:
|
||||
_StartRefreshWorker(true);
|
||||
break;
|
||||
|
||||
case MSG_MANAGE_REPOS:
|
||||
be_roster->Launch("application/x-vnd.Haiku-Repositories");
|
||||
break;
|
||||
|
||||
case MSG_LOG_IN:
|
||||
_OpenLoginWindow(BMessage());
|
||||
break;
|
||||
@@ -567,8 +573,10 @@ void
|
||||
MainWindow::_BuildMenu(BMenuBar* menuBar)
|
||||
{
|
||||
BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
|
||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"),
|
||||
new BMessage(MSG_REFRESH_DEPOTS)));
|
||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh repositories"),
|
||||
new BMessage(MSG_REFRESH_REPOS)));
|
||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"),
|
||||
new BMessage(MSG_MANAGE_REPOS)));
|
||||
|
||||
menuBar->AddItem(menu);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ SubInclude HAIKU_TOP src preferences mouse ;
|
||||
SubInclude HAIKU_TOP src preferences network ;
|
||||
SubInclude HAIKU_TOP src preferences notifications ;
|
||||
SubInclude HAIKU_TOP src preferences printers ;
|
||||
SubInclude HAIKU_TOP src preferences repositories ;
|
||||
SubInclude HAIKU_TOP src preferences screen ;
|
||||
SubInclude HAIKU_TOP src preferences screensaver ;
|
||||
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