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:
Stephan Aßmus
2010-05-27 14:48:27 +00:00
parent 279d26af87
commit de9dcd41f8
50 changed files with 5460 additions and 4 deletions
@@ -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);
}