Patch by plfiorini: Integration of InfoPopper as a system service. See ticket
#1245. There are some TODOs outlined in the ticket, but they will be much easier to review as individual patches against trunk, versus as a new version of the huge patch. I've messed a lot with src/servers/notification/NotificationsView.cpp in order to resolve a crash I was getting when testing this thing (rewrote line wrapping). I've also replaced the icons with the one that zuMi did long ago. Thanks, plfiorini, for working on this code as much as you did! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36949 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright 2010, Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2009, Pier Luigi Fiorini.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Pier Luigi Fiorini, [email protected]
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Roster.h>
|
||||
#include <GroupLayout.h>
|
||||
#include <GroupLayoutBuilder.h>
|
||||
#include <Alert.h>
|
||||
#include <Font.h>
|
||||
#include <Button.h>
|
||||
#include <StringView.h>
|
||||
#include <TextControl.h>
|
||||
#include <CheckBox.h>
|
||||
#include <String.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Node.h>
|
||||
#include <notification/Notifications.h>
|
||||
#include <Path.h>
|
||||
#include <File.h>
|
||||
#include <Directory.h>
|
||||
#include <VolumeRoster.h>
|
||||
#include <Volume.h>
|
||||
#include <Query.h>
|
||||
#include <SymLink.h>
|
||||
|
||||
#include "GeneralView.h"
|
||||
#include "SettingsHost.h"
|
||||
|
||||
#define _T(str) (str)
|
||||
|
||||
const int32 kServer = '_TSR';
|
||||
|
||||
const char* kStartServer = _T("Enable notifications");
|
||||
const char* kStopServer = _T("Disable notifications");
|
||||
const char* kStarted = _T("Events are notified");
|
||||
const char* kStopped = _T("Events are not notified");
|
||||
|
||||
|
||||
GeneralView::GeneralView(SettingsHost* host)
|
||||
:
|
||||
SettingsPane("general", host)
|
||||
{
|
||||
BFont statusFont;
|
||||
|
||||
// Set a smaller font for the status label
|
||||
statusFont.SetSize(be_plain_font->Size() * 0.8);
|
||||
|
||||
// Status button and label
|
||||
fServerButton = new BButton("server", kStartServer, new BMessage(kServer));
|
||||
fStatusLabel = new BStringView("status", kStopped);
|
||||
fStatusLabel->SetFont(&statusFont);
|
||||
|
||||
// Update status label and server button
|
||||
if (_IsServerRunning()) {
|
||||
fServerButton->SetLabel(kStopServer);
|
||||
fStatusLabel->SetText(kStarted);
|
||||
} else {
|
||||
fServerButton->SetLabel(kStartServer);
|
||||
fStatusLabel->SetText(kStopped);
|
||||
}
|
||||
|
||||
// Autostart
|
||||
fAutoStart = new BCheckBox("autostart",
|
||||
_T("Enable notifications at startup"), new BMessage(kSettingChanged));
|
||||
|
||||
// Display time
|
||||
fTimeout = new BTextControl(_T("Hide notifications from screen after"), NULL,
|
||||
new BMessage(kSettingChanged));
|
||||
BStringView* displayTimeLabel = new BStringView("dt_label",
|
||||
_T("seconds of inactivity"));
|
||||
|
||||
// Default position
|
||||
// TODO: Here will come a screen representation with the four corners clickable
|
||||
|
||||
// Load settings
|
||||
Load();
|
||||
|
||||
// Calculate inset
|
||||
float inset = ceilf(be_plain_font->Size() * 0.7f);
|
||||
|
||||
SetLayout(new BGroupLayout(B_VERTICAL));
|
||||
AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
|
||||
.AddGroup(B_HORIZONTAL, inset)
|
||||
.Add(fServerButton)
|
||||
.Add(fStatusLabel)
|
||||
.AddGlue()
|
||||
.End()
|
||||
|
||||
.AddGroup(B_VERTICAL, inset)
|
||||
.Add(fAutoStart)
|
||||
.AddGroup(B_HORIZONTAL)
|
||||
.AddGroup(B_HORIZONTAL, 2)
|
||||
.Add(fTimeout)
|
||||
.Add(displayTimeLabel)
|
||||
.End()
|
||||
.End()
|
||||
.End()
|
||||
|
||||
.AddGlue()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GeneralView::AttachedToWindow()
|
||||
{
|
||||
fServerButton->SetTarget(this);
|
||||
fAutoStart->SetTarget(this);
|
||||
fTimeout->SetTarget(this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GeneralView::MessageReceived(BMessage* msg)
|
||||
{
|
||||
switch (msg->what) {
|
||||
case kServer: {
|
||||
entry_ref ref;
|
||||
|
||||
// Check if server is available
|
||||
if (!_CanFindServer(&ref)) {
|
||||
BAlert* alert = new BAlert(_T("Notifications"),
|
||||
_T("The notifications server cannot be found, "
|
||||
"this means your InfoPopper installation was not "
|
||||
"successfully completed."), _T("OK"), NULL,
|
||||
NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_IsServerRunning()) {
|
||||
// Server team
|
||||
team_id team = be_roster->TeamFor(kNotificationServerSignature);
|
||||
|
||||
// Establish a connection to infopopper_server
|
||||
status_t ret = B_ERROR;
|
||||
BMessenger messenger(kNotificationServerSignature, team, &ret);
|
||||
if (ret != B_OK) {
|
||||
BAlert* alert = new BAlert(_T("Notifications"),
|
||||
_T("Notifications cannot be stopped, because "
|
||||
"the server can't be reached."),
|
||||
_T("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send quit message
|
||||
if (messenger.SendMessage(new BMessage(B_QUIT_REQUESTED)) != B_OK) {
|
||||
BAlert* alert = new BAlert(_T("Notifications"),
|
||||
_T("Cannot disable notifications because the server "
|
||||
"can't be reached."), _T("OK"), NULL,
|
||||
NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return;
|
||||
}
|
||||
|
||||
fServerButton->SetLabel(kStartServer);
|
||||
fStatusLabel->SetText(kStopped);
|
||||
} else {
|
||||
// Start server
|
||||
status_t err = be_roster->Launch(kNotificationServerSignature);
|
||||
if (err != B_OK) {
|
||||
BAlert* alert = new BAlert(_T("Notifications"),
|
||||
_T("Cannot enable notifications because the server "
|
||||
"cannot be found.\nThis means your InfoPopper "
|
||||
"installation was not successfully completed."),
|
||||
_T("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return;
|
||||
}
|
||||
|
||||
fServerButton->SetLabel(kStopServer);
|
||||
fStatusLabel->SetText(kStarted);
|
||||
}
|
||||
} break;
|
||||
case kSettingChanged:
|
||||
SettingsPane::MessageReceived(msg);
|
||||
break;
|
||||
default:
|
||||
BView::MessageReceived(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GeneralView::Load()
|
||||
{
|
||||
BPath path;
|
||||
|
||||
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
path.Append(kSettingsDirectory);
|
||||
|
||||
if (create_directory(path.Path(), 0755) != B_OK) {
|
||||
BAlert* alert = new BAlert("",
|
||||
_T("There was a problem saving the preferences.\n"
|
||||
"It's possible you don't have write access to the "
|
||||
"settings directory."), "OK", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
}
|
||||
|
||||
path.Append(kGeneralSettings);
|
||||
|
||||
BMessage settings;
|
||||
BFile file(path.Path(), B_READ_ONLY);
|
||||
settings.Unflatten(&file);
|
||||
|
||||
char buffer[255];
|
||||
|
||||
bool autoStart;
|
||||
if (settings.FindBool(kAutoStartName, &autoStart) != B_OK)
|
||||
autoStart = kDefaultAutoStart;
|
||||
fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
|
||||
|
||||
int32 timeout;
|
||||
if (settings.FindInt32(kTimeoutName, &timeout) != B_OK)
|
||||
timeout = kDefaultTimeout;
|
||||
(void)sprintf(buffer, "%ld", timeout);
|
||||
fTimeout->SetText(buffer);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GeneralView::Save()
|
||||
{
|
||||
BPath path;
|
||||
|
||||
status_t ret = B_OK;
|
||||
ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
|
||||
if (ret != B_OK)
|
||||
return ret;
|
||||
|
||||
path.Append(kSettingsDirectory);
|
||||
path.Append(kGeneralSettings);
|
||||
|
||||
BMessage settings;
|
||||
|
||||
bool autoStart = (fAutoStart->Value() == B_CONTROL_ON);
|
||||
settings.AddBool(kAutoStartName, autoStart);
|
||||
|
||||
int32 timeout = atol(fTimeout->Text());
|
||||
settings.AddInt32(kTimeoutName, timeout);
|
||||
|
||||
// Save settings file
|
||||
BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||
ret = settings.Flatten(&file);
|
||||
if (ret != B_OK) {
|
||||
BAlert* alert = new BAlert("",
|
||||
_T("An error is occurred saving the preferences.\n"
|
||||
"It's possible you are running out of disk space."),
|
||||
"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Find server path
|
||||
entry_ref ref;
|
||||
if (!_CanFindServer(&ref)) {
|
||||
BAlert* alert = new BAlert("",
|
||||
_T("The notifications server cannot be found.\n"
|
||||
"A possible cause is an installation not done correctly"),
|
||||
"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// UserBootscript command
|
||||
BPath serverPath(&ref);
|
||||
|
||||
// Start server at boot time
|
||||
ret = find_directory(B_USER_BOOT_DIRECTORY, &path, true);
|
||||
if (ret != B_OK) {
|
||||
BAlert* alert = new BAlert("",
|
||||
_T("Can't save preferences, you probably don't have write "
|
||||
"access to the boot settings directory."), "OK", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return ret;
|
||||
}
|
||||
|
||||
path.Append("launch");
|
||||
BDirectory directory(path.Path());
|
||||
BEntry entry(&directory, serverPath.Leaf());
|
||||
|
||||
// Remove symbolic link
|
||||
entry.Remove();
|
||||
|
||||
if (autoStart) {
|
||||
// Put a symlink into ~/config/boot/launch
|
||||
if ((ret = directory.CreateSymLink(serverPath.Leaf(),
|
||||
serverPath.Path(), NULL) != B_OK)) {
|
||||
BAlert* alert = new BAlert("",
|
||||
_T("Can't enable notifications at startup time, you probably don't have "
|
||||
"write permission to the boot settings directory."), "OK", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
||||
(void)alert->Go();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GeneralView::Revert()
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GeneralView::_CanFindServer(entry_ref* ref)
|
||||
{
|
||||
// Try searching with be_roster
|
||||
if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK)
|
||||
return true;
|
||||
|
||||
// Try with a query and take the first result
|
||||
BVolumeRoster vroster;
|
||||
BVolume volume;
|
||||
char volName[B_FILE_NAME_LENGTH];
|
||||
|
||||
vroster.Rewind();
|
||||
|
||||
while (vroster.GetNextVolume(&volume) == B_OK) {
|
||||
if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery())
|
||||
continue;
|
||||
|
||||
volume.GetName(volName);
|
||||
|
||||
BQuery *query = new BQuery();
|
||||
query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")");
|
||||
query->SetVolume(&volume);
|
||||
query->Fetch();
|
||||
|
||||
if (query->GetNextRef(ref) == B_OK)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GeneralView::_IsServerRunning()
|
||||
{
|
||||
return be_roster->IsRunning(kNotificationServerSignature);
|
||||
}
|
||||
Reference in New Issue
Block a user