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

View File

@ -44,7 +44,7 @@ SYSTEM_BIN = "[" addattr alert arp base64 basename bash bc beep bootman bzip2
listport listres listsem listusb ln locate logger login logname ls lsindex
mail2mbox makebootable mbox2mail md5sum merge message mimeset mkdos mkdir
mkfifo mkfs mkindex mktemp modifiers mount mount_nfs mountvolume mv
netcat netstat nl nohup nproc
netcat netstat nl nohup notify nproc
od open
passwd paste patch pathchk pc ping play playfile playsound playwav pr prio
printenv printf profile ps ptx pwd
@ -70,7 +70,7 @@ SYSTEM_APPS = AboutSystem ActivityMonitor CharacterMap CodyCam DeskCalc Devices
;
SYSTEM_PREFERENCES = Appearance Backgrounds CPUFrequency DataTranslations
<preference>Deskbar E-mail FileTypes Fonts Keyboard Keymap Locale Media
Mouse Network OpenGL Printers Screen ScreenSaver Shortcuts Sounds Time
Mouse Network Notifications OpenGL Printers Screen ScreenSaver Shortcuts Sounds Time
Touchpad <preference>Tracker VirtualMemory
;
SYSTEM_DEMOS = BSnow Chart Clock Cortex FontDemo
@ -103,7 +103,7 @@ PRIVATE_SYSTEM_LIBS =
;
SYSTEM_SERVERS = app_server cddb_daemon debug_server input_server mail_daemon
media_addon_server media_server midi_server mount_server net_server
print_server registrar syslog_daemon
notification_server print_server registrar syslog_daemon
;
SYSTEM_NETWORK_DEVICES = ethernet loopback ;

View File

@ -0,0 +1,72 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_H
#define _NOTIFICATION_H
#include <Entry.h>
// notification types
enum notification_type {
B_INFORMATION_NOTIFICATION,
B_IMPORTANT_NOTIFICATION,
B_ERROR_NOTIFICATION,
B_PROGRESS_NOTIFICATION
};
class BBitmap;
class BList;
class BNotification {
public:
BNotification(notification_type type);
~BNotification();
notification_type Type() const;
const char* Application() const;
void SetApplication(const char* app);
const char* Title() const;
void SetTitle(const char* title);
const char* Content() const;
void SetContent(const char* content);
const char* MessageID() const;
void SetMessageID(const char* id);
float Progress() const;
void SetProgress(float progress);
const char* OnClickApp() const;
void SetOnClickApp(const char* app);
entry_ref* OnClickFile() const;
void SetOnClickFile(const entry_ref* file);
BList* OnClickRefs() const;
void AddOnClickRef(const entry_ref* ref);
BList* OnClickArgv() const;
void AddOnClickArg(const char* arg);
BBitmap* Icon() const;
void SetIcon(BBitmap* icon);
private:
notification_type fType;
char* fAppName;
char* fTitle;
char* fContent;
char* fID;
float fProgress;
char* fApp;
entry_ref* fFile;
BList* fRefs;
BList* fArgv;
BBitmap* fBitmap;
};
#endif // _NOTIFICATION_H

View File

@ -13,6 +13,7 @@
class BFile;
class BMimeType;
class BNodeInfo;
class BNotification;
struct app_info {
@ -116,6 +117,10 @@ class BRoster {
void AddToRecentFolders(const entry_ref *folder,
const char *appSig = 0) const;
// notifications
status_t Notify(BNotification* notification,
int32 timeout = -1) const;
// private/reserved stuff starts here
class Private;

View File

@ -0,0 +1,54 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _APP_USAGE_H
#define _APP_USAGE_H
#include <map>
#include <Entry.h>
#include <Flattenable.h>
#include <Notification.h>
#include <Roster.h>
#include <String.h>
class BMessage;
class NotificationReceived;
typedef std::map<BString, NotificationReceived*> notify_t;
class AppUsage : public BFlattenable {
public:
AppUsage();
AppUsage(entry_ref ref, const char* name,
bool allow = true);
~AppUsage();
virtual bool AllowsTypeCode(type_code code) const;
virtual status_t Flatten(void* buffer, ssize_t numBytes) const;
virtual ssize_t FlattenedSize() const;
virtual bool IsFixedSize() const;
virtual type_code TypeCode() const;
virtual status_t Unflatten(type_code code, const void* buffer,
ssize_t numBytes);
entry_ref Ref();
const char* Name();
bool Allowed(const char* title, notification_type type);
bool Allowed();
NotificationReceived* NotificationAt(int32 index);
int32 Notifications();
void AddNotification(NotificationReceived* notification);
private:
entry_ref fRef;
BString fName;
bool fAllow;
notify_t fNotifications;
};
#endif // _APP_USAGE_H

View File

@ -0,0 +1,46 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_RECEIVED_H
#define _NOTIFICATION_RECEIVED_H
#include <Flattenable.h>
#include <Roster.h>
#include <String.h>
class NotificationReceived : public BFlattenable {
public:
NotificationReceived();
NotificationReceived(const char* title, notification_type type,
bool enabled = true);
~NotificationReceived();
virtual bool AllowsTypeCode(type_code code) const;
virtual status_t Flatten(void *buffer, ssize_t numBytes) const;
virtual ssize_t FlattenedSize() const;
virtual bool IsFixedSize() const;
virtual type_code TypeCode() const;
virtual status_t Unflatten(type_code code, const void *buffer,
ssize_t numBytes);
const char* Title();
notification_type Type();
void SetType(notification_type type);
time_t LastReceived();
bool Allowed();
void SetTimeStamp(time_t time);
void UpdateTimeStamp();
private:
BString fTitle;
notification_type fType;
bool fEnabled;
time_t fLastReceived;
};
#endif // _NOTIFICATION_RECEIVED_H

View File

@ -0,0 +1,45 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATIONS_H
#define _NOTIFICATIONS_H
#include <Mime.h>
#define kNotificationServerSignature "application/x-vnd.Haiku-notification_server"
// Messages
const uint32 kNotificationMessage = 'nssm';
// Notification layout
enum infoview_layout {
TitleAboveIcon = 0,
AllTextRightOfIcon = 1
};
// Settings constants
extern const char* kSettingsDirectory;
extern const char* kFiltersSettings;
extern const char* kGeneralSettings;
extern const char* kDisplaySettings;
// General settings
extern const char* kAutoStartName;
extern const char* kTimeoutName;
// General default settings
const float kDefaultAutoStart = false;
const int32 kDefaultTimeout = 10;
// Display settings
extern const char* kWidthName;
extern const char* kIconSizeName;
extern const char* kLayoutName;
// Display default settings
const float kDefaultWidth = 300.0f;
const icon_size kDefaultIconSize = B_LARGE_ICON;
const infoview_layout kDefaultLayout = TitleAboveIcon;
#endif // _NOTIFICATIONS_H

View File

@ -9,6 +9,7 @@ UsePrivateSystemHeaders ;
SubDirHdrs $(HAIKU_TOP) src add-ons kernel file_cache ;
UseLibraryHeaders ncurses ;
UseLibraryHeaders termcap ;
UseLibraryHeaders icon ;
local haiku-utils_rsrc = [ FGristFiles haiku-utils.rsrc ] ;
@ -157,6 +158,11 @@ StdBinCommands
translate.cpp
: be translation $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
# standard commands that need libbe.so, libtranslation.so, libicon.a, libstdc++.so
StdBinCommands
notify.cpp
: be translation libicon.a $(TARGET_LIBSTDC++) : $(haiku-utils_rsrc) ;
# standard commands that need libbe.so, libmedia.so
StdBinCommands
installsound.cpp

325
src/bin/notify.cpp Normal file
View File

@ -0,0 +1,325 @@
/*
* Copyright 2010, Haiku, Inc. All rights reserved.
* Copyright 2008, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Application.h>
#include <Bitmap.h>
#include <IconUtils.h>
#include <List.h>
#include <Mime.h>
#include <Notification.h>
#include <Path.h>
#include <Roster.h>
#include <TranslationUtils.h>
const char* kSignature = "application/x-vnd.Haiku-notify";
const char* kSmallIconAttribute = "BEOS:M:STD_ICON";
const char* kLargeIconAttribute = "BEOS:L:STD_ICON";
const char* kIconAttribute = "BEOS:ICON";
const char *kTypeNames[] = {
"information",
"important",
"error",
"progress",
NULL
};
const int32 kErrorInitFail = 127;
const int32 kErrorArgumentsFail = 126;
class NotifyApp : public BApplication {
public:
NotifyApp();
virtual ~NotifyApp();
virtual void ReadyToRun();
virtual void ArgvReceived(int32 argc, char** argv);
bool GoodArguments() const { return fOk; }
private:
bool fOk;
notification_type fType;
const char* fAppName;
const char* fTitle;
const char* fMsgId;
float fProgress;
int32 fTimeout;
const char* fIconFile;
entry_ref fFileRef;
const char* fMessage;
const char* fApp;
bool fHasFile;
entry_ref fFile;
BList* fRefs;
BList* fArgv;
void _Usage() const;
BBitmap* _GetBitmap(const entry_ref* ref) const;
};
NotifyApp::NotifyApp()
:
BApplication(kSignature),
fOk(false),
fType(B_INFORMATION_NOTIFICATION),
fAppName(NULL),
fTitle(NULL),
fMsgId(NULL),
fProgress(0.0f),
fTimeout(0),
fIconFile(NULL),
fMessage(NULL),
fApp(NULL),
fHasFile(false)
{
fRefs = new BList();
fArgv = new BList();
}
NotifyApp::~NotifyApp()
{
if (fAppName)
free((void*)fAppName);
if (fTitle)
free((void*)fTitle);
if (fMsgId)
free((void*)fMsgId);
if (fIconFile)
free((void*)fIconFile);
if (fMessage)
free((void*)fMessage);
if (fApp)
free((void*)fApp);
int32 i;
void* item;
for (i = 0; item = fRefs->ItemAt(i); i++)
delete item;
delete fRefs;
for (i = 0; item = fArgv->ItemAt(i); i++) {
if (item != NULL)
free(item);
}
delete fArgv;
}
void
NotifyApp::ArgvReceived(int32 argc, char** argv)
{
const uint32 kArgCount = argc - 1;
uint32 index = 1;
// Look for valid options
for (; index <= kArgCount; ++index) {
if (argv[index][0] == '-' && argv[index][1] == '-') {
const char* option = argv[index] + 2;
if (++index > kArgCount) {
// No argument to option
fprintf(stderr, "Missing argument to option --%s\n\n", option);
return;
}
const char* argument = argv[index];
if (strcmp(option, "type") == 0) {
for (int32 i = 0; kTypeNames[i]; i++) {
if (strncmp(kTypeNames[i], argument, strlen(argument)) == 0)
fType = (notification_type)i;
}
} else if (strcmp(option, "app") == 0) {
fAppName = strdup(argument);
} else if (strcmp(option, "title") == 0) {
fTitle = strdup(argument);
} else if (strcmp(option, "messageID") == 0) {
fMsgId = strdup(argument);
} else if (strcmp(option, "progress") == 0) {
fProgress = atof(argument);
} else if (strcmp(option, "timeout") == 0) {
fTimeout = atol(argument);
} else if (strcmp(option, "icon") == 0) {
fIconFile = strdup(argument);
if (get_ref_for_path(fIconFile, &fFileRef) < B_OK) {
fprintf(stderr, "Bad icon path!\n\n");
return;
}
} else if (strcmp(option, "onClickApp") == 0)
fApp = strdup(argument);
else if (strcmp(option, "onClickFile") == 0) {
if (get_ref_for_path(argument, &fFile) != B_OK) {
fprintf(stderr, "Bad path for --onClickFile!\n\n");
return;
}
fHasFile = true;
} else if (strcmp(option, "onClickRef") == 0) {
entry_ref ref;
if (get_ref_for_path(argument, &ref) != B_OK) {
fprintf(stderr, "Bad path for --onClickRef!\n\n");
return;
}
fRefs->AddItem((void*)new BEntry(&ref));
} else if (strcmp(option, "onClickArgv") == 0)
fArgv->AddItem((void*)strdup(argument));
else {
// Unrecognized option
fprintf(stderr, "Unrecognized option --%s\n\n", option);
return;
}
} else
// Option doesn't start with '--'
break;
if (index == kArgCount) {
// No text argument provided, only '--' arguments
fprintf(stderr, "Missing message argument!\n\n");
return;
}
}
// Check for missing arguments
if (!fAppName) {
fprintf(stderr, "Missing --app argument!\n\n");
return;
}
if (!fTitle) {
fprintf(stderr, "Missing --title argument!\n\n");
return;
}
fMessage = strdup(argv[index]);
fOk = true;
}
void
NotifyApp::_Usage() const
{
fprintf(stderr, "Usage: notify [OPTION]... [MESSAGE]\n"
"Send notifications to notification_server.\n"
" --type <type>\tNotification type,\n"
" \t <type>: ");
for (int32 i = 0; kTypeNames[i]; i++)
fprintf(stderr, kTypeNames[i + 1] ? "%s|" : "%s\n", kTypeNames[i]);
fprintf(stderr,
" --app <app name>\tApplication name\n"
" --title <title>\tMessage title\n"
" --messageID <msg id>\tMessage ID\n"
" --progress <float>\tProgress, value between 0.0 and 1.0 - if type is set to progress\n"
" --timeout <secs>\tSpecify timeout\n"
" --onClickApp <signature>\tApplication to open when notification is clicked\n"
" --onClickFile <fullpath>\tFile to open when notification is clicked\n"
" --onClickRef <fullpath>\tFile to open with the application when notification is clicked\n"
" --onClickArgv <arg>\tArgument to the application when notification is clicked\n"
" --icon <icon file> Icon\n");
}
BBitmap*
NotifyApp::_GetBitmap(const entry_ref* ref) const
{
BBitmap* bitmap = NULL;
// First try by contents
bitmap = BTranslationUtils::GetBitmap(ref);
if (bitmap)
return bitmap;
// Then, try reading its attribute
BNode node(BPath(ref).Path());
bitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1,
(float)B_LARGE_ICON - 1), B_RGBA32);
if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute,
kLargeIconAttribute, B_LARGE_ICON, bitmap) != B_OK) {
delete bitmap;
bitmap = NULL;
}
return bitmap;
}
void
NotifyApp::ReadyToRun()
{
if (GoodArguments()) {
BNotification* msg = new BNotification(fType);
msg->SetApplication(fAppName);
msg->SetTitle(fTitle);
msg->SetContent(fMessage);
if (fMsgId)
msg->SetMessageID(fMsgId);
if (fType == B_PROGRESS_NOTIFICATION)
msg->SetProgress(fProgress);
if (fIconFile) {
BBitmap* bitmap = _GetBitmap(&fFileRef);
if (bitmap)
msg->SetIcon(bitmap);
}
if (fApp)
msg->SetOnClickApp(fApp);
if (fHasFile)
msg->SetOnClickFile(&fFile);
int32 i;
void* item;
for (i = 0; item = fRefs->ItemAt(i); i++) {
BEntry* entry = (BEntry*)item;
entry_ref ref;
if (entry->GetRef(&ref) == B_OK)
msg->AddOnClickRef(&ref);
}
for (i = 0; item = fArgv->ItemAt(i); i++) {
const char* arg = (const char*)item;
msg->AddOnClickArg(arg);
}
be_roster->Notify(msg, fTimeout);
} else
_Usage();
Quit();
}
int
main(int argc, char** argv)
{
NotifyApp app;
if (app.InitCheck() != B_OK)
return kErrorInitFail;
app.Run();
if (!app.GoodArguments())
return kErrorArgumentsFail;
return 0;
}

View File

@ -81,6 +81,7 @@ SubInclude HAIKU_TOP src kits media ;
SubInclude HAIKU_TOP src kits midi ;
SubInclude HAIKU_TOP src kits midi2 ;
SubInclude HAIKU_TOP src kits network ;
SubInclude HAIKU_TOP src kits notification ;
SubInclude HAIKU_TOP src kits opengl ;
SubInclude HAIKU_TOP src kits print ;
SubInclude HAIKU_TOP src kits screensaver ;

View File

@ -17,7 +17,7 @@ if $(RUN_WITHOUT_APP_SERVER) != 0 {
SubDirC++Flags $(defines) ;
}
UsePrivateHeaders shared app interface kernel ;
UsePrivateHeaders shared app interface kernel notification ;
UsePrivateSystemHeaders ;
SetSubDirSupportedPlatforms haiku libbe_test ;
@ -45,6 +45,7 @@ MergeObject <libbe>app_kit.o :
MessageRunner.cpp
Messenger.cpp
MessageUtils.cpp
Notification.cpp
PropertyInfo.cpp
PortLink.cpp
RegistrarDefs.cpp

View File

@ -0,0 +1,216 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdlib.h>
#include <string.h>
#include <Bitmap.h>
#include <List.h>
#include <Message.h>
#include <Notification.h>
BNotification::BNotification(notification_type type)
:
fType(type),
fAppName(NULL),
fTitle(NULL),
fContent(NULL),
fID(NULL),
fApp(NULL),
fFile(NULL),
fBitmap(NULL)
{
fRefs = new BList();
fArgv = new BList();
}
BNotification::~BNotification()
{
if (fAppName)
free(fAppName);
if (fTitle)
free(fTitle);
if (fContent)
free(fContent);
if (fID)
free(fID);
if (fApp)
free(fApp);
delete fRefs;
delete fArgv;
}
notification_type
BNotification::Type() const
{
return fType;
}
const char*
BNotification::Application() const
{
return fAppName;
}
void
BNotification::SetApplication(const char* app)
{
free(fAppName);
fAppName = NULL;
if (app)
fAppName = strdup(app);
}
const char*
BNotification::Title() const
{
return fTitle;
}
void
BNotification::SetTitle(const char* title)
{
free(fTitle);
fTitle = NULL;
if (title)
fTitle = strdup(title);
}
const char*
BNotification::Content() const
{
return fContent;
}
void
BNotification::SetContent(const char* content)
{
free(fContent);
fContent = NULL;
if (content)
fContent = strdup(content);
}
const char*
BNotification::MessageID() const
{
return fID;
}
void
BNotification::SetMessageID(const char* id)
{
free(fID);
fID = NULL;
if (id)
fID = strdup(id);
}
float
BNotification::Progress() const
{
return fProgress;
}
void
BNotification::SetProgress(float progress)
{
fProgress = progress;
}
const char*
BNotification::OnClickApp() const
{
return fApp;
}
void
BNotification::SetOnClickApp(const char* app)
{
free(fApp);
fApp = NULL;
if (app)
fApp = strdup(app);
}
entry_ref*
BNotification::OnClickFile() const
{
return fFile;
}
void
BNotification::SetOnClickFile(const entry_ref* file)
{
fFile = (entry_ref*)file;
}
BList*
BNotification::OnClickRefs() const
{
return fRefs;
}
void
BNotification::AddOnClickRef(const entry_ref* ref)
{
fRefs->AddItem((void*)ref);
}
BList*
BNotification::OnClickArgv() const
{
return fArgv;
}
void
BNotification::AddOnClickArg(const char* arg)
{
fArgv->AddItem((void*)arg);
}
BBitmap*
BNotification::Icon() const
{
return fBitmap;
}
void
BNotification::SetIcon(BBitmap* icon)
{
fBitmap = icon;
}

View File

@ -25,6 +25,7 @@
#include <AppFileInfo.h>
#include <Application.h>
#include <Bitmap.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
@ -35,6 +36,8 @@
#include <Mime.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Notification.h>
#include <notification/Notifications.h>
#include <OS.h>
#include <Path.h>
#include <Query.h>
@ -1650,6 +1653,63 @@ BRoster::AddToRecentFolders(const entry_ref* folder, const char* appSig) const
DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error 0x%lx\n", err));
}
/*! \brief Sends a notification to the notification_server.
The notification is delivered synchronously to the notification_server,
that will displays it according to its settings and filters.
\param notification Notification message.
\param timeout Seconds after the message fades out.
\return
- \c B_OK: Everything went fine.
- \c B_BAD_PORT_ID: A connection to notification_server could not be
established or the server is not up and running anymore.
*/
status_t
BRoster::Notify(BNotification* notification, int32 timeout) const
{
BMessage msg(kNotificationMessage);
msg.AddInt32("type", (int32)notification->Type());
msg.AddString("app", notification->Application());
msg.AddString("title", notification->Title());
msg.AddString("content", notification->Content());
if (notification->MessageID())
msg.AddString("messageID", notification->MessageID());
if (notification->Type() == B_PROGRESS_NOTIFICATION)
msg.AddFloat("progress", notification->Progress());
if (notification->OnClickApp())
msg.AddString("onClickApp", notification->OnClickApp());
if (notification->OnClickFile())
msg.AddRef("onClickFile", notification->OnClickFile());
int32 i;
BList* refs = notification->OnClickRefs();
for (i = 0; i < refs->CountItems(); i++)
msg.AddRef("onClickRef", (entry_ref*)refs->ItemAt(i));
BList* argv = notification->OnClickArgv();
for (i = 0; i < argv->CountItems(); i++)
msg.AddString("onClickArgv", (const char*)argv->ItemAt(i));
BBitmap* icon = notification->Icon();
BMessage archive;
if (icon && icon->Archive(&archive) == B_OK)
msg.AddMessage("icon", &archive);
// Custom time out
if (timeout > 0)
msg.AddInt32("timeout", timeout);
// Send message
BMessenger server(kNotificationServerSignature);
return server.SendMessage(&msg);
}
// #pragma mark - Private or reserved

View File

@ -0,0 +1,204 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Message.h>
#include <AppUsage.h>
#include <NotificationReceived.h>
const type_code kTypeCode = 'ipau';
AppUsage::AppUsage()
:
fName(""),
fAllow(true)
{
}
AppUsage::AppUsage(entry_ref ref, const char* name, bool allow)
:
fRef(ref),
fName(name),
fAllow(allow)
{
}
AppUsage::~AppUsage()
{
notify_t::iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
delete nIt->second;
}
bool
AppUsage::AllowsTypeCode(type_code code) const
{
return code == kTypeCode;
}
status_t
AppUsage::Flatten(void* buffer, ssize_t numBytes) const
{
BMessage msg;
msg.AddString("app_name", fName);
msg.AddRef("app_ref", &fRef);
msg.AddBool("app_allow", fAllow);
notify_t::const_iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
msg.AddFlat("notify", nIt->second);
if (numBytes < msg.FlattenedSize())
return B_ERROR;
return msg.Flatten((char*)buffer, numBytes);
}
ssize_t
AppUsage::FlattenedSize() const
{
BMessage msg;
msg.AddString("app_name", fName);
msg.AddRef("app_ref", &fRef);
msg.AddBool("app_allow", fAllow);
notify_t::const_iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
msg.AddFlat("notify", nIt->second);
return msg.FlattenedSize();
}
bool
AppUsage::IsFixedSize() const
{
return false;
}
type_code
AppUsage::TypeCode() const
{
return kTypeCode;
}
status_t
AppUsage::Unflatten(type_code code, const void* buffer,
ssize_t numBytes)
{
if (code != kTypeCode)
return B_ERROR;
BMessage msg;
status_t status = B_ERROR;
status = msg.Unflatten((const char*)buffer);
if (status == B_OK) {
msg.FindString("app_name", &fName);
msg.FindRef("app_ref", &fRef);
msg.FindBool("app_allow", &fAllow);
type_code type;
int32 count = 0;
status = msg.GetInfo("notify", &type, &count);
if (status != B_OK)
return status;
for (int32 i = 0; i < count; i++) {
NotificationReceived *notify = new NotificationReceived();
msg.FindFlat("notify", i, notify);
fNotifications[notify->Title()] = notify;
}
status = B_OK;
}
return status;
}
entry_ref
AppUsage::Ref()
{
return fRef;
}
const char*
AppUsage::Name()
{
return fName.String();
}
bool
AppUsage::Allowed(const char* title, notification_type type)
{
bool allowed = fAllow;
if (allowed) {
notify_t::iterator nIt = fNotifications.find(title);
if (nIt == fNotifications.end()) {
allowed = true;
fNotifications[title] = new NotificationReceived(title, type);
} else {
allowed = nIt->second->Allowed();
nIt->second->UpdateTimeStamp();
nIt->second->SetType(type);
}
}
return allowed;
}
bool
AppUsage::Allowed()
{
return fAllow;
}
NotificationReceived*
AppUsage::NotificationAt(int32 index)
{
notify_t::iterator nIt = fNotifications.begin();
for (int32 i = 0; i < index; i++)
nIt++;
return nIt->second;
}
int32
AppUsage::Notifications()
{
return fNotifications.size();
}
void
AppUsage::AddNotification(NotificationReceived* notification)
{
fNotifications[notification->Title()] = notification;
}

View File

@ -0,0 +1,9 @@
SubDir HAIKU_TOP src kits notification ;
UsePrivateHeaders notification ;
StaticLibrary libnotification.a :
AppUsage.cpp
NotificationReceived.cpp
Notifications.cpp
;

View File

@ -0,0 +1,164 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Message.h>
#include <Notification.h>
#include <NotificationReceived.h>
const type_code kTypeCode = 'ipnt';
NotificationReceived::NotificationReceived()
:
fTitle(""),
fType(B_INFORMATION_NOTIFICATION),
fEnabled(false),
fLastReceived(time(NULL))
{
}
NotificationReceived::NotificationReceived(const char* title,
notification_type type, bool enabled)
:
fTitle(title),
fType(type),
fEnabled(enabled),
fLastReceived(time(NULL))
{
}
NotificationReceived::~NotificationReceived()
{
}
bool
NotificationReceived::AllowsTypeCode(type_code code) const
{
return code == kTypeCode;
}
status_t
NotificationReceived::Flatten(void* buffer, ssize_t numBytes) const
{
BMessage msg;
msg.AddString("notify_title", fTitle);
msg.AddInt32("notify_type", (int32)fType);
msg.AddInt32("notify_lastreceived", (int32)fLastReceived);
msg.AddBool("notify_enabled", fEnabled);
if (numBytes < msg.FlattenedSize())
return B_ERROR;
return msg.Flatten((char*)buffer, numBytes);
}
ssize_t
NotificationReceived::FlattenedSize() const
{
BMessage msg;
msg.AddString("notify_title", fTitle);
msg.AddInt32("notify_type", (int32)fType);
msg.AddInt32("notify_lastreceived", (int32)fLastReceived);
msg.AddBool("notify_enabled", fEnabled);
return msg.FlattenedSize();
}
bool
NotificationReceived::IsFixedSize() const
{
return false;
}
type_code
NotificationReceived::TypeCode() const
{
return kTypeCode;
}
status_t
NotificationReceived::Unflatten(type_code code, const void* buffer,
ssize_t numBytes)
{
if (code != kTypeCode)
return B_ERROR;
BMessage msg;
status_t error = msg.Unflatten((const char*)buffer);
if (error == B_OK) {
msg.FindString("notify_title", &fTitle);
msg.FindInt32("notify_type", (int32 *)&fType);
msg.FindInt32("notify_lastreceived", (int32 *)&fLastReceived);
msg.FindBool("notify_enabled", &fEnabled);
}
return error;
}
const char*
NotificationReceived::Title()
{
return fTitle.String();
}
notification_type
NotificationReceived::Type()
{
return fType;
}
void
NotificationReceived::SetType(notification_type type)
{
fType = type;
}
time_t
NotificationReceived::LastReceived()
{
return fLastReceived;
}
bool
NotificationReceived::Allowed()
{
return fEnabled;
}
void
NotificationReceived::UpdateTimeStamp()
{
fLastReceived = time(NULL);
}
void
NotificationReceived::SetTimeStamp(time_t time)
{
fLastReceived = time;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <Notifications.h>
// Settings constants
const char* kSettingsDirectory = "system/notifications";
const char* kFiltersSettings = "filters";
const char* kGeneralSettings = "general";
const char* kDisplaySettings = "display";
// General settings
const char* kAutoStartName = "auto-start";
const char* kTimeoutName = "timeout";
// Display settings
const char* kWidthName = "width";
const char* kIconSizeName = "icon size";
const char* kLayoutName = "layout";

View File

@ -17,6 +17,7 @@ SubInclude HAIKU_TOP src preferences mail ;
SubInclude HAIKU_TOP src preferences media ;
SubInclude HAIKU_TOP src preferences mouse ;
SubInclude HAIKU_TOP src preferences network ;
SubInclude HAIKU_TOP src preferences notifications ;
SubInclude HAIKU_TOP src preferences opengl ;
SubInclude HAIKU_TOP src preferences print ;
SubInclude HAIKU_TOP src preferences screen ;

View File

@ -0,0 +1,223 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <Alert.h>
#include <Directory.h>
#include <Message.h>
#include <FindDirectory.h>
#include <GroupLayout.h>
#include <GridLayoutBuilder.h>
#include <SpaceLayoutItem.h>
#include <TextControl.h>
#include <Menu.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <Mime.h>
#include <Node.h>
#include <notification/Notifications.h>
#include <Path.h>
#include "DisplayView.h"
#include "SettingsHost.h"
#define _T(str) (str)
DisplayView::DisplayView(SettingsHost* host)
:
SettingsPane("display", host)
{
// Window width
fWindowWidth = new BTextControl(_T("Window width:"), NULL,
new BMessage(kSettingChanged));
// Icon size
fIconSize = new BMenu("iconSize");
fIconSize->AddItem(new BMenuItem(_T("Mini icon"),
new BMessage(kSettingChanged)));
fIconSize->AddItem(new BMenuItem(_T("Large icon"),
new BMessage(kSettingChanged)));
fIconSize->SetLabelFromMarked(true);
fIconSizeField = new BMenuField(_T("Icon size:"), fIconSize);
// Title position
fTitlePosition = new BMenu("titlePosition");
fTitlePosition->AddItem(new BMenuItem(_T("Above icon"),
new BMessage(kSettingChanged)));
fTitlePosition->AddItem(new BMenuItem(_T("Right of icon"),
new BMessage(kSettingChanged)));
fTitlePosition->SetLabelFromMarked(true);
fTitlePositionField = new BMenuField(_T("Title position:"), fTitlePosition);
// Load settings
Load();
// Calculate inset
float inset = ceilf(be_plain_font->Size() * 0.7f);
SetLayout(new BGroupLayout(B_VERTICAL));
AddChild(BGridLayoutBuilder(inset, inset)
.Add(fWindowWidth->CreateLabelLayoutItem(), 0, 0)
.Add(fWindowWidth->CreateTextViewLayoutItem(), 1, 0)
.Add(fIconSizeField->CreateLabelLayoutItem(), 0, 1)
.Add(fIconSizeField->CreateMenuBarLayoutItem(), 1, 1)
.Add(fTitlePositionField->CreateLabelLayoutItem(), 0, 2)
.Add(fTitlePositionField->CreateMenuBarLayoutItem(), 1, 2)
.Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1)
);
}
void
DisplayView::AttachedToWindow()
{
fWindowWidth->SetTarget(this);
fIconSize->SetTargetForItems(this);
fTitlePosition->SetTargetForItems(this);
}
void
DisplayView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kSettingChanged:
SettingsPane::MessageReceived(msg);
break;
default:
BView::MessageReceived(msg);
}
}
status_t
DisplayView::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(kDisplaySettings);
BFile file(path.Path(), B_READ_ONLY);
BMessage settings;
settings.Unflatten(&file);
char buffer[255];
int32 setting;
BMenuItem* item = NULL;
float width;
if (settings.FindFloat(kWidthName, &width) != B_OK)
width = kDefaultWidth;
(void)sprintf(buffer, "%.2f", width);
fWindowWidth->SetText(buffer);
icon_size iconSize;
if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
iconSize = kDefaultIconSize;
else
iconSize = (icon_size)setting;
if (iconSize == B_MINI_ICON)
item = fIconSize->ItemAt(0);
else
item = fIconSize->ItemAt(1);
if (item)
item->SetMarked(true);
infoview_layout layout;
if (settings.FindInt32(kLayoutName, &setting) != B_OK)
layout = kDefaultLayout;
else {
switch (setting) {
case 0:
layout = TitleAboveIcon;
break;
case 1:
layout = AllTextRightOfIcon;
break;
default:
layout = kDefaultLayout;
}
}
item = fTitlePosition->ItemAt(layout);
if (item)
item->SetMarked(true);
return B_OK;
}
status_t
DisplayView::Save()
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return B_ERROR;
path.Append(kSettingsDirectory);
path.Append(kDisplaySettings);
BMessage settings;
float width = atof(fWindowWidth->Text());
settings.AddFloat(kWidthName, width);
icon_size iconSize = kDefaultIconSize;
switch (fIconSize->IndexOf(fIconSize->FindMarked())) {
case 0:
iconSize = B_MINI_ICON;
break;
default:
iconSize = B_LARGE_ICON;
}
settings.AddInt32(kIconSizeName, (int32)iconSize);
int32 layout = fTitlePosition->IndexOf(fTitlePosition->FindMarked());
if (layout == B_ERROR)
layout = (int32)kDefaultLayout;
settings.AddInt32(kLayoutName, layout);
// Save settings file
BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
status_t ret = settings.Flatten(&file);
if (ret != B_OK) {
BAlert* alert = new BAlert("",
_T("Can't save preferenes, you probably don't have write "
"access to the settings directory or the disk is full."), "OK", NULL, NULL,
B_WIDTH_AS_USUAL, B_STOP_ALERT);
(void)alert->Go();
return ret;
}
return B_OK;
}
status_t
DisplayView::Revert()
{
return B_ERROR;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _DISPLAY_VIEW_H
#define _DISPLAY_VIEW_H
#include "SettingsPane.h"
class BTextControl;
class BMenu;
class BMenuField;
class DisplayView : public SettingsPane {
public:
DisplayView(SettingsHost* host);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
// SettingsPane hooks
status_t Load();
status_t Save();
status_t Revert();
private:
BTextControl* fWindowWidth;
BMenu* fIconSize;
BMenuField* fIconSizeField;
BMenu* fTitlePosition;
BMenuField* fTitlePositionField;
};
#endif // _DISPLAY_VIEW_H

View File

@ -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, pierluigi.fiorini@gmail.com
*/
#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);
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _GENERAL_VIEW_H
#define _GENERAL_VIEW_H
#include "SettingsPane.h"
class BCheckBox;
class BButton;
class BStringView;
class BTextControl;
class GeneralView : public SettingsPane {
public:
GeneralView(SettingsHost* host);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
// SettingsPane hooks
status_t Load();
status_t Save();
status_t Revert();
private:
BButton* fServerButton;
BStringView* fStatusLabel;
BCheckBox* fAutoStart;
BTextControl* fTimeout;
BCheckBox* fHideAll;
bool _CanFindServer(entry_ref* ref);
bool _IsServerRunning();
};
#endif // _GENERAL_VIEW_H

View File

@ -0,0 +1,117 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdio.h>
#include <Bitmap.h>
#include "IconItem.h"
#include "IconRule.h"
const int32 kEdgeOffset = 4;
BIconItem::BIconItem(BView* owner, const char* label, BBitmap* icon)
:
fIcon(icon),
fLabel(label),
fSelected(false),
fOwner(owner)
{
}
BIconItem::~BIconItem()
{
delete fIcon;
}
void BIconItem::Draw()
{
if (IsSelected()) {
rgb_color color;
rgb_color origHigh;
origHigh = fOwner->HighColor();
if (IsSelected())
color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
else
color = fOwner->ViewColor();
fOwner->SetHighColor(color);
fOwner->FillRect(fFrame);
fOwner->SetHighColor(origHigh);
}
if (fIcon)
{
fOwner->SetDrawingMode(B_OP_ALPHA);
fOwner->DrawBitmap(fIcon, BPoint(fFrame.top + kEdgeOffset,
fFrame.left + kEdgeOffset));
fOwner->SetDrawingMode(B_OP_COPY);
}
if (IsSelected())
fOwner->SetDrawingMode(B_OP_OVER);
#if 0
fOwner->MovePenTo(frame.left + kEdgeOffset + fIconWidth + kEdgeOffset,
frame.bottom - fFontOffset);
#else
fOwner->MovePenTo(fFrame.left, fFrame.bottom - 10);
#endif
fOwner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
fOwner->DrawString(" ");
fOwner->DrawString(fLabel.String());
}
const char*
BIconItem::Label() const
{
return fLabel.String();
}
void
BIconItem::SetFrame(BRect frame)
{
fFrame = frame;
}
BRect
BIconItem::Frame() const
{
return fFrame;
}
void
BIconItem::Select()
{
fSelected = true;
Draw();
}
void
BIconItem::Deselect()
{
fSelected = false;
Draw();
}
bool
BIconItem::IsSelected() const
{
return fSelected;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _ICON_ITEM_H
#define _ICON_ITEM_H
#include <View.h>
#include <String.h>
class BBitmap;
class BIconItem {
public:
BIconItem(BView* owner, const char* label, BBitmap* icon);
virtual ~BIconItem();
void Draw();
const char* Label() const;
void SetFrame(BRect frame);
BRect Frame() const;
void Select();
void Deselect();
bool IsSelected() const;
private:
BString fLabel;
BBitmap* fIcon;
bool fSelected;
BView* fOwner;
BRect fFrame;
};
#endif // _ICON_ITEM_H

View File

@ -0,0 +1,270 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdio.h>
#include <Message.h>
#include <Bitmap.h>
#include <Window.h>
#include <List.h>
#include "IconRule.h"
#include "IconItem.h"
const int32 kEdgeOffset = 8;
const int32 kBorderOffset = 1;
// TODO: Do we need to inherit from BControl?
BIconRule::BIconRule(const char* name)
:
BView(name, B_WILL_DRAW),
fSelIndex(-1)
{
fIcons = new BList();
}
BIconRule::~BIconRule()
{
delete fIcons;
}
status_t
BIconRule::Invoke(BMessage* message)
{
bool notify = false;
uint32 kind = InvokeKind(&notify);
BMessage clone(kind);
status_t err = B_BAD_VALUE;
if (!message && !notify)
message = Message();
if (!message) {
if (!IsWatched())
return err;
} else
clone = *message;
clone.AddInt64("when", (int64)system_time());
clone.AddPointer("source", this);
clone.AddMessenger("be:sender", BMessenger(this));
clone.AddInt32("index", fSelIndex);
if (message)
err = BInvoker::Invoke(&clone);
SendNotices(kind, &clone);
return err;
}
void
BIconRule::AttachedToWindow()
{
BView::AttachedToWindow();
if (!Messenger().IsValid())
SetTarget(Window(), NULL);
}
void
BIconRule::MouseDown(BPoint point)
{
if (!IsFocus()) {
MakeFocus();
Sync();
Window()->UpdateIfNeeded();
}
int32 index = IndexOf(point);
if (index > -1)
SlideToIcon(index);
}
void
BIconRule::Draw(BRect updateRect)
{
int32 count = CountIcons();
if (count == 0)
return;
rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color lightColor = tint_color(panelColor, B_DARKEN_1_TINT);
rgb_color darkColor = tint_color(lightColor, B_DARKEN_2_TINT);
SetHighColor(darkColor);
StrokeLine(Bounds().LeftTop(), Bounds().RightTop());
StrokeLine(Bounds().LeftTop(), Bounds().LeftBottom());
SetHighColor(lightColor);
StrokeLine(Bounds().LeftBottom(), Bounds().RightBottom());
StrokeLine(Bounds().RightTop(), Bounds().RightBottom());
BRect itemFrame(kEdgeOffset, kBorderOffset, -1, kBorderOffset + 64);
for (int32 i = 0; i < count; i++) {
BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(i));
float width = StringWidth(item->Label()) + StringWidth(" ") * 2;
if (width < 64.0f)
width = 64.0f;
itemFrame.right = itemFrame.left + width - 1;
if (itemFrame.Intersects(updateRect)) {
item->SetFrame(itemFrame);
item->Draw();
}
itemFrame.left = itemFrame.right + kEdgeOffset + 1;
}
}
BMessage*
BIconRule::SelectionMessage() const
{
return fMessage;
}
void
BIconRule::SetSelectionMessage(BMessage* message)
{
delete fMessage;
fMessage = message;
}
int32
BIconRule::AddIcon(const char* label, const BBitmap* icon)
{
BIconItem* item = new BIconItem(this, label, (BBitmap*)icon);
if (CountIcons() == 0) {
item->Select();
fSelIndex = 0;
}
(void)fIcons->AddItem(item);
}
void
BIconRule::RemoveIconAt(int32 index)
{
}
void
BIconRule::RemoveAllIcons()
{
}
int32
BIconRule::CountIcons() const
{
return fIcons->CountItems();
}
void
BIconRule::SlideToIcon(int32 index)
{
// Ignore invalid items
if ((index < 0) || (index > CountIcons() - 1))
return;
BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(index));
if (item) {
// Deselect previously selected item
if (fSelIndex > -1) {
BIconItem* selItem = static_cast<BIconItem*>(fIcons->ItemAt(fSelIndex));
selItem->Deselect();
}
// Select this item
item->Select();
fSelIndex = index;
Invalidate();
// Invoke notification
InvokeNotify(fMessage, B_CONTROL_MODIFIED);
}
}
void
BIconRule::SlideToNext()
{
if (fSelIndex + 1 < CountIcons() - 1)
return;
SlideToIcon(fSelIndex + 1);
}
void
BIconRule::SlideToPrevious()
{
if (fSelIndex <= 0)
return;
SlideToIcon(fSelIndex - 1);
}
int32
BIconRule::IndexOf(BPoint point)
{
int32 low = 0;
int32 high = fIcons->CountItems() - 1;
int32 mid = -1;
float frameLeft = -1.0f;
float frameRight = 1.0f;
// Binary search the list
while (high >= low) {
mid = (low + high) / 2;
BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(mid));
frameLeft = item->Frame().left;
frameRight = item->Frame().right;
if (point.x < frameLeft)
high = mid - 1;
else if (point.x > frameRight)
low = mid + 1;
else
return mid;
}
return -1;
}
BSize
BIconRule::MinSize()
{
BSize minSize(BView::MinSize());
minSize.height = 64 + (kBorderOffset * 2);
return minSize;
}
BSize
BIconRule::MaxSize()
{
return BView::MaxSize();
}
BSize
BIconRule::PreferredSize()
{
return BView::PreferredSize();
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _ICON_RULE_H
#define _ICON_RULE_H
#include <Invoker.h>
#include <View.h>
class BList;
class BMessage;
class BIconRule : public BView, public BInvoker {
public:
BIconRule(const char* name);
virtual ~BIconRule();
status_t Invoke(BMessage* message);
virtual void AttachedToWindow();
virtual void MouseDown(BPoint point);
virtual void Draw(BRect updateRect);
BMessage* SelectionMessage() const;
void SetSelectionMessage(BMessage* message);
int32 AddIcon(const char* label, const BBitmap* icon);
void RemoveIconAt(int32 index);
void RemoveAllIcons();
int32 CountIcons() const;
void SlideToIcon(int32 index);
void SlideToNext();
void SlideToPrevious();
int32 IndexOf(BPoint point);
virtual BSize MinSize();
virtual BSize MaxSize();
virtual BSize PreferredSize();
private:
BList* fIcons;
int32 fSelIndex;
BMessage* fMessage;
};
#endif // _ICON_RULE_H

View File

@ -0,0 +1,20 @@
SubDir HAIKU_TOP src preferences notifications ;
UsePrivateHeaders interface ;
Application Notifications :
Notifications.cpp
PrefletWin.cpp
PrefletView.cpp
SettingsPane.cpp
GeneralView.cpp
DisplayView.cpp
NotificationsView.cpp
IconRule.cpp
IconItem.cpp
: be translation libcolumnlistview.a libnotification.a $(TARGET_LIBSTDC++)
: Notifications.rdef
;
Depends Notifications : libcolumnlistview.a ;
Depends Notifications : libnotification.a ;

View File

@ -0,0 +1,53 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Application.h>
#include "PrefletWin.h"
class PrefletApp : public BApplication {
public:
PrefletApp();
virtual void ReadyToRun();
virtual bool QuitRequested();
private:
PrefletWin* fWindow;
};
PrefletApp::PrefletApp()
:
BApplication("application/x-vnd.Haiku-Notifications")
{
}
void
PrefletApp::ReadyToRun()
{
fWindow = new PrefletWin;
}
bool
PrefletApp::QuitRequested()
{
return true;
}
int
main(int argc, char* argv[])
{
PrefletApp app;
app.Run();
return 0;
}

View File

@ -0,0 +1,33 @@
resource app_signature "application/x-vnd.Haiku-Notifications";
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_ALPHA,
internal = 0,
short_info = "Notifications",
long_info = "Notifications ©2009 Pier Luigi Fiorini ©2010 Haiku"
};
resource app_flags B_SINGLE_LAUNCH;
resource vector_icon {
$"6E636966060401780501020106023A06C63967B3B838E138A51E4B2EAA46CD17"
$"C3677F93FFFFFFFF02030603BF80A63DDD21BA0661BBABD34BDD9CC05D0900FF"
$"DA8F3EFFAA008290600202030603BBE0A53C15CEB9A968B967964C2B2D459EFF"
$"00FFDA8F29FFAA0054906002020106053CFCDB3CFBB1BB18EC3B1A2148FE2D4A"
$"C91D89FFFFFFA8BAC6D1C3677F93E2BAC6D1FFFFFFFF040406FE034460C6E460"
$"C4C6605460575C565E585A5E345E3460335D2E61305D2E4400054E244E24C5DB"
$"B453C756B4915123C756B491562655255727C95329C95329592B592E592D592E"
$"0616FFABAAAAAA0A4E244E24C5DBB453C756B4915123C756B491562655255727"
$"C95329C95329592B592E592D592E4957475A425C3C5B365832552F532C50284C"
$"264924462242213F203B2136233425330A1A4957475A425C3C5B365832552F53"
$"2C50284C264924462242213F203B2136233425332C333135353738393F3F4344"
$"4547484D4950070A000100000A01010130222201178822040A01010230222201"
$"178400040A02010130222201178422040A0301022022220A0401032022220A05"
$"0103123FBBD40000000000003FA51943DCFC44C1340117840004"
};

View File

@ -0,0 +1,273 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Alert.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <Window.h>
#include <CheckBox.h>
#include <TextControl.h>
#include <Path.h>
#include <Notification.h>
#include <notification/Notifications.h>
#include <notification/NotificationReceived.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include "NotificationsView.h"
#define _T(str) (str)
const float kEdgePadding = 5.0;
const float kCLVTitlePadding = 8.0;
const int32 kApplicationSelected = '_ASL';
const int32 kNotificationSelected = '_NSL';
const int32 kCLVDeleteRow = 'av02';
// Applications column indexes
const int32 kAppIndex = 0;
const int32 kAppEnabledIndex = 1;
// Notifications column indexes
const int32 kTitleIndex = 0;
const int32 kDateIndex = 1;
const int32 kTypeIndex = 2;
const int32 kAllowIndex = 3;
const int32 kSettingChanged = '_STC';
NotificationsView::NotificationsView()
:
BView("apps", B_WILL_DRAW)
{
BRect rect(0, 0, 100, 100);
// Search application field
fSearch = new BTextControl(_T("Search:"), NULL,
new BMessage(kSettingChanged));
// Applications list
fApplications = new BColumnListView(rect, _T("Applications"),
0, B_WILL_DRAW, B_FANCY_BORDER, true);
fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fAppCol = new BStringColumn(_T("Application"), 200,
be_plain_font->StringWidth(_T("Application")) + (kCLVTitlePadding * 2),
rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fApplications->AddColumn(fAppCol, kAppIndex);
fAppEnabledCol = new BStringColumn(_T("Enabled"), 10,
be_plain_font->StringWidth(_T("Enabled")) + (kCLVTitlePadding * 2),
rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
// Notifications list
fNotifications = new BColumnListView(rect, _T("Notifications"),
0, B_WILL_DRAW, B_FANCY_BORDER, true);
fNotifications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fTitleCol = new BStringColumn(_T("Title"), 100,
be_plain_font->StringWidth(_T("Title")) + (kCLVTitlePadding * 2),
rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fTitleCol, kTitleIndex);
fDateCol = new BDateColumn(_T("Last Received"), 100,
be_plain_font->StringWidth(_T("Last Received")) + (kCLVTitlePadding * 2),
rect.Width(), B_ALIGN_LEFT);
fNotifications->AddColumn(fDateCol, kDateIndex);
fTypeCol = new BStringColumn(_T("Type"), 100,
be_plain_font->StringWidth(_T("Type")) + (kCLVTitlePadding * 2),
rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fTypeCol, kTypeIndex);
fAllowCol = new BStringColumn(_T("Allowed"), 100,
be_plain_font->StringWidth(_T("Allowed")) + (kCLVTitlePadding * 2),
rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fAllowCol, kAllowIndex);
// Load the applications list
_LoadAppUsage();
_PopulateApplications();
// Calculate inset
float inset = ceilf(be_plain_font->Size() * 0.7f);
// Set layout
SetLayout(new BGroupLayout(B_VERTICAL));
// Add views
AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
.AddGroup(B_HORIZONTAL)
.AddGlue()
.Add(fSearch)
.End()
.Add(fApplications)
.Add(fNotifications)
);
}
void
NotificationsView::AttachedToWindow()
{
fApplications->SetTarget(this);
fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
fNotifications->SetTarget(this);
fNotifications->SetInvocationMessage(new BMessage(kNotificationSelected));
#if 0
fNotifications->AddFilter(new BMessageFilter(B_ANY_DELIVERY,
B_ANY_SOURCE, B_KEY_DOWN, CatchDelete));
#endif
}
void
NotificationsView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kApplicationSelected: {
BRow *row = fApplications->CurrentSelection();
if (row == NULL)
return;
BStringField* appname =
dynamic_cast<BStringField*>(row->GetField(kAppIndex));
appusage_t::iterator it = fAppFilters.find(appname->String());
if (it != fAppFilters.end())
_Populate(it->second);
} break;
case kNotificationSelected:
break;
default:
BView::MessageReceived(msg);
break;
}
}
status_t
NotificationsView::_LoadAppUsage()
{
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();
return B_ERROR;
}
path.Append(kFiltersSettings);
BFile file(path.Path(), B_READ_ONLY);
BMessage settings;
if (settings.Unflatten(&file) != B_OK)
return B_ERROR;
type_code type;
int32 count = 0;
if (settings.GetInfo("app_usage", &type, &count) != B_OK)
return B_ERROR;
// Clean filters
appusage_t::iterator auIt;
for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
delete auIt->second;
fAppFilters.clear();
// Add new filters
for (int32 i = 0; i < count; i++) {
AppUsage* app = new AppUsage();
settings.FindFlat("app_usage", i, app);
fAppFilters[app->Name()] = app;
}
return B_OK;
}
void
NotificationsView::_PopulateApplications()
{
appusage_t::iterator it;
fApplications->Clear();
for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
BRow* row = new BRow();
row->SetField(new BStringField(it->first.String()), kAppIndex);
fApplications->AddRow(row);
}
}
void
NotificationsView::_Populate(AppUsage* usage)
{
// Sanity check
if (!usage)
return;
int32 size = usage->Notifications();
if (usage->Allowed() == false)
fBlockAll->SetValue(B_CONTROL_ON);
fNotifications->Clear();
for (int32 i = 0; i < size; i++) {
NotificationReceived* notification = usage->NotificationAt(i);
time_t updated = notification->LastReceived();
const char* allow = notification->Allowed() ? _T("Yes") : _T("No");
const char* type = "";
switch (notification->Type()) {
case B_INFORMATION_NOTIFICATION:
type = _T("Information");
break;
case B_IMPORTANT_NOTIFICATION:
type = _T("Important");
break;
case B_ERROR_NOTIFICATION:
type = _T("Error");
break;
case B_PROGRESS_NOTIFICATION:
type = _T("Progress");
break;
default:
type = _T("Unknown");
}
BRow* row = new BRow();
row->SetField(new BStringField(notification->Title()), kTitleIndex);
row->SetField(new BDateField(&updated), kDateIndex);
row->SetField(new BStringField(type), kTypeIndex);
row->SetField(new BStringField(allow), kAllowIndex);
fNotifications->AddRow(row);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _APPS_VIEW_H
#define _APPS_VIEW_H
#include <View.h>
#include <notification/AppUsage.h>
typedef std::map<BString, AppUsage *> appusage_t;
class BCheckBox;
class BTextControl;
class BColumnListView;
class BStringColumn;
class BDateColumn;
class NotificationsView : public BView {
public:
NotificationsView();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
private:
status_t _LoadAppUsage();
void _PopulateApplications();
void _Populate(AppUsage* usage);
appusage_t fAppFilters;
BCheckBox* fBlockAll;
BTextControl* fSearch;
BColumnListView* fApplications;
BStringColumn* fAppCol;
BStringColumn* fAppEnabledCol;
BColumnListView* fNotifications;
BStringColumn* fTitleCol;
BDateColumn* fDateCol;
BStringColumn* fTypeCol;
BStringColumn* fAllowCol;
};
#endif // _APPS_VIEW_H

View File

@ -0,0 +1,137 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Message.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <CardLayout.h>
#include <LayoutItem.h>
#include "SettingsHost.h"
#include "PrefletView.h"
#include "IconRule.h"
#include "GeneralView.h"
#include "DisplayView.h"
#include "NotificationsView.h"
#define _T(str) (str)
const int32 kPageSelected = '_LCH';
PrefletView::PrefletView(SettingsHost* host)
:
BView("pages", B_WILL_DRAW)
{
// Page selector
fRule = new BIconRule("icon_rule");
fRule->SetSelectionMessage(new BMessage(kPageSelected));
(void)fRule->AddIcon(_T("General"), NULL);
(void)fRule->AddIcon(_T("Display"), NULL);
//(void)fRule->AddIcon(_T("Notifications"), NULL);
// View for card layout
fPagesView = new BView("pages", B_WILL_DRAW);
// Pages
GeneralView* general = new GeneralView(host);
DisplayView* display = new DisplayView(host);
NotificationsView* apps = new NotificationsView();
// Calculate inset
float inset = ceilf(be_plain_font->Size() * 0.7f);
// Build the layout
SetLayout(new BGroupLayout(B_VERTICAL));
// Card layout for pages
BCardLayout* layout = new BCardLayout();
fPagesView->SetLayout(layout);
layout->AddView(general);
layout->AddView(display);
layout->AddView(apps);
// Add childs
AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
.Add(fRule)
.Add(fPagesView)
);
// Select the first view
Select(0);
}
void
PrefletView::AttachedToWindow()
{
fRule->SetTarget(this);
}
void
PrefletView::MessageReceived(BMessage* message)
{
switch (message->what) {
case kPageSelected:
{
int32 index = B_ERROR;
if (message->FindInt32("index", &index) != B_OK)
return;
Select(index);
break;
}
default:
BView::MessageReceived(message);
}
}
void
PrefletView::Select(int32 index)
{
BCardLayout* layout
= dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
if (layout)
layout->SetVisibleItem(index);
}
BView*
PrefletView::CurrentPage()
{
BCardLayout* layout
= dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
if (layout)
return layout->VisibleItem()->View();
return NULL;
}
int32
PrefletView::CountPages() const
{
BCardLayout* layout
= dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
if (layout)
return layout->CountItems();
return 0;
}
BView*
PrefletView::PageAt(int32 index)
{
BCardLayout* layout
= dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
if (layout)
return layout->ItemAt(index)->View();
return NULL;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _PREFLET_VIEW_H
#define _PREFLET_VIEW_H
#include <View.h>
class BIconRule;
class SettingsHost;
class PrefletView : public BView {
public:
PrefletView(SettingsHost* host);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message);
void Select(int32 index);
BView* CurrentPage();
int32 CountPages() const;
BView* PageAt(int32 index);
private:
BIconRule* fRule;
BView* fPagesView;
};
#endif // PREFLETVIEW_H

View File

@ -0,0 +1,108 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Application.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <Button.h>
#include "PrefletWin.h"
#include "PrefletView.h"
#define _T(str) (str)
const int32 kRevert = '_RVT';
const int32 kSave = '_SAV';
PrefletWin::PrefletWin()
:
BWindow(BRect(0, 0, 1, 1), "Notifications", B_TITLED_WINDOW, B_NOT_ZOOMABLE
| B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
{
// Preflet container view
fMainView = new PrefletView(this);
// Save and revert buttons
fRevert = new BButton("revert", _T("Revert"), new BMessage(kRevert));
fRevert->SetEnabled(false);
fSave = new BButton("save", _T("Save"), new BMessage(kSave));
fSave->SetEnabled(false);
// Calculate inset
float inset = ceilf(be_plain_font->Size() * 0.7f);
// Build the layout
SetLayout(new BGroupLayout(B_VERTICAL));
// Add childs
AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
.Add(fMainView)
.AddGroup(B_HORIZONTAL, inset)
.AddGlue()
.Add(fRevert)
.Add(fSave)
.End()
.SetInsets(inset, inset, inset, inset)
);
// Center this window on screen and show it
CenterOnScreen();
Show();
}
void
PrefletWin::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kSave:
for (int32 i = 0; i < fMainView->CountPages(); i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane) {
if (pane->Save() == B_OK) {
fSave->SetEnabled(false);
fRevert->SetEnabled(true);
} else
break;
}
}
break;
case kRevert:
for (int32 i = 0; i < fMainView->CountPages(); i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane) {
if (pane->Revert() == B_OK)
fRevert->SetEnabled(false);
}
}
break;
default:
BWindow::MessageReceived(msg);
}
}
bool
PrefletWin::QuitRequested()
{
be_app_messenger.SendMessage(B_QUIT_REQUESTED);
return true;
}
void
PrefletWin::SettingChanged()
{
fSave->SetEnabled(true);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _PREFLET_WIN_H
#define _PREFLET_WIN_H
#include <Window.h>
#include "SettingsHost.h"
class BButton;
class PrefletView;
class PrefletWin : public BWindow, public SettingsHost {
public:
PrefletWin();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* msg);
virtual void SettingChanged();
private:
PrefletView* fMainView;
BButton* fSave;
BButton* fRevert;
};
#endif // _PREFLET_WIN_H

View File

@ -0,0 +1,20 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _SETTINGS_HOST_H
#define _SETTINGS_HOST_H
#include <vector>
#include "SettingsPane.h"
class SettingsHost {
public:
SettingsHost() {}
virtual void SettingChanged() = 0;
};
#endif // _SETTINGS_HOST_H

View File

@ -0,0 +1,38 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Message.h>
#include <Node.h>
#include <Path.h>
#include <FindDirectory.h>
#include <Directory.h>
#include "SettingsPane.h"
#include "SettingsHost.h"
SettingsPane::SettingsPane(const char* name, SettingsHost* host)
:
BView(name, B_WILL_DRAW),
fHost(host)
{
}
void
SettingsPane::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kSettingChanged:
fHost->SettingChanged();
break;
default:
BView::MessageReceived(msg);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _SETTINGS_PANE_H
#define _SETTINGS_PANE_H
#include <View.h>
class BNode;
class SettingsHost;
const int32 kSettingChanged = '_STC';
class SettingsPane : public BView {
public:
SettingsPane(const char* name, SettingsHost* host);
virtual void MessageReceived(BMessage* msg);
virtual status_t Load() = 0;
virtual status_t Save() = 0;
virtual status_t Revert() = 0;
protected:
SettingsHost* fHost;
};
#endif // _SETTINGS_PANE_H

View File

@ -11,6 +11,7 @@ SubInclude HAIKU_TOP src servers media_addon ;
SubInclude HAIKU_TOP src servers midi ;
SubInclude HAIKU_TOP src servers mount ;
SubInclude HAIKU_TOP src servers net ;
SubInclude HAIKU_TOP src servers notification ;
SubInclude HAIKU_TOP src servers power ;
SubInclude HAIKU_TOP src servers print ;
SubInclude HAIKU_TOP src servers registrar ;

View File

@ -0,0 +1,366 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <algorithm>
#include "AppGroupView.h"
#include "NotificationWindow.h"
#include "NotificationView.h"
AppGroupView::AppGroupView(NotificationWindow* win, const char* label)
:
BView(BRect(0, 0, win->ViewWidth(), 1), label, B_FOLLOW_LEFT_RIGHT,
B_WILL_DRAW|B_FULL_UPDATE_ON_RESIZE|B_FRAME_EVENTS),
fLabel(label),
fParent(win),
fCollapsed(false)
{
Show();
}
AppGroupView::~AppGroupView()
{
}
void
AppGroupView::AttachedToWindow()
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
}
void
AppGroupView::Draw(BRect updateRect)
{
FillRect(Bounds(), B_SOLID_LOW);
BString label = fLabel;
if (fCollapsed)
label << " (" << fInfo.size() << ")";
font_height fh;
be_bold_font->GetHeight(&fh);
float labelOffset = fh.ascent + fh.leading;
BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding);
borderRect.top = labelOffset;
BRect textRect = borderRect;
textRect.left = kEdgePadding * 2;
textRect.right = textRect.left + be_bold_font->StringWidth(label.String())
+ (kEdgePadding * 3);
textRect.bottom = labelOffset;
BRect closeCross = fCloseRect;
closeCross.InsetBy(kSmallPadding, kSmallPadding);
rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
// detailCol = tint_color(detailCol, B_LIGHTEN_1_TINT);
if (fCollapsed) {
PushState();
SetFont(be_bold_font);
SetPenSize(kPenSize);
float linePos = textRect.top + textRect.Height() / 2;
// Draw the line to the expand widget
PushState();
SetHighColor(detailCol);
StrokeLine(BPoint(kEdgePadding, linePos), BPoint(fCollapseRect.left, linePos));
PopState();
// Draw the expand widget
PushState();
SetHighColor(detailCol);
StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding);
BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
StrokeLine(expandHorStart, expandHorEnd);
BPoint expandVerStart(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.top + kSmallPadding);
BPoint expandVerEnd(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.bottom - kSmallPadding);
StrokeLine(expandVerStart, expandVerEnd);
PopState();
// Draw the app title
DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding));
// Draw the line from the label to the close widget
PushState();
SetHighColor(detailCol);
BPoint lineSeg2Start(textRect.right + kSmallPadding / 2, linePos);
BPoint lineSeg2End(fCloseRect.left, linePos);
StrokeLine(lineSeg2Start, lineSeg2End);
PopState();
// Draw the dismiss widget
PushState();
SetHighColor(detailCol);
StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding);
StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
StrokeLine(closeCross.RightTop(), closeCross.LeftBottom());
PopState();
// Draw the line from the dismiss widget
PushState();
SetHighColor(detailCol);
BPoint lineSeg3Start(fCloseRect.right, linePos);
BPoint lineSeg3End(borderRect.right, linePos);
StrokeLine(lineSeg3Start, lineSeg3End);
PopState();
PopState();
} else {
PushState();
SetFont(be_bold_font);
SetPenSize(kPenSize);
// Draw the border
PushState();
SetHighColor(detailCol);
// StrokeRoundRect(borderRect, kEdgePadding, kEdgePadding * 2);
StrokeRect(borderRect);
PopState();
FillRect(textRect, B_SOLID_LOW);
// Draw the collapse widget
PushState();
SetHighColor(detailCol);
StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding);
BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
StrokeLine(expandHorStart, expandHorEnd);
PopState();
// Draw the dismiss widget
PushState();
SetHighColor(detailCol);
FillRect(fCloseRect, B_SOLID_LOW);
StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding);
StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
StrokeLine(closeCross.RightTop(), closeCross.LeftBottom());
PopState();
// Draw the label
DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding));
PopState();
}
Sync();
}
void
AppGroupView::MouseDown(BPoint point)
{
bool changed = false;
if (fCloseRect.Contains(point)) {
changed = true;
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
fInfo[i]->RemoveSelf();
delete fInfo[i];
}
fInfo.clear();
}
if (fCollapseRect.Contains(point)) {
fCollapsed = !fCollapsed;
changed = true;
}
if (changed) {
ResizeViews();
Invalidate();
}
}
void
AppGroupView::GetPreferredSize(float* width, float* height)
{
font_height fh;
be_bold_font->GetHeight(&fh);
float h = fh.ascent + fh.leading + fh.leading;
h += kEdgePadding * 2; // Padding between top and bottom of label
if (!fCollapsed) {
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
float childHeight = 0;
float childWidth = 0;
fInfo[i]->GetPreferredSize(&childWidth, &childHeight);
h += childHeight;
}
}
h += kEdgePadding;
*width = fParent->ViewWidth();
*height = h;
}
void
AppGroupView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kRemoveView:
{
NotificationView* view = NULL;
if (msg->FindPointer("view", (void**)&view) != B_OK)
return;
infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view);
if (vIt != fInfo.end()) {
fInfo.erase(vIt);
view->RemoveSelf();
delete view;
}
ResizeViews();
Invalidate();
// When all the views are destroy, save app filters
if (fInfo.size() == 0)
dynamic_cast<NotificationWindow*>(Window())->SaveAppFilters();
break;
}
default:
BView::MessageReceived(msg);
}
}
void
AppGroupView::AddInfo(NotificationView* view)
{
BString id = view->MessageID();
if (id.Length() > 0) {
int32 children = fInfo.size();
bool found = false;
for (int32 i = 0; i < children; i++) {
if (fInfo[i]->HasMessageID(id.String())) {
fInfo[i]->RemoveSelf();
delete fInfo[i];
fInfo[i] = view;
found = true;
break;
}
}
if (!found)
fInfo.push_back(view);
} else
fInfo.push_back(view);
if (fParent->IsHidden())
fParent->Show();
if (IsHidden())
Show();
if (view->IsHidden())
view->Show();
AddChild(view);
ResizeViews();
Invalidate();
}
void
AppGroupView::ResizeViews()
{
font_height fh;
be_bold_font->GetHeight(&fh);
float offset = fh.ascent + fh.leading + fh.descent;
int32 children = fInfo.size();
if (!fCollapsed) {
offset += kEdgePadding + kPenSize;
for (int32 i = 0; i < children; i++) {
fInfo[i]->ResizeToPreferred();
fInfo[i]->MoveTo(kEdgePadding + kPenSize, offset);
offset += fInfo[i]->Bounds().Height();
if (fInfo[i]->IsHidden())
fInfo[i]->Show();
fInfo[i]->SetPosition(false, false);
};
} else {
for (int32 i = 0; i < children; i++)
if (!fInfo[i]->IsHidden())
fInfo[i]->Hide();
}
if (children == 1)
fInfo[0]->SetPosition(true, true);
else if (children > 1) {
fInfo[0]->SetPosition(true, false);
fInfo[children - 1]->SetPosition(false, true);
}
ResizeTo(fParent->ViewWidth(), offset);
float labelOffset = fh.ascent + fh.leading;
BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding);
borderRect.top = labelOffset;
fCollapseRect = borderRect;
fCollapseRect.right = fCollapseRect.left + kExpandSize;
fCollapseRect.bottom = fCollapseRect.top + kExpandSize;
fCollapseRect.OffsetTo(kEdgePadding * 2, kEdgePadding * 1.5);
fCloseRect = borderRect;
fCloseRect.right -= kEdgePadding * 4;
fCloseRect.left = fCloseRect.right - kCloseSize;
fCloseRect.bottom = fCloseRect.top + kCloseSize;
fCloseRect.OffsetTo(fCloseRect.left, kEdgePadding * 1.5);
fParent->ResizeAll();
}
bool
AppGroupView::HasChildren()
{
return !fInfo.empty();
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2005-2008, Mikael Eiman.
* Copyright 2005-2008, Michael Davidson.
* Distributed under the terms of the MIT License.
*
* Authors:
* Mikael Eiman <mikael@eiman.tv>
* Michael Davidson <slaad@bong.com.au>
*/
#ifndef APPGROUPVIEW_H
#define APPGROUPVIEW_H
#include <vector>
#include <String.h>
#include <View.h>
class NotificationWindow;
class NotificationView;
typedef std::vector<NotificationView *> infoview_t;
class AppGroupView : public BView {
public:
AppGroupView(NotificationWindow *win, const char *label);
~AppGroupView(void);
// Hooks
void AttachedToWindow(void);
void Draw(BRect bounds);
void MouseDown(BPoint point);
void GetPreferredSize(float *width, float *height);
void MessageReceived(BMessage *msg);
// Public
void AddInfo(NotificationView *view);
void ResizeViews(void);
bool HasChildren(void);
private:
BString fLabel;
NotificationWindow *fParent;
infoview_t fInfo;
bool fCollapsed;
BRect fCloseRect;
BRect fCollapseRect;
};
#endif

View File

@ -0,0 +1,114 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include "BorderView.h"
const float kBorderWidth = 2.0f;
const int32 kTitleSize = 14;
BorderView::BorderView(BRect rect, const char* text)
:
BView(rect, "NotificationBorderView", B_FOLLOW_ALL,
B_WILL_DRAW | B_FRAME_EVENTS),
fTitle(text)
{
SetViewColor(B_TRANSPARENT_COLOR);
}
BorderView::~BorderView()
{
}
void
BorderView::FrameResized(float, float)
{
Invalidate();
}
void
BorderView::Draw(BRect rect)
{
rgb_color col_bg = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color col_text = tint_color(col_bg, B_LIGHTEN_MAX_TINT);
rgb_color col_text_shadow = tint_color(col_bg, B_DARKEN_MAX_TINT);
// Background
SetDrawingMode(B_OP_COPY);
SetHighColor(col_bg);
FillRect(rect);
// Text
SetLowColor(col_bg);
SetDrawingMode(B_OP_ALPHA);
SetFont(be_bold_font);
SetFontSize(kTitleSize);
BFont font;
GetFont(&font);
font_height fh;
font.GetHeight(&fh);
// float line_height = fh.ascent + fh.descent + fh.leading;
float text_pos = fh.ascent;
SetHighColor(col_text);
DrawString(fTitle.String(), BPoint(kBorderWidth, text_pos));
SetHighColor(col_text_shadow);
DrawString(fTitle.String(), BPoint(kBorderWidth + 1, text_pos + 1));
// Content border
SetHighColor(tint_color(col_bg, B_DARKEN_2_TINT));
BRect content = Bounds();
content.InsetBy(kBorderWidth, kBorderWidth);
content.top += text_pos + fh.descent + 2;
BRect content_line(content);
content_line.InsetBy(-1, -1);
StrokeRect(content_line);
}
void
BorderView::GetPreferredSize(float* w, float* h)
{
SetFont(be_bold_font);
SetFontSize(kTitleSize);
BFont font;
GetFont(&font);
font_height fh;
font.GetHeight(&fh);
float line_height = fh.ascent + fh.descent;
*w = 2 * kBorderWidth + StringWidth(fTitle.String());
*h = 2 * kBorderWidth + line_height + 3;
*w = *h = 0;
}
float
BorderView::BorderSize()
{
return 0;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _BORDER_VIEW_H
#define _BORDER_VIEW_H
#include <View.h>
#include <String.h>
class BorderView : public BView {
public:
BorderView(BRect, const char*);
virtual ~BorderView();
virtual void Draw(BRect updateRect);
virtual void GetPreferredSize(float*, float*);
virtual void FrameResized(float, float);
float BorderSize();
private:
BString fTitle;
};
#endif // _BORDER_VIEW_H

View File

@ -0,0 +1,17 @@
SubDir HAIKU_TOP src servers notification ;
UsePrivateHeaders notification ;
UseLibraryHeaders icon ;
Server notification_server :
AppGroupView.cpp
BorderView.cpp
NotificationServer.cpp
NotificationView.cpp
NotificationWindow.cpp
: be $(TARGET_LIBSTDC++) libicon.a libnotification.a
: notification_server.rdef
;
Depends notification_server : libicon.a ;
Depends notification_server : libnotification.a ;

View File

@ -0,0 +1,117 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <Beep.h>
#include <Notifications.h>
#include "NotificationWindow.h"
#include "NotificationServer.h"
const char* kSoundNames[] = {
"Information notification",
"Important notification",
"Error notification",
"Progress notification",
NULL
};
NotificationServer::NotificationServer()
: BApplication(kNotificationServerSignature)
{
fWindow = new NotificationWindow();
}
NotificationServer::~NotificationServer()
{
}
void
NotificationServer::MessageReceived(BMessage* message)
{
switch (message->what) {
case kNotificationMessage:
{
// Skip this message if we don't have the window
if (!fWindow)
return;
int32 type = 0;
// Emit a sound for this event
if (message->FindInt32("type", &type) == B_OK) {
if (type < (int32)(sizeof(kSoundNames) / sizeof(const char*)))
system_beep(kSoundNames[type]);
}
// Let the notification window handle this message
BMessenger(fWindow).SendMessage(message);
break;
}
default:
BApplication::MessageReceived(message);
}
}
bool
NotificationServer::QuitRequested()
{
if (fWindow && fWindow->Lock()) {
fWindow->Quit();
fWindow = NULL;
}
return true;
}
status_t
NotificationServer::GetSupportedSuites(BMessage* msg)
{
msg->AddString("suites", "suite/x-vnd.Haiku-notification_server");
BPropertyInfo info(main_prop_list);
msg->AddFlat("messages", &info);
return BApplication::GetSupportedSuites(msg);
}
BHandler*
NotificationServer::ResolveSpecifier(BMessage* msg, int32 index,
BMessage* spec, int32 from, const char* prop)
{
BPropertyInfo info(main_prop_list);
if (strcmp(prop, "message") == 0) {
BMessenger messenger(fWindow);
messenger.SendMessage(msg, fWindow);
return NULL;
}
return BApplication::ResolveSpecifier(msg, index, spec, from, prop);
}
int
main(int argc, char* argv[])
{
int32 i = 0;
// Add system sounds
while (kSoundNames[i] != NULL)
add_system_beep_event(kSoundNames[i++], 0);
// Start!
NotificationServer* server = new NotificationServer();
server->Run();
return 0;
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_SERVER_H
#define _NOTIFICATION_SERVER_H
#include <Application.h>
class NotificationWindow;
class NotificationServer : public BApplication {
public:
NotificationServer();
virtual ~NotificationServer();
virtual void MessageReceived(BMessage* message);
virtual bool QuitRequested();
virtual status_t GetSupportedSuites(BMessage* msg);
virtual BHandler* ResolveSpecifier(BMessage* msg, int32 index, BMessage* spec,
int32 form, const char* prop);
private:
NotificationWindow* fWindow;
};
#endif // _NOTIFICATION_SERVER_H

View File

@ -0,0 +1,720 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdlib.h>
#include <Font.h>
#include <IconUtils.h>
#include <Messenger.h>
#include <Picture.h>
#include <PropertyInfo.h>
#include <Region.h>
#include <Resources.h>
#include <Roster.h>
#include <StringView.h>
#include <TranslationUtils.h>
#include "NotificationView.h"
#include "NotificationWindow.h"
const char* kSmallIconAttribute = "BEOS:M:STD_ICON";
const char* kLargeIconAttribute = "BEOS:L:STD_ICON";
const char* kIconAttribute = "BEOS:ICON";
property_info message_prop_list[] = {
{ "type", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get the notification type"},
{ "app", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get notification's app"},
{ "title", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get notification's title"},
{ "content", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get notification's contents"},
{ "icon", {B_GET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get icon as an archived bitmap"},
{ "progress", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
{B_DIRECT_SPECIFIER, 0}, "get the progress (between 0.0 and 1.0)"},
{ NULL }
};
NotificationView::NotificationView(NotificationWindow* win,
notification_type type, const char* app, const char* title, const char* text,
BMessage* details)
:
BView(BRect(0, 0, win->ViewWidth(), 1), "NotificationView",
B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS),
fParent(win),
fType(type),
fRunner(NULL),
fProgress(0.0f),
fMessageID(""),
fDetails(details),
fBitmap(NULL),
fIsFirst(false),
fIsLast(false)
{
BMessage iconMsg;
if (fDetails->FindMessage("icon", &iconMsg) == B_OK)
fBitmap = new BBitmap(&iconMsg);
if (!fBitmap)
_LoadIcon();
const char* messageID = NULL;
if (fDetails->FindString("messageID", &messageID) == B_OK)
fMessageID = messageID;
if (fDetails->FindFloat("progress", &fProgress) != B_OK)
fProgress = 0.0f;
// Progress is between 0 and 1
if (fProgress < 0.0f)
fProgress = 0.0f;
if (fProgress > 1.0f)
fProgress = 1.0f;
SetText(app, title, text);
ResizeToPreferred();
switch (type) {
case B_IMPORTANT_NOTIFICATION:
SetViewColor(255, 255, 255);
SetLowColor(255, 255, 255);
break;
case B_ERROR_NOTIFICATION:
SetViewColor(ui_color(B_FAILURE_COLOR));
SetLowColor(ui_color(B_FAILURE_COLOR));
break;
default:
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
break;
}
}
NotificationView::~NotificationView()
{
delete fRunner;
delete fDetails;
delete fBitmap;
LineInfoList::iterator lIt;
for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
delete (*lIt);
}
void
NotificationView::AttachedToWindow()
{
BMessage msg(kRemoveView);
msg.AddPointer("view", this);
int32 timeout = -1;
if (fDetails->FindInt32("timeout", &timeout) != B_OK)
timeout = fParent->Timeout();
bigtime_t delay = timeout * 1000 * 1000;
if (delay > 0)
fRunner = new BMessageRunner(BMessenger(Parent()), &msg, delay, 1);
}
void
NotificationView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case B_GET_PROPERTY:
{
BMessage specifier;
const char* property;
BMessage reply(B_REPLY);
bool msgOkay = true;
if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
msgOkay = false;
if (specifier.FindString("property", &property) != B_OK)
msgOkay = false;
if (msgOkay) {
if (strcmp(property, "type") == 0)
reply.AddInt32("result", fType);
if (strcmp(property, "app") == 0)
reply.AddString("result", fApp);
if (strcmp(property, "title") == 0)
reply.AddString("result", fTitle);
if (strcmp(property, "content") == 0)
reply.AddString("result", fText);
if (strcmp(property, "progress") == 0)
reply.AddFloat("result", fProgress);
if ((strcmp(property, "icon") == 0) && fBitmap) {
BMessage archive;
if (fBitmap->Archive(&archive) == B_OK)
reply.AddMessage("result", &archive);
}
reply.AddInt32("error", B_OK);
} else {
reply.what = B_MESSAGE_NOT_UNDERSTOOD;
reply.AddInt32("error", B_ERROR);
}
msg->SendReply(&reply);
break;
}
case B_SET_PROPERTY:
{
BMessage specifier;
const char* property;
BMessage reply(B_REPLY);
bool msgOkay = true;
if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
msgOkay = false;
if (specifier.FindString("property", &property) != B_OK)
msgOkay = false;
if (msgOkay) {
if (strcmp(property, "app") == 0)
msg->FindString("data", &fApp);
if (strcmp(property, "title") == 0)
msg->FindString("data", &fTitle);
if (strcmp(property, "content") == 0)
msg->FindString("data", &fText);
if (strcmp(property, "icon") == 0) {
BMessage archive;
if (msg->FindMessage("data", &archive) == B_OK) {
delete fBitmap;
fBitmap = new BBitmap(&archive);
}
}
SetText(Application(), Title(), Text());
Invalidate();
reply.AddInt32("error", B_OK);
} else {
reply.what = B_MESSAGE_NOT_UNDERSTOOD;
reply.AddInt32("error", B_ERROR);
}
msg->SendReply(&reply);
break;
}
case kRemoveView:
{
BMessage remove(kRemoveView);
remove.AddPointer("view", this);
BMessenger msgr(Window());
msgr.SendMessage( &remove );
break;
}
default:
BView::MessageReceived(msg);
}
}
void
NotificationView::GetPreferredSize(float* w, float* h)
{
// Parent width, minus the edge padding, minus the pensize
*w = fParent->ViewWidth() - (kEdgePadding * 2) - (kPenSize * 2);
*h = fHeight;
if (fType == B_PROGRESS_NOTIFICATION) {
font_height fh;
be_plain_font->GetHeight(&fh);
float fontHeight = fh.ascent + fh.descent + fh.leading;
*h += (kSmallPadding * 2) + (kEdgePadding * 1) + fontHeight;
}
}
void
NotificationView::Draw(BRect updateRect)
{
BRect progRect;
// Draw progress background
if (fType == B_PROGRESS_NOTIFICATION) {
PushState();
font_height fh;
be_plain_font->GetHeight(&fh);
float fontHeight = fh.ascent + fh.descent + fh.leading;
progRect = Bounds();
progRect.InsetBy(kEdgePadding, kEdgePadding);
progRect.top = progRect.bottom - (kSmallPadding * 2) - fontHeight;
StrokeRect(progRect);
BRect barRect = progRect;
barRect.InsetBy(1.0, 1.0);
barRect.right *= fProgress;
SetHighColor(ui_color(B_CONTROL_HIGHLIGHT_COLOR));
FillRect(barRect);
SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
BString label = "";
label << (int)(fProgress * 100) << " %";
float labelWidth = be_plain_font->StringWidth(label.String());
float labelX = progRect.left + (progRect.IntegerWidth() / 2) - (labelWidth / 2);
SetLowColor(B_TRANSPARENT_COLOR);
SetDrawingMode(B_OP_ALPHA);
DrawString(label.String(), label.Length(),
BPoint(labelX, progRect.top + fh.ascent + fh.leading + kSmallPadding));
PopState();
}
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
// Icon size
float iconSize = (float)fParent->IconSize();
// Rectangle for icon and overlay icon
BRect iconRect(0, 0, 0, 0);
// Draw icon
if (fBitmap) {
LineInfo* appLine = fLines.back();
font_height fh;
appLine->font.GetHeight(&fh);
float title_bottom = appLine->location.y + fh.descent;
float ix = kEdgePadding;
float iy = 0;
if (fParent->Layout() == TitleAboveIcon)
iy = title_bottom + kEdgePadding + (Bounds().Height() - title_bottom
- kEdgePadding * 2 - iconSize) / 2;
else
iy = (Bounds().Height() - iconSize) / 2.0;
if (fType == B_PROGRESS_NOTIFICATION)
// Move icon up by half progress bar height if it's present
iy -= (progRect.Height() + kEdgePadding) / 2.0;
iconRect.left = ix;
iconRect.top = iy;
iconRect.right = ix + iconSize;
iconRect.bottom = iy + iconSize;
DrawBitmapAsync(fBitmap, fBitmap->Bounds(),
iconRect, B_FILTER_BITMAP_BILINEAR);
}
// Draw content
LineInfoList::iterator lIt;
for (lIt = fLines.begin(); lIt != fLines.end(); lIt++) {
LineInfo *l = (*lIt);
SetFont(&l->font);
DrawString(l->text.String(), l->text.Length(), l->location);
}
rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
// Draw the close widget
BRect closeRect = Bounds();
closeRect.InsetBy(kEdgePadding, kEdgePadding);
closeRect.left = closeRect.right - kCloseSize;
closeRect.bottom = closeRect.top + kCloseSize;
PushState();
SetHighColor(detailCol);
StrokeRoundRect(closeRect, kSmallPadding, kSmallPadding);
BRect closeCross = closeRect.InsetByCopy(kSmallPadding, kSmallPadding);
StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
StrokeLine(closeCross.LeftBottom(), closeCross.RightTop());
PopState();
Sync();
}
void
NotificationView::MouseDown(BPoint point)
{
int32 buttons;
Window()->CurrentMessage()->FindInt32("buttons", &buttons);
switch (buttons) {
case B_PRIMARY_MOUSE_BUTTON:
{
BRect closeRect = Bounds().InsetByCopy(2,2);
closeRect.left = closeRect.right - kCloseSize;
closeRect.bottom = closeRect.top + kCloseSize;
if (!closeRect.Contains(point)) {
entry_ref launchRef;
BString launchString;
BMessage argMsg(B_ARGV_RECEIVED);
BMessage refMsg(B_REFS_RECEIVED);
entry_ref appRef;
bool useArgv = false;
BList messages;
entry_ref ref;
if (fDetails->FindString("onClickApp", &launchString) == B_OK)
if (be_roster->FindApp(launchString.String(), &appRef) == B_OK)
useArgv = true;
if (fDetails->FindRef("onClickFile", &launchRef) == B_OK) {
if (be_roster->FindApp(&launchRef, &appRef) == B_OK)
useArgv = true;
}
if (fDetails->FindRef("onClickRef", &ref) == B_OK) {
for (int32 i = 0; fDetails->FindRef("onClickRef", i, &ref) == B_OK; i++)
refMsg.AddRef("refs", &ref);
messages.AddItem((void*)&refMsg);
}
if (useArgv) {
type_code type;
int32 argc = 0;
BString arg;
BPath p(&appRef);
argMsg.AddString("argv", p.Path());
fDetails->GetInfo("onClickArgv", &type, &argc);
argMsg.AddInt32("argc", argc + 1);
for (int32 i = 0; fDetails->FindString("onClickArgv", i, &arg) == B_OK; i++)
argMsg.AddString("argv", arg);
messages.AddItem((void*)&argMsg);
}
BMessage tmp;
for (int32 i = 0; fDetails->FindMessage("onClickMsg", i, &tmp) == B_OK; i++)
messages.AddItem((void*)&tmp);
if (fDetails->FindString("onClickApp", &launchString) == B_OK)
be_roster->Launch(launchString.String(), &messages);
else
be_roster->Launch(&launchRef, &messages);
}
// Remove the info view after a click
BMessage remove_msg(kRemoveView);
remove_msg.AddPointer("view", this);
BMessenger msgr(Parent());
msgr.SendMessage(&remove_msg);
break;
}
}
}
void
NotificationView::FrameResized( float w, float /*h*/)
{
SetText(Application(), Title(), Text());
}
BHandler*
NotificationView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* spec, int32 form, const char* prop)
{
BPropertyInfo prop_info(message_prop_list);
if (prop_info.FindMatch(msg, index, spec, form, prop) >= 0) {
msg->PopSpecifier();
return this;
}
return BView::ResolveSpecifier(msg, index, spec, form, prop);
}
status_t
NotificationView::GetSupportedSuites(BMessage* msg)
{
msg->AddString("suites", "suite/x-vnd.Haiku-notification_server");
BPropertyInfo prop_info(message_prop_list);
msg->AddFlat("messages", &prop_info);
return BView::GetSupportedSuites(msg);
}
const char*
NotificationView::Application() const
{
return fApp.Length() > 0 ? fApp.String() : NULL;
}
const char*
NotificationView::Title() const
{
return fTitle.Length() > 0 ? fTitle.String() : NULL;
}
const char*
NotificationView::Text() const
{
return fText.Length() > 0 ? fText.String() : NULL;
}
void
NotificationView::SetText(const char* app, const char* title, const char* text,
float newMaxWidth)
{
if (newMaxWidth < 0)
newMaxWidth = Bounds().Width() - (kEdgePadding * 2);
// Delete old lines
LineInfoList::iterator lIt;
for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
delete (*lIt);
fLines.clear();
fApp = app;
fTitle = title;
fText = text;
float iconRight = kEdgePadding + kEdgePadding;
if (fBitmap != NULL)
iconRight += fParent->IconSize();
font_height fh;
be_bold_font->GetHeight(&fh);
float fontHeight = ceilf(fh.leading) + ceilf(fh.descent)
+ ceilf(fh.ascent);
float y = fontHeight;
// Title
LineInfo* titleLine = new LineInfo;
titleLine->text = fTitle;
titleLine->font = *be_bold_font;
if (fParent->Layout() == AllTextRightOfIcon)
titleLine->location = BPoint(iconRight, y);
else
titleLine->location = BPoint(kEdgePadding, y);
fLines.push_front(titleLine);
y += fontHeight;
// Rest of text is rendered with be_plain_font.
be_plain_font->GetHeight(&fh);
fontHeight = ceilf(fh.leading) + ceilf(fh.descent)
+ ceilf(fh.ascent);
// Split text into chunks between certain characters and compose the lines.
const char kSeparatorCharacters[] = " \n-\\/";
BString textBuffer = fText;
textBuffer.ReplaceAll("\t", " ");
const char* chunkStart = textBuffer.String();
float maxWidth = newMaxWidth - kEdgePadding - iconRight;
LineInfo* line = NULL;
ssize_t length = textBuffer.Length();
while (chunkStart - textBuffer.String() < length) {
size_t chunkLength = strcspn(chunkStart, kSeparatorCharacters) + 1;
// Start a new line if either we didn't start one before,
// the current offset
BString tempText(line != NULL ? line->text : "");
tempText.Append(chunkStart, chunkLength);
if (line == NULL || chunkStart[0] == '\n'
|| StringWidth(tempText) > maxWidth) {
line = new LineInfo;
line->font = *be_plain_font;
line->location = BPoint(iconRight + kEdgePadding, y);
fLines.push_front(line);
y += fontHeight;
// Skip the eventual new-line character at the beginning of this
// chunk.
if (chunkStart[0] == '\n') {
chunkStart++;
chunkLength--;
}
// Skip more new-line characters and move the line further down.
while (chunkStart[0] == '\n') {
chunkStart++;
chunkLength--;
line->location.y += fontHeight;
y += fontHeight;
}
// Strip space at beginning of a new line.
while (chunkStart[0] == ' ') {
chunkLength--;
chunkStart++;
}
}
if (chunkStart[0] == '\0')
break;
// Append the chunk to the current line, which was either a new
// line or the one from the previous iteration.
line->text.Append(chunkStart, chunkLength);
chunkStart += chunkLength;
}
fHeight = y + (kEdgePadding * 2);
// Make sure icon fits
if (fBitmap != NULL) {
float minHeight = 0;
if (fParent->Layout() == TitleAboveIcon) {
LineInfo* appLine = fLines.back();
font_height fh;
appLine->font.GetHeight(&fh);
minHeight = appLine->location.y + fh.descent;
}
minHeight += fBitmap->Bounds().Height() + 2 * kEdgePadding;
if (fHeight < minHeight)
fHeight = minHeight;
}
BMessenger(Parent()).SendMessage(kResizeToFit);
}
bool
NotificationView::HasMessageID(const char* id)
{
return fMessageID == id;
}
const char*
NotificationView::MessageID()
{
return fMessageID.String();
}
void
NotificationView::SetPosition(bool first, bool last)
{
fIsFirst = first;
fIsLast = last;
}
BBitmap*
NotificationView::_ReadNodeIcon(const char* fileName, icon_size size)
{
BEntry entry(fileName, true);
entry_ref ref;
entry.GetRef(&ref);
BNode node(BPath(&ref).Path());
BBitmap* ret = new BBitmap(BRect(0, 0, (float)size - 1, (float)size - 1), B_RGBA32);
if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute,
kLargeIconAttribute, size, ret) != B_OK) {
delete ret;
ret = NULL;
}
return ret;
}
void
NotificationView::_LoadIcon()
{
// First try to get the icon from the caller application
app_info info;
BMessenger msgr = fDetails->ReturnAddress();
if (msgr.IsValid())
be_roster->GetRunningAppInfo(msgr.Team(), &info);
else if (fType == B_PROGRESS_NOTIFICATION)
be_roster->GetAppInfo("application/x-vnd.Haiku-notification_server",
&info);
BPath path;
path.SetTo(&info.ref);
fBitmap = _ReadNodeIcon(path.Path(), fParent->IconSize());
if (fBitmap)
return;
// If that failed get icons from app_server
if (find_directory(B_BEOS_SERVERS_DIRECTORY, &path) != B_OK)
return;
path.Append("app_server");
BFile file(path.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return;
BResources res(&file);
if (res.InitCheck() != B_OK)
return;
// Which one should we choose?
const char* iconName = "";
switch (fType) {
case B_INFORMATION_NOTIFICATION:
iconName = "info";
break;
case B_ERROR_NOTIFICATION:
iconName = "stop";
break;
case B_IMPORTANT_NOTIFICATION:
iconName = "warn";
break;
default:
return;
}
// Allocate the bitmap
fBitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1,
(float)B_LARGE_ICON - 1), B_RGBA32);
if (!fBitmap || fBitmap->InitCheck() != B_OK) {
fBitmap = NULL;
return;
}
// Load raw icon data
size_t size = 0;
const uint8* data = (const uint8*)res.LoadResource(B_VECTOR_ICON_TYPE,
iconName, &size);
if ((data == NULL
|| BIconUtils::GetVectorIcon(data, size, fBitmap) != B_OK))
fBitmap = NULL;
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_VIEW_H
#define _NOTIFICATION_VIEW_H
#include <list>
#include <Bitmap.h>
#include <Entry.h>
#include <Message.h>
#include <MessageRunner.h>
#include <MimeType.h>
#include <Notification.h>
#include <Path.h>
#include <Roster.h>
#include <String.h>
#include <TextView.h>
#include <View.h>
class NotificationWindow;
const uint32 kRemoveView = 'ReVi';
class NotificationView : public BView {
public:
NotificationView(NotificationWindow* win,
notification_type type,
const char* app, const char* title,
const char* text, BMessage* details);
virtual ~NotificationView();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message);
virtual void GetPreferredSize(float* width, float* height);
virtual void Draw(BRect updateRect);
virtual void MouseDown(BPoint point);
virtual void FrameResized(float width, float height);
virtual BHandler* ResolveSpecifier(BMessage* msg, int32 index,
BMessage* specifier, int32 form,
const char* property);
virtual status_t GetSupportedSuites(BMessage* msg);
const char* Application() const;
const char* Title() const;
const char* Text() const;
void SetText(const char* app, const char* title,
const char* text, float newMaxWidth = -1);
bool HasMessageID(const char* id);
const char* MessageID();
void SetPosition(bool first, bool last);
private:
BBitmap* _ReadNodeIcon(const char* fileName,
icon_size size);
void _LoadIcon();
private:
struct LineInfo {
BFont font;
BString text;
BPoint location;
};
typedef std::list<LineInfo*> LineInfoList;
NotificationWindow* fParent;
notification_type fType;
BMessageRunner* fRunner;
float fProgress;
BString fMessageID;
BMessage* fDetails;
BBitmap* fBitmap;
LineInfoList fLines;
BString fApp;
BString fTitle;
BString fText;
float fHeight;
bool fIsFirst;
bool fIsLast;
};
#endif // _NOTIFICATION_VIEW_H

View File

@ -0,0 +1,598 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <algorithm>
#include <Alert.h>
#include <Application.h>
#include <Debug.h>
#include <File.h>
#include <NodeMonitor.h>
#include <PropertyInfo.h>
#include "AppGroupView.h"
#include "AppUsage.h"
#include "BorderView.h"
#include "NotificationWindow.h"
property_info main_prop_list[] = {
{ "message", {B_GET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0}, "get a message"},
{ "message", {B_COUNT_PROPERTIES, 0}, {B_DIRECT_SPECIFIER, 0}, "count messages"},
{ "message", {B_CREATE_PROPERTY, 0}, {B_DIRECT_SPECIFIER, 0}, "create a message"},
{ "message", {B_SET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0 }, "modify a message" },
0
};
const float kCloseSize = 8;
const float kExpandSize = 8;
const float kPenSize = 1;
const float kEdgePadding = 5;
const float kSmallPadding = 2;
NotificationWindow::NotificationWindow()
:
BWindow(BRect(10, 10, 30, 30), "Notification", B_BORDERED_WINDOW,
B_AVOID_FRONT | B_AVOID_FOCUS | B_NOT_CLOSABLE | B_NOT_ZOOMABLE
| B_NOT_MINIMIZABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES)
{
fBorder = new BorderView(Bounds(), "Notification");
AddChild(fBorder);
Show();
Hide();
LoadSettings(true);
LoadAppFilters(true);
}
NotificationWindow::~NotificationWindow()
{
appfilter_t::iterator aIt;
for (aIt = fAppFilters.begin(); aIt != fAppFilters.end(); aIt++)
delete aIt->second;
}
bool
NotificationWindow::QuitRequested()
{
appview_t::iterator aIt;
for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
aIt->second->RemoveSelf();
delete aIt->second;
}
BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
return BWindow::QuitRequested();
}
void
NotificationWindow::WorkspaceActivated(int32 /*workspace*/, bool active)
{
// Ensure window is in the correct position
if (active)
ResizeAll();
}
void
NotificationWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_NODE_MONITOR:
{
LoadSettings();
LoadAppFilters();
break;
}
case kResizeToFit:
ResizeAll();
break;
case B_COUNT_PROPERTIES:
{
BMessage reply(B_REPLY);
BMessage specifier;
const char* property = NULL;
bool messageOkay = true;
if (message->FindMessage("specifiers", 0, &specifier) != B_OK)
messageOkay = false;
if (specifier.FindString("property", &property) != B_OK)
messageOkay = false;
if (strcmp(property, "message") != 0)
messageOkay = false;
if (messageOkay)
reply.AddInt32("result", fViews.size());
else {
reply.what = B_MESSAGE_NOT_UNDERSTOOD;
reply.AddInt32("error", B_ERROR);
}
message->SendReply(&reply);
break;
}
case B_CREATE_PROPERTY:
case kNotificationMessage:
{
int32 type;
const char* content = NULL;
const char* title = NULL;
const char* app = NULL;
BMessage reply(B_REPLY);
bool messageOkay = true;
if (message->FindInt32("type", &type) != B_OK)
type = B_INFORMATION_NOTIFICATION;
if (message->FindString("content", &content) != B_OK)
messageOkay = false;
if (message->FindString("title", &title) != B_OK)
messageOkay = false;
if (message->FindString("app", &app) != B_OK && message->FindString("appTitle", &app) != B_OK)
messageOkay = false;
if (messageOkay) {
NotificationView* view = new NotificationView(this, (notification_type)type, app,
title, content, new BMessage(*message));
appfilter_t::iterator fIt = fAppFilters.find(app);
bool allow = false;
if (fIt == fAppFilters.end()) {
app_info info;
BMessenger messenger = message->ReturnAddress();
if (messenger.IsValid())
be_roster->GetRunningAppInfo(messenger.Team(), &info);
else
be_roster->GetAppInfo("application/x-vnd.Be-SHEL", &info);
AppUsage* appUsage = new AppUsage(info.ref, app, true);
fAppFilters[app] = appUsage;
appUsage->Allowed(title, (notification_type)type);
allow = true;
} else
allow = fIt->second->Allowed(title, (notification_type)type);
if (allow) {
appview_t::iterator aIt = fAppViews.find(app);
AppGroupView *group = NULL;
if (aIt == fAppViews.end()) {
group = new AppGroupView(this, app);
fAppViews[app] = group;
fBorder->AddChild(group);
} else {
group = aIt->second;
};
group->AddInfo(view);
ResizeAll();
reply.AddInt32("error", B_OK);
} else
reply.AddInt32("Error", B_ERROR);
} else {
reply.what = B_MESSAGE_NOT_UNDERSTOOD;
reply.AddInt32("error", B_ERROR);
}
message->SendReply(&reply);
break;
}
case kRemoveView:
{
void* _ptr;
message->FindPointer("view", &_ptr);
NotificationView* info
= reinterpret_cast<NotificationView*>(_ptr);
fBorder->RemoveChild(info);
std::vector<NotificationView*>::iterator i
= find(fViews.begin(), fViews.end(), info);
if (i != fViews.end())
fViews.erase(i);
delete info;
ResizeAll();
break;
}
default:
BWindow::MessageReceived(message);
}
}
BHandler*
NotificationWindow::ResolveSpecifier(BMessage *msg, int32 index,
BMessage *spec, int32 form, const char *prop)
{
BPropertyInfo prop_info(main_prop_list);
BHandler* handler = NULL;
if (strcmp(prop,"message") == 0) {
switch (msg->what) {
case B_CREATE_PROPERTY:
{
msg->PopSpecifier();
handler = this;
break;
}
case B_SET_PROPERTY:
case B_GET_PROPERTY:
{
int32 i;
if (spec->FindInt32("index", &i) != B_OK)
i = -1;
if (i >= 0 && i < (int32)fViews.size()) {
msg->PopSpecifier();
handler = fViews[i];
} else
handler = NULL;
break;
}
case B_COUNT_PROPERTIES:
msg->PopSpecifier();
handler = this;
break;
default:
break;
}
}
if (!handler)
handler = BWindow::ResolveSpecifier(msg, index, spec, form, prop);
return handler;
}
icon_size
NotificationWindow::IconSize()
{
return fIconSize;
}
int32
NotificationWindow::Timeout()
{
return fTimeout;
}
infoview_layout
NotificationWindow::Layout()
{
return fLayout;
}
float
NotificationWindow::ViewWidth()
{
return fWidth;
}
void
NotificationWindow::ResizeAll()
{
if (fAppViews.empty()) {
if (!IsHidden())
Hide();
return;
}
appview_t::iterator aIt;
bool shouldHide = true;
for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
AppGroupView *app = aIt->second;
if (app->HasChildren()) {
shouldHide = false;
break;
}
}
if (shouldHide) {
if (!IsHidden())
Hide();
return;
}
if (IsHidden())
Show();
float width = 0;
float height = 0;
for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
AppGroupView* view = aIt->second;
float w = -1;
float h = -1;
if (!view->HasChildren()) {
if (!view->IsHidden())
view->Hide();
} else {
view->GetPreferredSize(&w, &h);
width = max_c(width, h);
view->ResizeToPreferred();
view->MoveTo(0, height);
height += h;
if (view->IsHidden())
view->Show();
}
}
ResizeTo(ViewWidth(), height);
PopupAnimation(Bounds().Width(), Bounds().Height());
}
void
NotificationWindow::PopupAnimation(float width, float height)
{
float x = 0, y = 0, sx, sy;
float pad = 0;
BDeskbar deskbar;
BRect frame = deskbar.Frame();
switch (deskbar.Location()) {
case B_DESKBAR_TOP:
// Put it just under, top right corner
sx = frame.right;
sy = frame.bottom+pad;
y = sy;
x = sx-width-pad;
break;
case B_DESKBAR_BOTTOM:
// Put it just above, lower left corner
sx = frame.right;
sy = frame.top-height-pad;
y = sy;
x = sx - width-pad;
break;
case B_DESKBAR_LEFT_TOP:
// Put it just to the right of the deskbar
sx = frame.right+pad;
sy = frame.top-height;
x = sx;
y = frame.top+pad;
break;
case B_DESKBAR_RIGHT_TOP:
// Put it just to the left of the deskbar
sx = frame.left-width-pad;
sy = frame.top-height;
x = sx;
y = frame.top+pad;
break;
case B_DESKBAR_LEFT_BOTTOM:
// Put it to the right of the deskbar.
sx = frame.right+pad;
sy = frame.bottom;
x = sx;
y = sy-height-pad;
break;
case B_DESKBAR_RIGHT_BOTTOM:
// Put it to the left of the deskbar.
sx = frame.left-width-pad;
sy = frame.bottom;
y = sy-height-pad;
x = sx;
break;
default:
break;
}
MoveTo(x, y);
if (IsHidden() && fViews.size() != 0)
Show();
//Activate();// it hides floaters from apps :-(
}
void
NotificationWindow::LoadSettings(bool startMonitor)
{
_LoadGeneralSettings(startMonitor);
_LoadDisplaySettings(startMonitor);
}
void
NotificationWindow::LoadAppFilters(bool startMonitor)
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append(kSettingsDirectory);
if (create_directory(path.Path(), 0755) != B_OK)
return;
path.Append(kFiltersSettings);
BFile file(path.Path(), B_READ_ONLY);
BMessage settings;
if (settings.Unflatten(&file) != B_OK)
return;
type_code type;
int32 count = 0;
if (settings.GetInfo("app_usage", &type, &count) != B_OK)
return;
for (int32 i = 0; i < count; i++) {
AppUsage* app = new AppUsage();
settings.FindFlat("app_usage", i, app);
fAppFilters[app->Name()] = app;
}
if (startMonitor) {
node_ref nref;
BEntry entry(path.Path());
entry.GetNodeRef(&nref);
if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
BAlert* alert = new BAlert("", "Couldn't start filter "
" monitor. Live filter changes disabled.", "Darn.");
alert->Go();
}
}
}
void
NotificationWindow::SaveAppFilters()
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append(kSettingsDirectory);
path.Append(kFiltersSettings);
BMessage settings;
BFile file(path.Path(), B_WRITE_ONLY);
appfilter_t::iterator fIt;
for (fIt = fAppFilters.begin(); fIt != fAppFilters.end(); fIt++)
settings.AddFlat("app_usage", fIt->second);
settings.Flatten(&file);
}
void
NotificationWindow::_LoadGeneralSettings(bool startMonitor)
{
BPath path;
BMessage settings;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append(kSettingsDirectory);
if (create_directory(path.Path(), 0755) == B_OK) {
path.Append(kGeneralSettings);
BFile file(path.Path(), B_READ_ONLY);
settings.Unflatten(&file);
}
if (settings.FindInt32(kTimeoutName, &fTimeout) != B_OK)
fTimeout = kDefaultTimeout;
// Notify the view about the change
views_t::iterator it;
for (it = fViews.begin(); it != fViews.end(); ++it) {
NotificationView* view = (*it);
view->SetText(view->Application(), view->Title(), view->Text());
view->Invalidate();
}
if (startMonitor) {
node_ref nref;
BEntry entry(path.Path());
entry.GetNodeRef(&nref);
if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
BAlert* alert = new BAlert("", "Couldn't start general settings "
" monitor.\nLive filter changes disabled.", "OK");
alert->Go();
}
}
}
void
NotificationWindow::_LoadDisplaySettings(bool startMonitor)
{
BPath path;
BMessage settings;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append(kSettingsDirectory);
if (create_directory(path.Path(), 0755) == B_OK) {
path.Append(kDisplaySettings);
BFile file(path.Path(), B_READ_ONLY);
settings.Unflatten(&file);
}
int32 setting;
if (settings.FindFloat(kWidthName, &fWidth) != B_OK)
fWidth = kDefaultWidth;
if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
fIconSize = kDefaultIconSize;
else
fIconSize = (icon_size)setting;
if (settings.FindInt32(kLayoutName, &setting) != B_OK)
fLayout = kDefaultLayout;
else {
switch (setting) {
case 0:
fLayout = TitleAboveIcon;
break;
case 1:
fLayout = AllTextRightOfIcon;
break;
default:
fLayout = kDefaultLayout;
}
}
// Notify the view about the change
views_t::iterator it;
for (it = fViews.begin(); it != fViews.end(); ++it) {
NotificationView* view = (*it);
view->SetText(view->Application(), view->Title(), view->Text());
view->Invalidate();
}
if (startMonitor) {
node_ref nref;
BEntry entry(path.Path());
entry.GetNodeRef(&nref);
if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
BAlert* alert = new BAlert("", "Couldn't start display settings "
" monitor.\nLive filter changes disabled.", "OK");
alert->Go();
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_WINDOW_H
#define _NOTIFICATION_WINDOW_H
#include <cmath>
#include <vector>
#include <map>
#include <Directory.h>
#include <Deskbar.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Message.h>
#include <Notifications.h>
#include <PropertyInfo.h>
#include <String.h>
#include <Window.h>
#include "NotificationView.h"
class AppGroupView;
class AppUsage;
class BorderView;
class SettingsFile;
typedef std::map<BString, AppGroupView*> appview_t;
typedef std::map<BString, AppUsage*> appfilter_t;
typedef std::vector<NotificationView*> views_t;
extern const float kEdgePadding;
extern const float kSmallPadding;
extern const float kCloseSize;
extern const float kExpandSize;
extern const float kPenSize;
const uint32 kResizeToFit = 'IWrf';
class NotificationWindow : public BWindow {
public:
NotificationWindow();
virtual ~NotificationWindow();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage*);
virtual void WorkspaceActivated(int32, bool);
virtual BHandler* ResolveSpecifier(BMessage*, int32, BMessage*,
int32, const char*);
icon_size IconSize();
int32 Timeout();
infoview_layout Layout();
float ViewWidth();
void ResizeAll();
private:
friend class AppGroupView;
void PopupAnimation(float, float);
void LoadSettings(bool startMonitor = false);
void LoadAppFilters(bool startMonitor = false);
void SaveAppFilters();
void _LoadGeneralSettings(bool startMonitor);
void _LoadDisplaySettings(bool startMonitor);
views_t fViews;
BorderView* fBorder;
appview_t fAppViews;
BString fStatusText;
BString fMessageText;
float fWidth;
icon_size fIconSize;
int32 fTimeout;
infoview_layout fLayout;
appfilter_t fAppFilters;
};
extern property_info main_prop_list[];
#endif // _NOTIFICATION_WINDOW_H

View File

@ -0,0 +1,33 @@
resource app_signature "application/x-vnd.Haiku-notification_server";
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_ALPHA,
internal = 0,
short_info = "notification_server",
long_info = "notification_server ©2010 Haiku Inc."
};
resource vector_icon {
$"6E636966060401780501020106023A06C63967B3B838E138A51E4B2EAA46CD17"
$"C3677F93FFFFFFFF02030603BF80A63DDD21BA0661BBABD34BDD9CC05D0900FF"
$"DA8F3EFFAA008290600202030603BBE0A53C15CEB9A968B967964C2B2D459EFF"
$"00FFDA8F29FFAA0054906002020106053CFCDB3CFBB1BB18EC3B1A2148FE2D4A"
$"C91D89FFFFFFA8BAC6D1C3677F93E2BAC6D1FFFFFFFF040406FE034460C6E460"
$"C4C6605460575C565E585A5E345E3460335D2E61305D2E4400054E244E24C5DB"
$"B453C756B4915123C756B491562655255727C95329C95329592B592E592D592E"
$"0616FFABAAAAAA0A4E244E24C5DBB453C756B4915123C756B491562655255727"
$"C95329C95329592B592E592D592E4957475A425C3C5B365832552F532C50284C"
$"264924462242213F203B2136233425330A1A4957475A425C3C5B365832552F53"
$"2C50284C264924462242213F203B2136233425332C333135353738393F3F4344"
$"4547484D4950070A000100000A01010130222201178822040A01010230222201"
$"178400040A02010130222201178422040A0301022022220A0401032022220A05"
$"0103123FBBD40000000000003FA51943DCFC44C1340117840004"
};