Ticket #1245: infopopper.patch

File infopopper.patch, 141.6 KB (added by plfiorini, 9 years ago)

infopopper integration patch

  • build/jam/HaikuImage

    diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage
    index 9e9cd66..24f585a 100644
    a b SYSTEM_BIN = "[" addattr alert arp base64 basename bash bc beep bootman bzip2 
    4444    listport listres listsem listusb ln locate logger login logname ls lsindex
    4545    mail2mbox makebootable mbox2mail md5sum merge message mimeset mkdos mkdir
    4646    mkfifo mkfs mkindex mktemp modifiers mount mount_nfs mountvolume mv
    47     netcat netstat nl nohup nproc
     47    netcat netstat nl nohup notify nproc
    4848    od open
    4949    passwd paste patch pathchk pc ping play playfile playsound playwav pr prio
    5050    printenv printf profile ps ptx pwd
    SYSTEM_APPS = AboutSystem ActivityMonitor CharacterMap CodyCam DeskCalc Devices 
    7070;
    7171SYSTEM_PREFERENCES = Appearance Backgrounds CPUFrequency DataTranslations
    7272    <preference>Deskbar E-mail FileTypes Fonts Keyboard Keymap Locale Media
    73     Mouse Network OpenGL Printers Screen ScreenSaver Shortcuts Sounds Time
     73    Mouse Network Notifications OpenGL Printers Screen ScreenSaver Shortcuts Sounds Time
    7474    Touchpad <preference>Tracker VirtualMemory
    7575;
    7676SYSTEM_DEMOS = BSnow Chart Clock Cortex FontDemo
    PRIVATE_SYSTEM_LIBS = 
    103103;
    104104SYSTEM_SERVERS = app_server cddb_daemon debug_server input_server mail_daemon
    105105    media_addon_server media_server midi_server mount_server net_server
    106     print_server registrar syslog_daemon
     106    notification_server print_server registrar syslog_daemon
    107107;
    108108
    109109SYSTEM_NETWORK_DEVICES = ethernet loopback ;
  • new file headers/os/app/Notification.h

    diff --git a/headers/os/app/Notification.h b/headers/os/app/Notification.h
    new file mode 100644
    index 0000000..6f9c100
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef _NOTIFICATION_H
     6#define _NOTIFICATION_H
     7
     8#include <Entry.h>
     9
     10// notification types
     11enum notification_type {
     12    B_INFORMATION_NOTIFICATION,
     13    B_IMPORTANT_NOTIFICATION,
     14    B_ERROR_NOTIFICATION,
     15    B_PROGRESS_NOTIFICATION
     16};
     17
     18class BBitmap;
     19class BList;
     20
     21class BNotification {
     22public:
     23                        BNotification(notification_type type);
     24                        ~BNotification();
     25
     26    notification_type   Type() const;
     27
     28    const char*         Application() const;
     29    void                SetApplication(const char* app);
     30
     31    const char*         Title() const;
     32    void                SetTitle(const char* title);
     33
     34    const char*         Content() const;
     35    void                SetContent(const char* content);
     36
     37    const char*         MessageID() const;
     38    void                SetMessageID(const char* id);
     39
     40    float               Progress() const;
     41    void                SetProgress(float progress);
     42
     43    const char*         OnClickApp() const;
     44    void                SetOnClickApp(const char* app);
     45
     46    entry_ref*          OnClickFile() const;
     47    void                SetOnClickFile(const entry_ref* file);
     48
     49    BList*              OnClickRefs() const;
     50    void                AddOnClickRef(const entry_ref* ref);
     51
     52    BList*              OnClickArgv() const;
     53    void                AddOnClickArg(const char* arg);
     54
     55    BBitmap*            Icon() const;
     56    void                SetIcon(BBitmap* icon);
     57
     58private:
     59    notification_type   fType;
     60    char*               fAppName;
     61    char*               fTitle;
     62    char*               fContent;
     63    char*               fID;
     64    float               fProgress;
     65    char*               fApp;
     66    entry_ref*          fFile;
     67    BList*              fRefs;
     68    BList*              fArgv;
     69    BBitmap*            fBitmap;
     70};
     71
     72#endif  // _NOTIFICATION_H
  • headers/os/app/Roster.h

    diff --git a/headers/os/app/Roster.h b/headers/os/app/Roster.h
    index 50a6612..641c9c6 100644
    a b  
    1313class BFile;
    1414class BMimeType;
    1515class BNodeInfo;
     16class BNotification;
    1617
    1718
    1819struct app_info {
    class BRoster { 
    116117        void AddToRecentFolders(const entry_ref *folder,
    117118                    const char *appSig = 0) const;
    118119
     120        // notifications
     121        status_t Notify(BNotification* notification,
     122                    int32 timeout = -1) const;
     123
    119124        // private/reserved stuff starts here
    120125        class Private;
    121126
  • new file headers/private/notification/AppUsage.h

    diff --git a/headers/private/notification/AppUsage.h b/headers/private/notification/AppUsage.h
    new file mode 100644
    index 0000000..c245c77
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 */
     8#ifndef _APP_USAGE_H
     9#define _APP_USAGE_H
     10
     11#include <map>
     12
     13#include <Entry.h>
     14#include <Flattenable.h>
     15#include <Notification.h>
     16#include <Roster.h>
     17#include <String.h>
     18
     19class BMessage;
     20class NotificationReceived;
     21
     22typedef std::map<BString, NotificationReceived*> notify_t;
     23
     24class AppUsage : public BFlattenable {
     25public:
     26                                        AppUsage();
     27                                        AppUsage(entry_ref ref, const char* name,
     28                                            bool allow = true);
     29                                        ~AppUsage();
     30
     31    virtual bool                        AllowsTypeCode(type_code code) const;
     32    virtual status_t                    Flatten(void* buffer, ssize_t numBytes) const;
     33    virtual ssize_t                     FlattenedSize() const;
     34    virtual bool                        IsFixedSize() const;
     35    virtual type_code                   TypeCode() const;
     36    virtual status_t                    Unflatten(type_code code, const void* buffer,
     37                                            ssize_t numBytes);
     38
     39            entry_ref                   Ref();
     40            const char*                 Name();
     41            bool                        Allowed(const char* title, notification_type type);
     42            bool                        Allowed();
     43            NotificationReceived*       NotificationAt(int32 index);
     44            int32                       Notifications();
     45            void                        AddNotification(NotificationReceived* notification);
     46
     47private:
     48            entry_ref                   fRef;
     49            BString                     fName;
     50            bool                        fAllow;
     51            notify_t                    fNotifications;
     52};
     53
     54#endif  // _APP_USAGE_H
  • new file headers/private/notification/NotificationReceived.h

    diff --git a/headers/private/notification/NotificationReceived.h b/headers/private/notification/NotificationReceived.h
    new file mode 100644
    index 0000000..9d8a88f
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 */
     8#ifndef _NOTIFICATION_RECEIVED_H
     9#define _NOTIFICATION_RECEIVED_H
     10
     11#include <Flattenable.h>
     12#include <Roster.h>
     13#include <String.h>
     14
     15class NotificationReceived : public BFlattenable {
     16public:
     17                                NotificationReceived();
     18                                NotificationReceived(const char* title, notification_type type,
     19                                    bool enabled = true);
     20                                ~NotificationReceived();
     21
     22    virtual bool                AllowsTypeCode(type_code code) const;
     23    virtual status_t            Flatten(void *buffer, ssize_t numBytes) const;
     24    virtual ssize_t             FlattenedSize() const;
     25    virtual bool                IsFixedSize() const;
     26    virtual type_code           TypeCode() const;
     27    virtual status_t            Unflatten(type_code code, const void *buffer,
     28                                ssize_t numBytes);
     29
     30            const char*         Title();
     31            notification_type   Type();
     32            void                SetType(notification_type type);
     33            time_t              LastReceived();
     34            bool                Allowed();
     35
     36            void                SetTimeStamp(time_t time);
     37            void                UpdateTimeStamp();
     38
     39private:
     40            BString             fTitle;
     41            notification_type   fType;
     42            bool                fEnabled;
     43            time_t              fLastReceived;
     44};
     45
     46#endif  // _NOTIFICATION_RECEIVED_H
  • new file headers/private/notification/Notifications.h

    diff --git a/headers/private/notification/Notifications.h b/headers/private/notification/Notifications.h
    new file mode 100644
    index 0000000..964b026
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef _NOTIFICATIONS_H
     6#define _NOTIFICATIONS_H
     7
     8#include <Mime.h>
     9
     10#define kNotificationServerSignature "application/x-vnd.Haiku-notification_server"
     11
     12// Messages
     13const uint32 kNotificationMessage       = 'nssm';
     14
     15// Notification layout
     16enum infoview_layout {
     17    TitleAboveIcon = 0,
     18    AllTextRightOfIcon = 1
     19};
     20
     21// Settings constants
     22extern const char* kSettingsDirectory;
     23extern const char* kFiltersSettings;
     24extern const char* kGeneralSettings;
     25extern const char* kDisplaySettings;
     26
     27// General settings
     28extern const char* kAutoStartName;
     29extern const char* kTimeoutName;
     30
     31// General default settings
     32const float kDefaultAutoStart           = false;
     33const int32 kDefaultTimeout             = 10;
     34
     35// Display settings
     36extern const char* kWidthName;
     37extern const char* kIconSizeName;
     38extern const char* kLayoutName;
     39
     40// Display default settings
     41const float kDefaultWidth               = 300.0f;
     42const icon_size kDefaultIconSize        = B_LARGE_ICON;
     43const infoview_layout kDefaultLayout    = TitleAboveIcon;
     44
     45#endif  // _NOTIFICATIONS_H
  • src/bin/Jamfile

    diff --git a/src/bin/Jamfile b/src/bin/Jamfile
    index 03e8ebd..5425646 100644
    a b UsePrivateSystemHeaders ; 
    99SubDirHdrs $(HAIKU_TOP) src add-ons kernel file_cache ;
    1010UseLibraryHeaders ncurses ;
    1111UseLibraryHeaders termcap ;
     12UseLibraryHeaders icon ;
    1213
    1314local haiku-utils_rsrc = [ FGristFiles haiku-utils.rsrc ] ;
    1415
    StdBinCommands 
    157158    translate.cpp
    158159    : be translation $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
    159160
     161# standard commands that need libbe.so, libtranslation.so, libicon.a, libstdc++.so
     162StdBinCommands
     163    notify.cpp
     164    : be translation libicon.a $(TARGET_LIBSTDC++) : $(haiku-utils_rsrc) ;
     165
    160166# standard commands that need libbe.so, libmedia.so
    161167StdBinCommands
    162168    installsound.cpp
  • new file src/bin/notify.cpp

    diff --git a/src/bin/notify.cpp b/src/bin/notify.cpp
    new file mode 100644
    index 0000000..1528f39
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All rights reserved.
     3 * Copyright 2008, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *              Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
     8 */
     9
     10#include <stdio.h>
     11#include <stdlib.h>
     12#include <string.h>
     13
     14#include <Application.h>
     15#include <Bitmap.h>
     16#include <IconUtils.h>
     17#include <List.h>
     18#include <Mime.h>
     19#include <Notification.h>
     20#include <Path.h>
     21#include <Roster.h>
     22#include <TranslationUtils.h>
     23
     24const char* kSignature          = "application/x-vnd.Haiku-notify";
     25const char* kSmallIconAttribute = "BEOS:M:STD_ICON";
     26const char* kLargeIconAttribute = "BEOS:L:STD_ICON";
     27const char* kIconAttribute      = "BEOS:ICON";
     28
     29const char *kTypeNames[] = {
     30    "information",
     31    "important",
     32    "error",
     33    "progress",
     34    NULL
     35};
     36
     37const int32 kErrorInitFail      = 127;
     38const int32 kErrorArgumentsFail = 126;
     39
     40class NotifyApp : public BApplication {
     41public:
     42                                NotifyApp();
     43    virtual                     ~NotifyApp();
     44
     45    virtual void                ReadyToRun();
     46    virtual void                ArgvReceived(int32 argc, char** argv);
     47
     48            bool                GoodArguments() const { return fOk; }
     49
     50private:
     51            bool                fOk;
     52            notification_type   fType;
     53            const char*         fAppName;
     54            const char*         fTitle;
     55            const char*         fMsgId;
     56            float               fProgress;
     57            int32               fTimeout;
     58            const char*         fIconFile;
     59            entry_ref           fFileRef;
     60            const char*         fMessage;
     61            const char*         fApp;
     62            bool                fHasFile;
     63            entry_ref           fFile;
     64            BList*              fRefs;
     65            BList*              fArgv;
     66
     67            void                _Usage() const;
     68            BBitmap*            _GetBitmap(const entry_ref* ref) const;
     69};
     70
     71
     72NotifyApp::NotifyApp()
     73    :
     74    BApplication(kSignature),
     75    fOk(false),
     76    fType(B_INFORMATION_NOTIFICATION),
     77    fAppName(NULL),
     78    fTitle(NULL),
     79    fMsgId(NULL),
     80    fProgress(0.0f),
     81    fTimeout(0),
     82    fIconFile(NULL),
     83    fMessage(NULL),
     84    fApp(NULL),
     85    fHasFile(false)
     86{
     87    fRefs = new BList();
     88    fArgv = new BList();
     89}
     90
     91
     92NotifyApp::~NotifyApp()
     93{
     94    if (fAppName)
     95        free((void*)fAppName);
     96    if (fTitle)
     97        free((void*)fTitle);
     98    if (fMsgId)
     99        free((void*)fMsgId);
     100    if (fIconFile)
     101        free((void*)fIconFile);
     102    if (fMessage)
     103        free((void*)fMessage);
     104    if (fApp)
     105        free((void*)fApp);
     106
     107    int32 i;
     108    void* item;
     109
     110    for (i = 0; item = fRefs->ItemAt(i); i++)
     111        delete item;
     112    delete fRefs;
     113
     114    for (i = 0; item = fArgv->ItemAt(i); i++) {
     115        if (item != NULL)
     116            free(item);
     117    }
     118    delete fArgv;
     119}
     120
     121
     122void
     123NotifyApp::ArgvReceived(int32 argc, char** argv)
     124{
     125    const uint32 kArgCount = argc - 1;
     126    uint32 index = 1;
     127
     128    // Look for valid options
     129    for (; index <= kArgCount; ++index) {
     130        if (argv[index][0] == '-' && argv[index][1] == '-') {
     131            const char* option = argv[index] + 2;
     132
     133            if (++index > kArgCount) {
     134                // No argument to option
     135                fprintf(stderr, "Missing argument to option --%s\n\n", option);
     136                return;
     137            }
     138
     139            const char* argument = argv[index];
     140
     141            if (strcmp(option, "type") == 0) {
     142                for (int32 i = 0; kTypeNames[i]; i++) {
     143                    if (strncmp(kTypeNames[i], argument, strlen(argument)) == 0)
     144                        fType = (notification_type)i;
     145                }
     146            } else if (strcmp(option, "app") == 0) {
     147                fAppName = strdup(argument);
     148            } else if (strcmp(option, "title") == 0) {
     149                fTitle = strdup(argument);
     150            } else if (strcmp(option, "messageID") == 0) {
     151                fMsgId = strdup(argument);
     152            } else if (strcmp(option, "progress") == 0) {
     153                fProgress = atof(argument);
     154            } else if (strcmp(option, "timeout") == 0) {
     155                fTimeout = atol(argument);
     156            } else if (strcmp(option, "icon") == 0) {
     157                fIconFile = strdup(argument);
     158
     159                if (get_ref_for_path(fIconFile, &fFileRef) < B_OK) {
     160                    fprintf(stderr, "Bad icon path!\n\n");
     161                    return;
     162                }
     163            } else if (strcmp(option, "onClickApp") == 0)
     164                fApp = strdup(argument);
     165            else if (strcmp(option, "onClickFile") == 0) {
     166                if (get_ref_for_path(argument, &fFile) != B_OK) {
     167                    fprintf(stderr, "Bad path for --onClickFile!\n\n");
     168                    return;
     169                }
     170
     171                fHasFile = true;
     172            } else if (strcmp(option, "onClickRef") == 0) {
     173                entry_ref ref;
     174
     175                if (get_ref_for_path(argument, &ref) != B_OK) {
     176                    fprintf(stderr, "Bad path for --onClickRef!\n\n");
     177                    return;
     178                }
     179
     180                fRefs->AddItem((void*)new BEntry(&ref));
     181            } else if (strcmp(option, "onClickArgv") == 0)
     182                fArgv->AddItem((void*)strdup(argument));
     183            else {
     184                // Unrecognized option
     185                fprintf(stderr, "Unrecognized option --%s\n\n", option);
     186                return;
     187            }
     188        } else
     189            // Option doesn't start with '--'
     190            break;
     191
     192        if (index == kArgCount) {
     193            // No text argument provided, only '--' arguments
     194            fprintf(stderr, "Missing message argument!\n\n");
     195            return;
     196        }
     197    }
     198
     199    // Check for missing arguments
     200    if (!fAppName) {
     201        fprintf(stderr, "Missing --app argument!\n\n");
     202        return;
     203    }
     204    if (!fTitle) {
     205        fprintf(stderr, "Missing --title argument!\n\n");
     206        return;
     207    }
     208
     209    fMessage = strdup(argv[index]);
     210    fOk = true;
     211}
     212
     213void
     214NotifyApp::_Usage() const
     215{
     216    fprintf(stderr, "Usage: notify [OPTION]... [MESSAGE]\n"
     217        "Send notifications to notification_server.\n"
     218        "  --type <type>\tNotification type,\n"
     219        "               \t      <type>: ");
     220
     221    for (int32 i = 0; kTypeNames[i]; i++)
     222        fprintf(stderr, kTypeNames[i + 1] ? "%s|" : "%s\n", kTypeNames[i]);
     223
     224    fprintf(stderr,
     225        "  --app <app name>\tApplication name\n"
     226        "  --title <title>\tMessage title\n"
     227        "  --messageID <msg id>\tMessage ID\n"
     228        "  --progress <float>\tProgress, value between 0.0 and 1.0  - if type is set to progress\n"
     229        "  --timeout <secs>\tSpecify timeout\n"
     230        "  --onClickApp <signature>\tApplication to open when notification is clicked\n"
     231        "  --onClickFile <fullpath>\tFile to open when notification is clicked\n"
     232        "  --onClickRef <fullpath>\tFile to open with the application when notification is clicked\n"
     233        "  --onClickArgv <arg>\tArgument to the application when notification is clicked\n"
     234        "  --icon <icon file> Icon\n");
     235}
     236
     237
     238BBitmap*
     239NotifyApp::_GetBitmap(const entry_ref* ref) const
     240{
     241    BBitmap* bitmap = NULL;
     242
     243    // First try by contents
     244    bitmap = BTranslationUtils::GetBitmap(ref);
     245    if (bitmap)
     246        return bitmap;
     247
     248    // Then, try reading its attribute
     249    BNode node(BPath(ref).Path());
     250    bitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1,
     251        (float)B_LARGE_ICON - 1), B_RGBA32);
     252    if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute,
     253        kLargeIconAttribute, B_LARGE_ICON, bitmap) != B_OK) {
     254        delete bitmap;
     255        bitmap = NULL;
     256    }
     257
     258    return bitmap;
     259}
     260
     261
     262void
     263NotifyApp::ReadyToRun()
     264{
     265    if (GoodArguments()) {
     266        BNotification* msg = new BNotification(fType);
     267        msg->SetApplication(fAppName);
     268        msg->SetTitle(fTitle);
     269        msg->SetContent(fMessage);
     270
     271        if (fMsgId)
     272            msg->SetMessageID(fMsgId);
     273
     274        if (fType == B_PROGRESS_NOTIFICATION)
     275            msg->SetProgress(fProgress);
     276
     277        if (fIconFile) {
     278            BBitmap* bitmap = _GetBitmap(&fFileRef);
     279            if (bitmap)
     280                msg->SetIcon(bitmap);
     281        }
     282
     283        if (fApp)
     284            msg->SetOnClickApp(fApp);
     285
     286        if (fHasFile)
     287            msg->SetOnClickFile(&fFile);
     288
     289        int32 i;
     290        void* item;
     291
     292        for (i = 0; item = fRefs->ItemAt(i); i++) {
     293            BEntry* entry = (BEntry*)item;
     294
     295            entry_ref ref;
     296            if (entry->GetRef(&ref) == B_OK)
     297                msg->AddOnClickRef(&ref);
     298        }
     299
     300        for (i = 0; item = fArgv->ItemAt(i); i++) {
     301            const char* arg = (const char*)item;
     302            msg->AddOnClickArg(arg);
     303        }
     304
     305        be_roster->Notify(msg, fTimeout);
     306    } else
     307        _Usage();
     308
     309    Quit();
     310}
     311
     312
     313int
     314main(int argc, char** argv)
     315{
     316    NotifyApp app;
     317    if (app.InitCheck() != B_OK)
     318        return kErrorInitFail;
     319
     320    app.Run();
     321    if (!app.GoodArguments())
     322        return kErrorArgumentsFail;
     323
     324    return 0;
     325}
  • src/kits/Jamfile

    diff --git a/src/kits/Jamfile b/src/kits/Jamfile
    index 5adc2d5..7a09a4c 100644
    a b SubInclude HAIKU_TOP src kits media ; 
    8181SubInclude HAIKU_TOP src kits midi ;
    8282SubInclude HAIKU_TOP src kits midi2 ;
    8383SubInclude HAIKU_TOP src kits network ;
     84SubInclude HAIKU_TOP src kits notification ;
    8485SubInclude HAIKU_TOP src kits opengl ;
    8586SubInclude HAIKU_TOP src kits print ;
    8687SubInclude HAIKU_TOP src kits screensaver ;
  • src/kits/app/Jamfile

    diff --git a/src/kits/app/Jamfile b/src/kits/app/Jamfile
    index 686116d..cf85c79 100644
    a b if $(RUN_WITHOUT_APP_SERVER) != 0 { 
    1717    SubDirC++Flags $(defines) ;
    1818}
    1919
    20 UsePrivateHeaders shared app interface kernel ;
     20UsePrivateHeaders shared app interface kernel notification ;
    2121UsePrivateSystemHeaders ;
    2222
    2323SetSubDirSupportedPlatforms haiku libbe_test ;
    MergeObject <libbe>app_kit.o : 
    4545    MessageRunner.cpp
    4646    Messenger.cpp
    4747    MessageUtils.cpp
     48    Notification.cpp
    4849    PropertyInfo.cpp
    4950    PortLink.cpp
    5051    RegistrarDefs.cpp
  • new file src/kits/app/Notification.cpp

    diff --git a/src/kits/app/Notification.cpp b/src/kits/app/Notification.cpp
    new file mode 100644
    index 0000000..e39c927
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Distributed under the terms of the MIT License.
     4 *
     5 * Authors:
     6 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     7 */
     8
     9#include <stdlib.h>
     10#include <string.h>
     11
     12#include <Bitmap.h>
     13#include <List.h>
     14#include <Message.h>
     15#include <Notification.h>
     16
     17
     18BNotification::BNotification(notification_type type)
     19    :
     20    fType(type),
     21    fAppName(NULL),
     22    fTitle(NULL),
     23    fContent(NULL),
     24    fID(NULL),
     25    fApp(NULL),
     26    fFile(NULL),
     27    fBitmap(NULL)
     28{
     29    fRefs = new BList();
     30    fArgv = new BList();
     31}
     32
     33
     34BNotification::~BNotification()
     35{
     36    if (fAppName)
     37        free(fAppName);
     38    if (fTitle)
     39        free(fTitle);
     40    if (fContent)
     41        free(fContent);
     42    if (fID)
     43        free(fID);
     44    if (fApp)
     45        free(fApp);
     46
     47    delete fRefs;
     48    delete fArgv;
     49}
     50
     51
     52notification_type
     53BNotification::Type() const
     54{
     55    return fType;
     56}
     57
     58
     59const char*
     60BNotification::Application() const
     61{
     62    return fAppName;
     63}
     64
     65
     66void
     67BNotification::SetApplication(const char* app)
     68{
     69    free(fAppName);
     70    fAppName = NULL;
     71
     72    if (app)
     73        fAppName = strdup(app);
     74}
     75
     76
     77const char*
     78BNotification::Title() const
     79{
     80    return fTitle;
     81}
     82
     83
     84void
     85BNotification::SetTitle(const char* title)
     86{
     87    free(fTitle);
     88    fTitle = NULL;
     89
     90    if (title)
     91        fTitle = strdup(title);
     92}
     93
     94
     95const char*
     96BNotification::Content() const
     97{
     98    return fContent;
     99}
     100
     101
     102void
     103BNotification::SetContent(const char* content)
     104{
     105    free(fContent);
     106    fContent = NULL;
     107
     108    if (content)
     109        fContent = strdup(content);
     110}
     111
     112
     113const char*
     114BNotification::MessageID() const
     115{
     116    return fID;
     117}
     118
     119
     120void
     121BNotification::SetMessageID(const char* id)
     122{
     123    free(fID);
     124    fID = NULL;
     125
     126    if (id)
     127        fID = strdup(id);
     128}
     129
     130
     131float
     132BNotification::Progress() const
     133{
     134    return fProgress;
     135}
     136
     137
     138void
     139BNotification::SetProgress(float progress)
     140{
     141    fProgress = progress;
     142}
     143
     144
     145const char*
     146BNotification::OnClickApp() const
     147{
     148    return fApp;
     149}
     150
     151
     152void
     153BNotification::SetOnClickApp(const char* app)
     154{
     155    free(fApp);
     156    fApp = NULL;
     157
     158    if (app)
     159        fApp = strdup(app);
     160}
     161
     162
     163entry_ref*
     164BNotification::OnClickFile() const
     165{
     166    return fFile;
     167}
     168
     169
     170void
     171BNotification::SetOnClickFile(const entry_ref* file)
     172{
     173    fFile = (entry_ref*)file;
     174}
     175
     176
     177BList*
     178BNotification::OnClickRefs() const
     179{
     180    return fRefs;
     181}
     182
     183
     184void
     185BNotification::AddOnClickRef(const entry_ref* ref)
     186{
     187    fRefs->AddItem((void*)ref);
     188}
     189
     190
     191BList*
     192BNotification::OnClickArgv() const
     193{
     194    return fArgv;
     195}
     196
     197
     198void
     199BNotification::AddOnClickArg(const char* arg)
     200{
     201    fArgv->AddItem((void*)arg);
     202}
     203
     204
     205BBitmap*
     206BNotification::Icon() const
     207{
     208    return fBitmap;
     209}
     210
     211
     212void
     213BNotification::SetIcon(BBitmap* icon)
     214{
     215    fBitmap = icon;
     216}
  • src/kits/app/Roster.cpp

    diff --git a/src/kits/app/Roster.cpp b/src/kits/app/Roster.cpp
    index d5071ad..2f14cda 100644
    a b  
    2525
    2626#include <AppFileInfo.h>
    2727#include <Application.h>
     28#include <Bitmap.h>
    2829#include <Directory.h>
    2930#include <File.h>
    3031#include <FindDirectory.h>
     
    3536#include <Mime.h>
    3637#include <Node.h>
    3738#include <NodeInfo.h>
     39#include <Notification.h>
     40#include <notification/Notifications.h>
    3841#include <OS.h>
    3942#include <Path.h>
    4043#include <Query.h>
    BRoster::AddToRecentFolders(const entry_ref* folder, const char* appSig) const 
    16501653        DBG(OUT("WARNING: BRoster::AddToRecentDocuments() failed with error 0x%lx\n", err));
    16511654}
    16521655
     1656/*! \brief Sends a notification to the notification_server.
     1657
     1658    The notification is delivered synchronously to the notification_server,
     1659    that will displays it according to its settings and filters.
     1660
     1661    \param notification Notification message.
     1662    \param timeout Seconds after the message fades out.
     1663    \return
     1664    - \c B_OK: Everything went fine.
     1665    - \c B_BAD_PORT_ID: A connection to notification_server could not be
     1666      established or the server is not up and running anymore.
     1667*/
     1668status_t
     1669BRoster::Notify(BNotification* notification, int32 timeout) const
     1670{
     1671    BMessage msg(kNotificationMessage);
     1672    msg.AddInt32("type", (int32)notification->Type());
     1673    msg.AddString("app", notification->Application());
     1674    msg.AddString("title", notification->Title());
     1675    msg.AddString("content", notification->Content());
     1676
     1677    if (notification->MessageID())
     1678        msg.AddString("messageID", notification->MessageID());
     1679
     1680    if (notification->Type() == B_PROGRESS_NOTIFICATION)
     1681        msg.AddFloat("progress", notification->Progress());
     1682
     1683    if (notification->OnClickApp())
     1684        msg.AddString("onClickApp", notification->OnClickApp());
     1685    if (notification->OnClickFile())
     1686        msg.AddRef("onClickFile", notification->OnClickFile());
     1687
     1688    int32 i;
     1689
     1690    BList* refs = notification->OnClickRefs();
     1691    for (i = 0; i < refs->CountItems(); i++)
     1692        msg.AddRef("onClickRef", (entry_ref*)refs->ItemAt(i));
     1693
     1694    BList* argv = notification->OnClickArgv();
     1695    for (i = 0; i < argv->CountItems(); i++)
     1696        msg.AddString("onClickArgv", (const char*)argv->ItemAt(i));
     1697
     1698    BBitmap* icon = notification->Icon();
     1699
     1700    BMessage archive;
     1701    if (icon && icon->Archive(&archive) == B_OK)
     1702        msg.AddMessage("icon", &archive);
     1703
     1704    // Custom time out
     1705    if (timeout > 0)
     1706        msg.AddInt32("timeout", timeout);
     1707
     1708    // Send message
     1709    BMessenger server(kNotificationServerSignature);
     1710    return server.SendMessage(&msg);
     1711}
     1712
    16531713
    16541714//  #pragma mark - Private or reserved
    16551715
  • new file src/kits/notification/AppUsage.cpp

    diff --git a/src/kits/notification/AppUsage.cpp b/src/kits/notification/AppUsage.cpp
    new file mode 100644
    index 0000000..7310796
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include <Message.h>
     15
     16#include <AppUsage.h>
     17#include <NotificationReceived.h>
     18
     19const type_code kTypeCode = 'ipau';
     20
     21
     22AppUsage::AppUsage()
     23    :
     24    fName(""),
     25    fAllow(true)
     26{
     27}
     28
     29
     30AppUsage::AppUsage(entry_ref ref, const char* name, bool allow)
     31    :
     32    fRef(ref),
     33    fName(name),
     34    fAllow(allow)
     35{   
     36}
     37
     38
     39AppUsage::~AppUsage()
     40{
     41    notify_t::iterator nIt;
     42    for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
     43        delete nIt->second;
     44}
     45
     46
     47bool
     48AppUsage::AllowsTypeCode(type_code code) const
     49{
     50    return code == kTypeCode;
     51}
     52
     53
     54status_t
     55AppUsage::Flatten(void* buffer, ssize_t numBytes) const
     56{
     57    BMessage msg;
     58    msg.AddString("app_name", fName);
     59    msg.AddRef("app_ref", &fRef);
     60    msg.AddBool("app_allow", fAllow);
     61
     62    notify_t::const_iterator nIt;
     63    for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
     64        msg.AddFlat("notify", nIt->second);
     65
     66    if (numBytes < msg.FlattenedSize())
     67        return B_ERROR;
     68
     69    return msg.Flatten((char*)buffer, numBytes);
     70}
     71
     72
     73ssize_t
     74AppUsage::FlattenedSize() const
     75{
     76    BMessage msg;
     77    msg.AddString("app_name", fName);
     78    msg.AddRef("app_ref", &fRef);
     79    msg.AddBool("app_allow", fAllow);
     80
     81    notify_t::const_iterator nIt;
     82    for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
     83        msg.AddFlat("notify", nIt->second);
     84
     85    return msg.FlattenedSize();
     86}
     87
     88
     89bool
     90AppUsage::IsFixedSize() const
     91{
     92    return false;
     93}
     94
     95
     96type_code
     97AppUsage::TypeCode() const
     98{
     99    return kTypeCode;
     100}
     101
     102
     103status_t
     104AppUsage::Unflatten(type_code code, const void* buffer,
     105    ssize_t numBytes)
     106{
     107    if (code != kTypeCode)
     108        return B_ERROR;
     109
     110    BMessage msg;
     111    status_t status = B_ERROR;
     112
     113    status = msg.Unflatten((const char*)buffer);
     114
     115    if (status == B_OK) {
     116        msg.FindString("app_name", &fName);
     117        msg.FindRef("app_ref", &fRef);
     118        msg.FindBool("app_allow", &fAllow);
     119
     120        type_code type;
     121        int32 count = 0;
     122
     123        status = msg.GetInfo("notify", &type, &count);
     124        if (status != B_OK)
     125            return status;
     126
     127        for (int32 i = 0; i < count; i++) {
     128            NotificationReceived *notify = new NotificationReceived();
     129            msg.FindFlat("notify", i, notify);
     130            fNotifications[notify->Title()] = notify;
     131        }
     132
     133        status = B_OK;
     134    }
     135   
     136    return status;
     137}
     138
     139                       
     140entry_ref
     141AppUsage::Ref()
     142{
     143    return fRef;
     144}
     145
     146
     147const char*
     148AppUsage::Name()
     149{
     150    return fName.String();
     151}
     152
     153
     154bool
     155AppUsage::Allowed(const char* title, notification_type type)
     156{
     157    bool allowed = fAllow;
     158
     159    if (allowed) {
     160        notify_t::iterator nIt = fNotifications.find(title);
     161        if (nIt == fNotifications.end()) {
     162            allowed = true;     
     163            fNotifications[title] = new NotificationReceived(title, type);
     164        } else {
     165            allowed = nIt->second->Allowed();
     166            nIt->second->UpdateTimeStamp();
     167            nIt->second->SetType(type);
     168        }
     169    }
     170
     171    return allowed;
     172}
     173
     174
     175bool
     176AppUsage::Allowed()
     177{
     178    return fAllow;
     179}
     180
     181
     182NotificationReceived*
     183AppUsage::NotificationAt(int32 index)
     184{
     185    notify_t::iterator nIt = fNotifications.begin();
     186    for (int32 i = 0; i < index; i++)
     187        nIt++;
     188
     189    return nIt->second;
     190}
     191
     192
     193int32
     194AppUsage::Notifications()
     195{
     196    return fNotifications.size();
     197}
     198
     199
     200void
     201AppUsage::AddNotification(NotificationReceived* notification)
     202{
     203    fNotifications[notification->Title()] = notification;
     204}
  • new file src/kits/notification/Jamfile

    diff --git a/src/kits/notification/Jamfile b/src/kits/notification/Jamfile
    new file mode 100644
    index 0000000..6027124
    - +  
     1SubDir HAIKU_TOP src kits notification ;
     2
     3UsePrivateHeaders notification ;
     4
     5StaticLibrary libnotification.a :
     6    AppUsage.cpp
     7    NotificationReceived.cpp
     8    Notifications.cpp
     9;
  • new file src/kits/notification/NotificationReceived.cpp

    diff --git a/src/kits/notification/NotificationReceived.cpp b/src/kits/notification/NotificationReceived.cpp
    new file mode 100644
    index 0000000..f5d9b01
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include <Message.h>
     15#include <Notification.h>
     16#include <NotificationReceived.h>
     17
     18const type_code kTypeCode = 'ipnt';
     19
     20
     21NotificationReceived::NotificationReceived()
     22    :
     23    fTitle(""),
     24    fType(B_INFORMATION_NOTIFICATION),
     25    fEnabled(false),
     26    fLastReceived(time(NULL))
     27{
     28}
     29
     30
     31NotificationReceived::NotificationReceived(const char* title,
     32    notification_type type, bool enabled)
     33    :
     34    fTitle(title),
     35    fType(type),
     36    fEnabled(enabled),
     37    fLastReceived(time(NULL))
     38{
     39}
     40
     41
     42NotificationReceived::~NotificationReceived()
     43{
     44}
     45
     46
     47bool
     48NotificationReceived::AllowsTypeCode(type_code code) const
     49{
     50    return code == kTypeCode;
     51}
     52
     53
     54status_t
     55NotificationReceived::Flatten(void* buffer, ssize_t numBytes) const
     56{
     57    BMessage msg;
     58    msg.AddString("notify_title", fTitle);
     59    msg.AddInt32("notify_type", (int32)fType);
     60    msg.AddInt32("notify_lastreceived", (int32)fLastReceived);
     61    msg.AddBool("notify_enabled", fEnabled);
     62
     63    if (numBytes < msg.FlattenedSize())
     64        return B_ERROR;
     65       
     66    return msg.Flatten((char*)buffer, numBytes);
     67}
     68
     69
     70ssize_t
     71NotificationReceived::FlattenedSize() const
     72{
     73    BMessage msg;
     74    msg.AddString("notify_title", fTitle);
     75    msg.AddInt32("notify_type", (int32)fType);
     76    msg.AddInt32("notify_lastreceived", (int32)fLastReceived);
     77    msg.AddBool("notify_enabled", fEnabled);
     78   
     79    return msg.FlattenedSize();
     80}
     81
     82
     83bool
     84NotificationReceived::IsFixedSize() const
     85{
     86    return false;
     87}
     88
     89
     90type_code
     91NotificationReceived::TypeCode() const
     92{
     93    return kTypeCode;
     94}
     95
     96
     97status_t
     98NotificationReceived::Unflatten(type_code code, const void* buffer,
     99    ssize_t numBytes)
     100{
     101    if (code != kTypeCode)
     102        return B_ERROR;
     103
     104    BMessage msg;
     105    status_t error = msg.Unflatten((const char*)buffer);
     106
     107    if (error == B_OK) {
     108        msg.FindString("notify_title", &fTitle);
     109        msg.FindInt32("notify_type", (int32 *)&fType);
     110        msg.FindInt32("notify_lastreceived", (int32 *)&fLastReceived);
     111        msg.FindBool("notify_enabled", &fEnabled);
     112    }
     113
     114    return error;
     115}
     116
     117
     118const char*
     119NotificationReceived::Title()
     120{
     121    return fTitle.String();
     122}
     123
     124
     125notification_type
     126NotificationReceived::Type()
     127{
     128    return fType;
     129}
     130
     131
     132void
     133NotificationReceived::SetType(notification_type type)
     134{
     135    fType = type;
     136}
     137
     138
     139time_t
     140NotificationReceived::LastReceived()
     141{
     142    return fLastReceived;
     143}
     144
     145
     146bool
     147NotificationReceived::Allowed()
     148{
     149    return fEnabled;
     150}
     151
     152
     153void
     154NotificationReceived::UpdateTimeStamp()
     155{
     156    fLastReceived = time(NULL);
     157}
     158
     159
     160void
     161NotificationReceived::SetTimeStamp(time_t time)
     162{
     163    fLastReceived = time;
     164}
  • src/preferences/Jamfile

    diff --git a/src/preferences/Jamfile b/src/preferences/Jamfile
    index 34253ff..2a42084 100644
    a b SubInclude HAIKU_TOP src preferences mail ; 
    1717SubInclude HAIKU_TOP src preferences media ;
    1818SubInclude HAIKU_TOP src preferences mouse ;
    1919SubInclude HAIKU_TOP src preferences network ;
     20SubInclude HAIKU_TOP src preferences notifications ;
    2021SubInclude HAIKU_TOP src preferences opengl ;
    2122SubInclude HAIKU_TOP src preferences print ;
    2223SubInclude HAIKU_TOP src preferences screen ;
  • new file src/preferences/notifications/DisplayView.cpp

    diff --git a/src/preferences/notifications/DisplayView.cpp b/src/preferences/notifications/DisplayView.cpp
    new file mode 100644
    index 0000000..fca5fca
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <stdio.h>
     11#include <stdlib.h>
     12
     13#include <Alert.h>
     14#include <Directory.h>
     15#include <Message.h>
     16#include <FindDirectory.h>
     17#include <GroupLayout.h>
     18#include <GridLayoutBuilder.h>
     19#include <SpaceLayoutItem.h>
     20#include <TextControl.h>
     21#include <Menu.h>
     22#include <MenuItem.h>
     23#include <MenuField.h>
     24#include <Mime.h>
     25#include <Node.h>
     26#include <notification/Notifications.h>
     27#include <Path.h>
     28
     29#include "DisplayView.h"
     30#include "SettingsHost.h"
     31
     32#define _T(str) (str)
     33
     34
     35DisplayView::DisplayView(SettingsHost* host)
     36    :
     37    SettingsPane("display", host)
     38{
     39    // Window width
     40    fWindowWidth = new BTextControl(_T("Window width:"), NULL,
     41        new BMessage(kSettingChanged));
     42
     43    // Icon size
     44    fIconSize = new BMenu("iconSize");
     45    fIconSize->AddItem(new BMenuItem(_T("Mini icon"),
     46        new BMessage(kSettingChanged)));
     47    fIconSize->AddItem(new BMenuItem(_T("Large icon"),
     48        new BMessage(kSettingChanged)));
     49    fIconSize->SetLabelFromMarked(true);
     50    fIconSizeField = new BMenuField(_T("Icon size:"), fIconSize);
     51
     52    // Title position
     53    fTitlePosition = new BMenu("titlePosition");
     54    fTitlePosition->AddItem(new BMenuItem(_T("Above icon"),
     55        new BMessage(kSettingChanged)));
     56    fTitlePosition->AddItem(new BMenuItem(_T("Right of icon"),
     57        new BMessage(kSettingChanged)));
     58    fTitlePosition->SetLabelFromMarked(true);
     59    fTitlePositionField = new BMenuField(_T("Title position:"), fTitlePosition);
     60
     61    // Load settings
     62    Load();
     63
     64    // Calculate inset
     65    float inset = ceilf(be_plain_font->Size() * 0.7f);
     66
     67    SetLayout(new BGroupLayout(B_VERTICAL));
     68    AddChild(BGridLayoutBuilder(inset, inset)
     69        .Add(fWindowWidth->CreateLabelLayoutItem(), 0, 0)
     70        .Add(fWindowWidth->CreateTextViewLayoutItem(), 1, 0)
     71        .Add(fIconSizeField->CreateLabelLayoutItem(), 0, 1)
     72        .Add(fIconSizeField->CreateMenuBarLayoutItem(), 1, 1)
     73        .Add(fTitlePositionField->CreateLabelLayoutItem(), 0, 2)
     74        .Add(fTitlePositionField->CreateMenuBarLayoutItem(), 1, 2)
     75        .Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1)
     76    );
     77}
     78
     79
     80void
     81DisplayView::AttachedToWindow()
     82{
     83    fWindowWidth->SetTarget(this);
     84    fIconSize->SetTargetForItems(this);
     85    fTitlePosition->SetTargetForItems(this);
     86}
     87
     88
     89void
     90DisplayView::MessageReceived(BMessage* msg)
     91{
     92    switch (msg->what) {
     93        case kSettingChanged:
     94            SettingsPane::MessageReceived(msg);
     95            break;
     96        default:
     97            BView::MessageReceived(msg);
     98    }
     99}
     100
     101
     102status_t
     103DisplayView::Load()
     104{
     105    BPath path;
     106
     107    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     108        return B_ERROR;
     109
     110    path.Append(kSettingsDirectory);
     111
     112    if (create_directory(path.Path(), 0755) != B_OK) {
     113        BAlert* alert = new BAlert("",
     114            _T("There was a problem saving the preferences.\n"
     115                "It's possible you don't have write access to the "
     116                "settings directory."), "OK", NULL, NULL,
     117            B_WIDTH_AS_USUAL, B_STOP_ALERT);
     118        (void)alert->Go();
     119    }
     120
     121    path.Append(kDisplaySettings);
     122
     123    BFile file(path.Path(), B_READ_ONLY);
     124    BMessage settings;
     125    settings.Unflatten(&file);
     126
     127    char buffer[255];
     128    int32 setting;
     129    BMenuItem* item = NULL;
     130
     131    float width;
     132    if (settings.FindFloat(kWidthName, &width) != B_OK)
     133        width = kDefaultWidth;
     134    (void)sprintf(buffer, "%.2f", width);
     135    fWindowWidth->SetText(buffer);
     136
     137    icon_size iconSize;
     138    if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
     139        iconSize = kDefaultIconSize;
     140    else
     141        iconSize = (icon_size)setting;
     142    if (iconSize == B_MINI_ICON)
     143        item = fIconSize->ItemAt(0);
     144    else
     145        item = fIconSize->ItemAt(1);
     146    if (item)
     147        item->SetMarked(true);
     148
     149    infoview_layout layout;
     150    if (settings.FindInt32(kLayoutName, &setting) != B_OK)
     151        layout = kDefaultLayout;
     152    else {
     153        switch (setting) {
     154            case 0:
     155                layout = TitleAboveIcon;
     156                break;
     157            case 1:
     158                layout = AllTextRightOfIcon;
     159                break;
     160            default:
     161                layout = kDefaultLayout;
     162        }
     163    }
     164    item = fTitlePosition->ItemAt(layout);
     165    if (item)
     166        item->SetMarked(true);
     167
     168    return B_OK;
     169}
     170
     171
     172status_t
     173DisplayView::Save()
     174{
     175    BPath path;
     176
     177    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     178        return B_ERROR;
     179
     180    path.Append(kSettingsDirectory);
     181    path.Append(kDisplaySettings);
     182
     183    BMessage settings;
     184
     185    float width = atof(fWindowWidth->Text());
     186    settings.AddFloat(kWidthName, width);
     187
     188    icon_size iconSize = kDefaultIconSize;
     189    switch (fIconSize->IndexOf(fIconSize->FindMarked())) {
     190        case 0:
     191            iconSize = B_MINI_ICON;
     192            break;
     193        default:
     194            iconSize = B_LARGE_ICON;
     195    }
     196    settings.AddInt32(kIconSizeName, (int32)iconSize);
     197
     198    int32 layout = fTitlePosition->IndexOf(fTitlePosition->FindMarked());
     199    if (layout == B_ERROR)
     200        layout = (int32)kDefaultLayout;
     201    settings.AddInt32(kLayoutName, layout);
     202
     203    // Save settings file
     204    BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
     205    status_t ret = settings.Flatten(&file);
     206    if (ret != B_OK) {
     207        BAlert* alert = new BAlert("",
     208            _T("Can't save preferenes, you probably don't have write "
     209                "access to the settings directory or the disk is full."), "OK", NULL, NULL,
     210                B_WIDTH_AS_USUAL, B_STOP_ALERT);
     211        (void)alert->Go();
     212        return ret;
     213    }
     214
     215    return B_OK;
     216}
     217
     218
     219status_t
     220DisplayView::Revert()
     221{
     222    return B_ERROR;
     223}
  • new file src/preferences/notifications/DisplayView.h

    diff --git a/src/preferences/notifications/DisplayView.h b/src/preferences/notifications/DisplayView.h
    new file mode 100644
    index 0000000..ddf528c
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _DISPLAY_VIEW_H
     7#define _DISPLAY_VIEW_H
     8
     9#include "SettingsPane.h"
     10
     11class BTextControl;
     12class BMenu;
     13class BMenuField;
     14
     15class DisplayView : public SettingsPane {
     16public:
     17                            DisplayView(SettingsHost* host);
     18
     19    virtual void            AttachedToWindow();
     20    virtual void            MessageReceived(BMessage* msg);
     21
     22            // SettingsPane hooks
     23            status_t        Load();
     24            status_t        Save();
     25            status_t        Revert();
     26
     27private:
     28            BTextControl*   fWindowWidth;
     29            BMenu*          fIconSize;
     30            BMenuField*     fIconSizeField;
     31            BMenu*          fTitlePosition;
     32            BMenuField*     fTitlePositionField;
     33
     34};
     35
     36#endif // _DISPLAY_VIEW_H
  • new file src/preferences/notifications/GeneralView.cpp

    diff --git a/src/preferences/notifications/GeneralView.cpp b/src/preferences/notifications/GeneralView.cpp
    new file mode 100644
    index 0000000..e8de047
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <stdio.h>
     11#include <stdlib.h>
     12
     13#include <vector>
     14
     15#include <Roster.h>
     16#include <GroupLayout.h>
     17#include <GroupLayoutBuilder.h>
     18#include <Alert.h>
     19#include <Font.h>
     20#include <Button.h>
     21#include <StringView.h>
     22#include <TextControl.h>
     23#include <CheckBox.h>
     24#include <String.h>
     25#include <FindDirectory.h>
     26#include <Node.h>
     27#include <notification/Notifications.h>
     28#include <Path.h>
     29#include <File.h>
     30#include <Directory.h>
     31#include <VolumeRoster.h>
     32#include <Volume.h>
     33#include <Query.h>
     34#include <SymLink.h>
     35
     36#include "GeneralView.h"
     37#include "SettingsHost.h"
     38
     39#define _T(str) (str)
     40
     41const int32 kServer = '_TSR';
     42
     43const char* kStartServer = _T("Enable notifications");
     44const char* kStopServer = _T("Disable notifications");
     45const char* kStarted = _T("Events are notified");
     46const char* kStopped = _T("Events are not notified");
     47
     48
     49GeneralView::GeneralView(SettingsHost* host)
     50    :
     51    SettingsPane("general", host)
     52{
     53    BFont statusFont;
     54
     55    // Set a smaller font for the status label
     56    statusFont.SetSize(be_plain_font->Size() * 0.8);
     57
     58    // Status button and label
     59    fServerButton = new BButton("server", kStartServer, new BMessage(kServer));
     60    fStatusLabel = new BStringView("status", kStopped);
     61    fStatusLabel->SetFont(&statusFont);
     62
     63    // Update status label and server button
     64    if (_IsServerRunning()) {
     65        fServerButton->SetLabel(kStopServer);
     66        fStatusLabel->SetText(kStarted);
     67    } else {
     68        fServerButton->SetLabel(kStartServer);
     69        fStatusLabel->SetText(kStopped);
     70    }
     71
     72    // Autostart
     73    fAutoStart = new BCheckBox("autostart",
     74        _T("Enable notifications at startup"), new BMessage(kSettingChanged));
     75
     76    // Display time
     77    fTimeout = new BTextControl(_T("Hide notifications from screen after"), NULL,
     78        new BMessage(kSettingChanged));
     79    BStringView* displayTimeLabel = new BStringView("dt_label",
     80        _T("seconds of inactivity"));
     81
     82    // Default position
     83    // TODO: Here will come a screen representation with the four corners clickable
     84
     85    // Load settings
     86    Load();
     87
     88    // Calculate inset
     89    float inset = ceilf(be_plain_font->Size() * 0.7f);
     90
     91    SetLayout(new BGroupLayout(B_VERTICAL));
     92    AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
     93        .AddGroup(B_HORIZONTAL, inset)
     94            .Add(fServerButton)
     95            .Add(fStatusLabel)
     96            .AddGlue()
     97        .End()
     98
     99        .AddGroup(B_VERTICAL, inset)
     100            .Add(fAutoStart)
     101            .AddGroup(B_HORIZONTAL)
     102                .AddGroup(B_HORIZONTAL, 2)
     103                    .Add(fTimeout)
     104                    .Add(displayTimeLabel)
     105                .End()
     106            .End()
     107        .End()
     108
     109        .AddGlue()
     110    );
     111}
     112
     113
     114void
     115GeneralView::AttachedToWindow()
     116{
     117    fServerButton->SetTarget(this);
     118    fAutoStart->SetTarget(this);
     119    fTimeout->SetTarget(this);
     120}
     121
     122
     123void
     124GeneralView::MessageReceived(BMessage* msg)
     125{
     126    switch (msg->what) {
     127        case kServer: {
     128                entry_ref ref;
     129
     130                // Check if server is available
     131                if (!_CanFindServer(&ref)) {
     132                    BAlert* alert = new BAlert(_T("Notifications"),
     133                        _T("The notifications server cannot be found, "
     134                           "this means your InfoPopper installation was not "
     135                           "successfully completed."), _T("OK"), NULL,
     136                        NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     137                    (void)alert->Go();
     138                    return;
     139                }
     140
     141                if (_IsServerRunning()) {
     142                    // Server team
     143                    team_id team = be_roster->TeamFor(kNotificationServerSignature);
     144
     145                    // Establish a connection to infopopper_server
     146                    status_t ret = B_ERROR;
     147                    BMessenger messenger(kNotificationServerSignature, team, &ret);
     148                    if (ret != B_OK) {
     149                        BAlert* alert = new BAlert(_T("Notifications"),
     150                            _T("Notifications cannot be stopped, because "
     151                               "the server can't be reached."),
     152                            _T("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     153                        (void)alert->Go();
     154                        return;
     155                    }
     156
     157                    // Send quit message
     158                    if (messenger.SendMessage(new BMessage(B_QUIT_REQUESTED)) != B_OK) {
     159                        BAlert* alert = new BAlert(_T("Notifications"),
     160                            _T("Cannot disable notifications because the server "
     161                               "can't be reached."), _T("OK"), NULL,
     162                            NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     163                        (void)alert->Go();
     164                        return;
     165                    }
     166
     167                    fServerButton->SetLabel(kStartServer);
     168                    fStatusLabel->SetText(kStopped);
     169                } else {
     170                    // Start server
     171                    status_t err = be_roster->Launch(kNotificationServerSignature);
     172                    if (err != B_OK) {
     173                        BAlert* alert = new BAlert(_T("Notifications"),
     174                            _T("Cannot enable notifications because the server "
     175                               "cannot be found.\nThis means your InfoPopper "
     176                               "installation was not successfully completed."),
     177                            _T("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     178                        (void)alert->Go();
     179                        return;
     180                    }
     181
     182                    fServerButton->SetLabel(kStopServer);
     183                    fStatusLabel->SetText(kStarted);
     184                }
     185            } break;
     186        case kSettingChanged:
     187            SettingsPane::MessageReceived(msg);
     188            break;
     189        default:
     190            BView::MessageReceived(msg);
     191            break;
     192    }
     193}
     194
     195
     196status_t
     197GeneralView::Load()
     198{
     199    BPath path;
     200
     201    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     202        return B_ERROR;
     203
     204    path.Append(kSettingsDirectory);
     205
     206    if (create_directory(path.Path(), 0755) != B_OK) {
     207        BAlert* alert = new BAlert("",
     208            _T("There was a problem saving the preferences.\n"
     209                "It's possible you don't have write access to the "
     210                "settings directory."), "OK", NULL, NULL,
     211            B_WIDTH_AS_USUAL, B_STOP_ALERT);
     212        (void)alert->Go();
     213    }
     214
     215    path.Append(kGeneralSettings);
     216
     217    BMessage settings;
     218    BFile file(path.Path(), B_READ_ONLY);
     219    settings.Unflatten(&file);
     220
     221    char buffer[255];
     222
     223    bool autoStart;
     224    if (settings.FindBool(kAutoStartName, &autoStart) != B_OK)
     225        autoStart = kDefaultAutoStart;
     226    fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
     227
     228    int32 timeout;
     229    if (settings.FindInt32(kTimeoutName, &timeout) != B_OK)
     230        timeout = kDefaultTimeout;
     231    (void)sprintf(buffer, "%ld", timeout);
     232    fTimeout->SetText(buffer);
     233
     234    return B_OK;
     235}
     236
     237
     238status_t
     239GeneralView::Save()
     240{
     241    BPath path;
     242
     243    status_t ret = B_OK;
     244    ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
     245    if (ret != B_OK)
     246        return ret;
     247
     248    path.Append(kSettingsDirectory);
     249    path.Append(kGeneralSettings);
     250
     251    BMessage settings;
     252
     253    bool autoStart = (fAutoStart->Value() == B_CONTROL_ON);
     254    settings.AddBool(kAutoStartName, autoStart);
     255
     256    int32 timeout = atol(fTimeout->Text());
     257    settings.AddInt32(kTimeoutName, timeout);
     258
     259    // Save settings file
     260    BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
     261    ret = settings.Flatten(&file);
     262    if (ret != B_OK) {
     263        BAlert* alert = new BAlert("",
     264            _T("An error is occurred saving the preferences.\n"
     265                "It's possible you are running out of disk space."),
     266            "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     267        (void)alert->Go();
     268        return ret;
     269    }
     270
     271    // Find server path
     272    entry_ref ref;
     273    if (!_CanFindServer(&ref)) {
     274        BAlert* alert = new BAlert("",
     275            _T("The notifications server cannot be found.\n"
     276               "A possible cause is an installation not done correctly"),
     277            "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
     278        (void)alert->Go();
     279        return B_ERROR;
     280    }
     281
     282    // UserBootscript command
     283    BPath serverPath(&ref);
     284
     285    // Start server at boot time
     286    ret = find_directory(B_USER_BOOT_DIRECTORY, &path, true);
     287    if (ret != B_OK) {
     288        BAlert* alert = new BAlert("",
     289            _T("Can't save preferences, you probably don't have write "
     290               "access to the boot settings directory."), "OK", NULL, NULL,
     291            B_WIDTH_AS_USUAL, B_STOP_ALERT);
     292        (void)alert->Go();
     293        return ret;
     294    }
     295
     296    path.Append("launch");
     297    BDirectory directory(path.Path());
     298    BEntry entry(&directory, serverPath.Leaf());
     299
     300    // Remove symbolic link
     301    entry.Remove();
     302
     303    if (autoStart) {
     304        // Put a symlink into ~/config/boot/launch
     305        if ((ret = directory.CreateSymLink(serverPath.Leaf(),
     306            serverPath.Path(), NULL) != B_OK)) {
     307            BAlert* alert = new BAlert("",
     308                _T("Can't enable notifications at startup time, you probably don't have "
     309                   "write permission to the boot settings directory."), "OK", NULL, NULL,
     310                B_WIDTH_AS_USUAL, B_STOP_ALERT);
     311            (void)alert->Go();
     312            return ret;
     313        }
     314    }
     315
     316    return B_OK;
     317}
     318
     319
     320status_t
     321GeneralView::Revert()
     322{
     323    return B_ERROR;
     324}
     325
     326
     327bool
     328GeneralView::_CanFindServer(entry_ref* ref)
     329{
     330    // Try searching with be_roster
     331    if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK)
     332        return true;
     333
     334    // Try with a query and take the first result
     335    BVolumeRoster vroster;
     336    BVolume volume;
     337    char volName[B_FILE_NAME_LENGTH];
     338
     339    vroster.Rewind();
     340
     341    while (vroster.GetNextVolume(&volume) == B_OK) {
     342        if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery())
     343            continue;
     344
     345        volume.GetName(volName);
     346
     347        BQuery *query = new BQuery();
     348        query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")");
     349        query->SetVolume(&volume);
     350        query->Fetch();
     351
     352        if (query->GetNextRef(ref) == B_OK)
     353            return true;
     354    }
     355
     356    return false;
     357}
     358
     359
     360bool
     361GeneralView::_IsServerRunning()
     362{
     363    return be_roster->IsRunning(kNotificationServerSignature);
     364}
  • new file src/preferences/notifications/GeneralView.h

    diff --git a/src/preferences/notifications/GeneralView.h b/src/preferences/notifications/GeneralView.h
    new file mode 100644
    index 0000000..12c5f40
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _GENERAL_VIEW_H
     7#define _GENERAL_VIEW_H
     8
     9#include "SettingsPane.h"
     10
     11class BCheckBox;
     12class BButton;
     13class BStringView;
     14class BTextControl;
     15
     16class GeneralView : public SettingsPane {
     17public:
     18                            GeneralView(SettingsHost* host);
     19
     20    virtual void            AttachedToWindow();
     21    virtual void            MessageReceived(BMessage* msg);
     22
     23            // SettingsPane hooks
     24            status_t        Load();
     25            status_t        Save();
     26            status_t        Revert();
     27
     28private:
     29        BButton*            fServerButton;
     30        BStringView*        fStatusLabel;
     31        BCheckBox*          fAutoStart;
     32        BTextControl*       fTimeout;
     33        BCheckBox*          fHideAll;
     34
     35        bool                _CanFindServer(entry_ref* ref);
     36        bool                _IsServerRunning();
     37};
     38
     39#endif // _GENERAL_VIEW_H
  • new file src/preferences/notifications/IconItem.cpp

    diff --git a/src/preferences/notifications/IconItem.cpp b/src/preferences/notifications/IconItem.cpp
    new file mode 100644
    index 0000000..58f68a6
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <stdio.h>
     11
     12#include <Bitmap.h>
     13
     14#include "IconItem.h"
     15#include "IconRule.h"
     16
     17const int32 kEdgeOffset = 4;
     18
     19
     20BIconItem::BIconItem(BView* owner, const char* label, BBitmap* icon)
     21    :
     22    fIcon(icon),
     23    fLabel(label),
     24    fSelected(false),
     25    fOwner(owner)
     26{
     27}
     28
     29
     30BIconItem::~BIconItem()
     31{
     32    delete fIcon;
     33}
     34
     35void BIconItem::Draw()
     36{
     37    if (IsSelected()) {
     38        rgb_color color;
     39        rgb_color origHigh;
     40
     41        origHigh = fOwner->HighColor();
     42
     43        if (IsSelected())
     44            color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
     45        else
     46            color = fOwner->ViewColor();
     47
     48        fOwner->SetHighColor(color);
     49        fOwner->FillRect(fFrame);
     50        fOwner->SetHighColor(origHigh);
     51    }
     52
     53    if (fIcon)
     54    {
     55        fOwner->SetDrawingMode(B_OP_ALPHA);
     56        fOwner->DrawBitmap(fIcon, BPoint(fFrame.top + kEdgeOffset,
     57            fFrame.left + kEdgeOffset));
     58        fOwner->SetDrawingMode(B_OP_COPY);
     59    }
     60
     61    if (IsSelected())
     62        fOwner->SetDrawingMode(B_OP_OVER);
     63
     64#if 0
     65    fOwner->MovePenTo(frame.left + kEdgeOffset + fIconWidth + kEdgeOffset,
     66        frame.bottom - fFontOffset);
     67#else
     68    fOwner->MovePenTo(fFrame.left, fFrame.bottom - 10);
     69#endif
     70    fOwner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
     71    fOwner->DrawString(" ");
     72    fOwner->DrawString(fLabel.String());
     73}
     74
     75
     76const char*
     77BIconItem::Label() const
     78{
     79    return fLabel.String();
     80}
     81
     82
     83void
     84BIconItem::SetFrame(BRect frame)
     85{
     86    fFrame = frame;
     87}
     88
     89
     90BRect
     91BIconItem::Frame() const
     92{
     93    return fFrame;
     94}
     95
     96
     97void
     98BIconItem::Select()
     99{
     100    fSelected = true;
     101    Draw();
     102}
     103
     104
     105void
     106BIconItem::Deselect()
     107{
     108    fSelected = false;
     109    Draw();
     110}
     111
     112
     113bool
     114BIconItem::IsSelected() const
     115{
     116    return fSelected;
     117}
  • new file src/preferences/notifications/IconItem.h

    diff --git a/src/preferences/notifications/IconItem.h b/src/preferences/notifications/IconItem.h
    new file mode 100644
    index 0000000..e5feba0
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _ICON_ITEM_H
     7#define _ICON_ITEM_H
     8
     9#include <View.h>
     10#include <String.h>
     11
     12class BBitmap;
     13
     14class BIconItem {
     15public:
     16                            BIconItem(BView* owner, const char* label, BBitmap* icon);
     17    virtual                 ~BIconItem();
     18
     19            void            Draw();
     20
     21            const char*     Label() const;
     22
     23            void            SetFrame(BRect frame);
     24            BRect           Frame() const;
     25
     26            void            Select();
     27            void            Deselect();
     28            bool            IsSelected() const;
     29
     30private:
     31            BString         fLabel;
     32            BBitmap*        fIcon;
     33            bool            fSelected;
     34            BView*          fOwner;
     35            BRect           fFrame;
     36};
     37
     38#endif // _ICON_ITEM_H
  • new file src/preferences/notifications/IconRule.cpp

    diff --git a/src/preferences/notifications/IconRule.cpp b/src/preferences/notifications/IconRule.cpp
    new file mode 100644
    index 0000000..f8912b3
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <stdio.h>
     11
     12#include <Message.h>
     13#include <Bitmap.h>
     14#include <Window.h>
     15#include <List.h>
     16
     17#include "IconRule.h"
     18#include "IconItem.h"
     19
     20const int32 kEdgeOffset = 8;
     21const int32 kBorderOffset = 1;
     22
     23// TODO: Do we need to inherit from BControl?
     24
     25BIconRule::BIconRule(const char* name)
     26    :
     27    BView(name, B_WILL_DRAW),
     28    fSelIndex(-1)
     29{
     30    fIcons = new BList();
     31}
     32
     33
     34BIconRule::~BIconRule()
     35{
     36    delete fIcons;
     37}
     38
     39
     40status_t
     41BIconRule::Invoke(BMessage* message)
     42{
     43    bool notify = false;
     44    uint32 kind = InvokeKind(&notify);
     45
     46    BMessage clone(kind);
     47    status_t err = B_BAD_VALUE;
     48
     49    if (!message && !notify)
     50        message = Message();
     51
     52    if (!message) {
     53        if (!IsWatched())
     54            return err;
     55    } else
     56        clone = *message;
     57
     58    clone.AddInt64("when", (int64)system_time());
     59    clone.AddPointer("source", this);
     60    clone.AddMessenger("be:sender", BMessenger(this));
     61    clone.AddInt32("index", fSelIndex);
     62
     63    if (message)
     64        err = BInvoker::Invoke(&clone);
     65
     66    SendNotices(kind, &clone);
     67
     68    return err;
     69}
     70
     71
     72void
     73BIconRule::AttachedToWindow()
     74{
     75    BView::AttachedToWindow();
     76
     77    if (!Messenger().IsValid())
     78        SetTarget(Window(), NULL);
     79}
     80
     81
     82void
     83BIconRule::MouseDown(BPoint point)
     84{
     85    if (!IsFocus()) {
     86        MakeFocus();
     87        Sync();
     88        Window()->UpdateIfNeeded();
     89    }
     90
     91    int32 index = IndexOf(point);
     92    if (index > -1)
     93        SlideToIcon(index);
     94}
     95
     96
     97void
     98BIconRule::Draw(BRect updateRect)
     99{
     100    int32 count = CountIcons();
     101    if (count == 0)
     102        return;
     103
     104    rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
     105    rgb_color lightColor = tint_color(panelColor, B_DARKEN_1_TINT);
     106    rgb_color darkColor = tint_color(lightColor, B_DARKEN_2_TINT);
     107
     108    SetHighColor(darkColor);
     109    StrokeLine(Bounds().LeftTop(), Bounds().RightTop());
     110    StrokeLine(Bounds().LeftTop(), Bounds().LeftBottom());
     111
     112    SetHighColor(lightColor);
     113    StrokeLine(Bounds().LeftBottom(), Bounds().RightBottom());
     114    StrokeLine(Bounds().RightTop(), Bounds().RightBottom());
     115
     116    BRect itemFrame(kEdgeOffset, kBorderOffset, -1, kBorderOffset + 64);
     117    for (int32 i = 0; i < count; i++) {
     118        BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(i));
     119        float width = StringWidth(item->Label()) + StringWidth(" ") * 2;
     120        if (width < 64.0f)
     121            width = 64.0f;
     122        itemFrame.right = itemFrame.left + width - 1;
     123
     124        if (itemFrame.Intersects(updateRect)) {
     125            item->SetFrame(itemFrame);
     126            item->Draw();
     127        }
     128
     129        itemFrame.left = itemFrame.right + kEdgeOffset + 1;
     130    }
     131}
     132
     133
     134BMessage*
     135BIconRule::SelectionMessage() const
     136{
     137    return fMessage;
     138}
     139
     140
     141void
     142BIconRule::SetSelectionMessage(BMessage* message)
     143{
     144    delete fMessage;
     145    fMessage = message;
     146}
     147
     148
     149int32
     150BIconRule::AddIcon(const char* label, const BBitmap* icon)
     151{
     152    BIconItem* item = new BIconItem(this, label, (BBitmap*)icon);
     153    if (CountIcons() == 0) {
     154        item->Select();
     155        fSelIndex = 0;
     156    }
     157    (void)fIcons->AddItem(item);
     158}
     159
     160
     161void
     162BIconRule::RemoveIconAt(int32 index)
     163{
     164}
     165
     166
     167void
     168BIconRule::RemoveAllIcons()
     169{
     170}
     171
     172
     173int32
     174BIconRule::CountIcons() const
     175{
     176    return fIcons->CountItems();
     177}
     178
     179
     180void
     181BIconRule::SlideToIcon(int32 index)
     182{
     183    // Ignore invalid items
     184    if ((index < 0) || (index > CountIcons() - 1))
     185        return;
     186
     187    BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(index));
     188    if (item) {
     189        // Deselect previously selected item
     190        if (fSelIndex > -1) {
     191            BIconItem* selItem = static_cast<BIconItem*>(fIcons->ItemAt(fSelIndex));
     192            selItem->Deselect();
     193        }
     194
     195        // Select this item
     196        item->Select();
     197        fSelIndex = index;
     198        Invalidate();
     199
     200        // Invoke notification
     201        InvokeNotify(fMessage, B_CONTROL_MODIFIED);
     202    }
     203}
     204
     205
     206void
     207BIconRule::SlideToNext()
     208{
     209    if (fSelIndex + 1 < CountIcons() - 1)
     210        return;
     211    SlideToIcon(fSelIndex + 1);
     212}
     213
     214
     215void
     216BIconRule::SlideToPrevious()
     217{
     218    if (fSelIndex <= 0)
     219        return;
     220    SlideToIcon(fSelIndex - 1);
     221}
     222
     223
     224int32
     225BIconRule::IndexOf(BPoint point)
     226{
     227    int32 low = 0;
     228    int32 high = fIcons->CountItems() - 1;
     229    int32 mid = -1;
     230    float frameLeft = -1.0f;
     231    float frameRight = 1.0f;
     232
     233    // Binary search the list
     234    while (high >= low) {
     235        mid = (low + high) / 2;
     236        BIconItem* item = static_cast<BIconItem*>(fIcons->ItemAt(mid));
     237        frameLeft = item->Frame().left;
     238        frameRight = item->Frame().right;
     239        if (point.x < frameLeft)
     240            high = mid - 1;
     241        else if (point.x > frameRight)
     242            low = mid + 1;
     243        else
     244            return mid;
     245    }
     246
     247    return -1;
     248}
     249
     250BSize
     251BIconRule::MinSize()
     252{
     253    BSize minSize(BView::MinSize());
     254    minSize.height = 64 + (kBorderOffset * 2);
     255    return minSize;
     256}
     257
     258
     259BSize
     260BIconRule::MaxSize()
     261{
     262    return BView::MaxSize();
     263}
     264
     265
     266BSize
     267BIconRule::PreferredSize()
     268{
     269    return BView::PreferredSize();
     270}
  • new file src/preferences/notifications/IconRule.h

    diff --git a/src/preferences/notifications/IconRule.h b/src/preferences/notifications/IconRule.h
    new file mode 100644
    index 0000000..854bbc1
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _ICON_RULE_H
     7#define _ICON_RULE_H
     8
     9#include <Invoker.h>
     10#include <View.h>
     11
     12class BList;
     13class BMessage;
     14
     15class BIconRule : public BView, public BInvoker {
     16public:
     17                        BIconRule(const char* name);
     18    virtual             ~BIconRule();
     19
     20            status_t    Invoke(BMessage* message);
     21
     22    virtual void        AttachedToWindow();
     23
     24    virtual void        MouseDown(BPoint point);
     25    virtual void        Draw(BRect updateRect);
     26
     27            BMessage*   SelectionMessage() const;
     28            void        SetSelectionMessage(BMessage* message);
     29
     30            int32       AddIcon(const char* label, const BBitmap* icon);
     31            void        RemoveIconAt(int32 index);
     32            void        RemoveAllIcons();
     33
     34            int32       CountIcons() const;
     35
     36            void        SlideToIcon(int32 index);
     37            void        SlideToNext();
     38            void        SlideToPrevious();
     39
     40            int32       IndexOf(BPoint point);
     41
     42    virtual BSize       MinSize();
     43    virtual BSize       MaxSize();
     44    virtual BSize       PreferredSize();
     45
     46private:
     47            BList*      fIcons;
     48            int32       fSelIndex;
     49            BMessage*   fMessage;
     50};
     51
     52#endif // _ICON_RULE_H
  • new file src/preferences/notifications/Jamfile

    diff --git a/src/preferences/notifications/Jamfile b/src/preferences/notifications/Jamfile
    new file mode 100644
    index 0000000..48ad097
    - +  
     1SubDir HAIKU_TOP src preferences notifications ;
     2
     3UsePrivateHeaders interface ;
     4
     5Application Notifications :
     6    Notifications.cpp
     7    PrefletWin.cpp
     8    PrefletView.cpp
     9    SettingsPane.cpp
     10    GeneralView.cpp
     11    DisplayView.cpp
     12    NotificationsView.cpp
     13    IconRule.cpp
     14    IconItem.cpp
     15    : be translation libcolumnlistview.a libnotification.a $(TARGET_LIBSTDC++)
     16    : Notifications.rdef
     17;
     18
     19Depends Notifications : libcolumnlistview.a ;
     20Depends Notifications : libnotification.a ;
  • new file src/preferences/notifications/Notifications.cpp

    diff --git a/src/preferences/notifications/Notifications.cpp b/src/preferences/notifications/Notifications.cpp
    new file mode 100644
    index 0000000..208e72a
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <Application.h>
     11
     12#include "PrefletWin.h"
     13
     14class PrefletApp : public BApplication {
     15public:
     16                        PrefletApp();
     17
     18    virtual void        ReadyToRun();
     19    virtual bool        QuitRequested();
     20
     21private:
     22            PrefletWin* fWindow;
     23};
     24
     25
     26PrefletApp::PrefletApp()
     27    :
     28    BApplication("application/x-vnd.Haiku-Notifications")
     29{
     30}
     31
     32
     33void
     34PrefletApp::ReadyToRun()
     35{
     36    fWindow = new PrefletWin;
     37}
     38
     39
     40bool
     41PrefletApp::QuitRequested()
     42{
     43    return true;
     44}
     45
     46
     47int
     48main(int argc, char* argv[])
     49{
     50    PrefletApp app;
     51    app.Run();
     52    return 0;
     53}
  • new file src/preferences/notifications/Notifications.rdef

    diff --git a/src/preferences/notifications/Notifications.rdef b/src/preferences/notifications/Notifications.rdef
    new file mode 100644
    index 0000000..3168bbb
    - +  
     1
     2resource app_signature "application/x-vnd.Haiku-Notifications";
     3
     4resource app_version {
     5    major  = 1,
     6    middle = 0,
     7    minor  = 0,
     8
     9    variety = B_APPV_ALPHA,
     10    internal = 0,
     11
     12    short_info = "Notifications",
     13    long_info = "Notifications ©2009 Pier Luigi Fiorini ©2010 Haiku"
     14};
     15
     16resource app_flags B_SINGLE_LAUNCH;
     17
     18resource vector_icon {
     19    $"6E6369661201B0AEB6330200160734E2E73823CEB823CE34E2E74AE8E44C3C6C"
     20    $"000020054715732EA251D37EE38F03B89B9B0200160734000B36F73FB6F73F34"
     21    $"000B4AF7984C1AC8000020054715732EA251D37EE38F039F9FAA0200160730F6"
     22    $"C6343295B4329530F6C64B14D64C04C5000020054715732EA251D37EE38F0200"
     23    $"16073104ED343D08B43D083104ED4B14734C02F8000020054715732EA251D37E"
     24    $"E38F039F9FBD020116023C322F0000000000003C322F4B471E4AA0B10000FF8F"
     25    $"0500020116073C263B0000000000003C263B4AA2864A01D4000020054715732E"
     26    $"A251D37EE38F039997AE0312031903B0AEB603292F3D0200160736FA5E3A3533"
     27    $"BA353336FA5E4945B24A4CD9000020054715732EA251D37EE38F039B9C9F0400"
     28    $"19140204CAC5CED7CAC5CE14CAC5CF99BF00D037CB4CD062B85FD02020CED720"
     29    $"CF9920CE14BF00CD76B85FCD76C5A0CD76060B7FF413CACFCF60CACFCF60CACF"
     30    $"CF5FCAD0CF5CCAD0CF5DCAD0CF5ACACFCF57CACFCF58CACFCF57CE42C3A0CF66"
     31    $"C3A2CF66C3A2CF66C3B8CFDAC738D037C54AD037C926D037CACECF66CAB8CFDA"
     32    $"CACECF66CACFCF600204C738CD66C535CD66C93BCD66CAD0CE42CAD0CDC7CAD0"
     33    $"CEBDC738CF1DC93BCF1DC535CF1DC3A0CE42C3A0CEBDC3A0CDC7060847FFC914"
     34    $"CDEAC914CDECC914CDEACCCCC514CDEBC515CDEBC515CDEBC515CDEDC514CDF1"
     35    $"C514CDEFC514CE31C714CE65C5F9CE65C830CE65C915CDF1C915CE31C915CDEF"
     36    $"0204C714CC50C5F5CC50C833CC50C915CCC4C915CC83C915CD05C714CD39C833"
     37    $"CD39C5F5CD39C514CCC4C514CD05C514CC830204C714CC51C668CC51C7C0CC51"
     38    $"C848CC97C848CC70C848CCBEC714CCDCC7C0CCDCC668CCDCC5E1CC97C5E1CCBE"
     39    $"C5E1CC700A04C5E1CC67C847CC67C847CC97C5E1CC970204C714CC26C668CC26"
     40    $"C7C0CC26C848CC6BC848CC44C848CC92C714CCB1C7C0CCB1C668CCB1C5E1CC6B"
     41    $"C5E1CC92C5E1CC440611FEFFEDFF02C394BADFC73EBD9CC613BD06C869BE32CB"
     42    $"07BFE3CA26BF02CBE7C0C3CC45C2CFCC45C20ACC45C394CB56C5A4CC23C4D7CA"
     43    $"89C671CA0BC6CFCAD0C651C946C74DC82EC865C8A0C7A4C7BCC926C77DCBC4C7"
     44    $"7DCA4AC77DCD3ECC61C70CCC7DC762CC7DC6B6CC7DC6A8CC61C681C926C681C9"
     45    $"F3C681C859C77DC6DFC6FBC761C7FFC65DC9E4C394CA37C503C991C22AC852C2"
     46    $"A8C8F7C271C7ACC2DFC56DC42AC61A4AC4C0C498C385C56D0204C2A9B8AAC14F"
     47    $"B8AAC403B8AAC513BFF8C513BBE1C513C40FC2A9C746C403C746C14FC746C03F"
     48    $"BFF8C03FC40FC03FBBE10204C298B8E9C15BB8E9C3D6B8E9C4D0BFF8C4D0BC04"
     49    $"C4D0C3ECC298C706C3D6C706C15BC706C061BFF8C061C3ECC061BC040607FF3E"
     50    $"CAFCC32ACB30C35DCAFCC32ACA65C4DBCAF7C449C9D4C56DC936C5F5C990C5AB"
     51    $"C8DBC640C80EC6FEC864C680C7D3C754C77DC875C90BC698C7FCC73EC9A2C63B"
     52    $"CB36C4BCCB04C529CB86C40D0205C517C3A4C517C369C517C3A4C817C271C78A"
     53    $"C29EC8A8C242C9EBC27CC938C1F7CA40C2BCCA3EC1E7CAADC23EC9BAC17EC6E7"
     54    $"C23DC87AC199C5B8C2BA0608FEFAC26DB961C2EBBA45C29CB9CFC33ABABBC41A"
     55    $"BCDBC3EFBAB7C446BEFEC3F7C39CC4A0C0FEC34EC63AC30BC67DC31EC690C491"
     56    $"BE5DC4FFC3D7C43DBA33C2D7B95DC379B994C236B9260205C186BD52C1BABCA9"
     57    $"C186BD52C14BC116C12ABF53C16CC2D9C254C45BC1CAC46BC2DDC44BC2DDC2AC"
     58    $"C33FC335C27BC222C1B3C0E5C1B3C1EEC1B3BFDD0205C0EFBD9CC12EBCE8C0EF"
     59    $"BD9CC0A8C19DC081BFBEC0CFC37CC1E7C516C142C528C28DC505C28DC34CC303"
     60    $"C3DEC216C2BAC126C169C126C282C126C0500205C2B8C588C2D2C5A2C2B8C588"
     61    $"C252C68CC252C655C252C6C3C2A4C6D7C281C6D7C2C8C6D7C2F9C6A6C316C6C3"
     62    $"C2DBC688C2ACC680C2ACC6A4C2ACC65D0207B7C920B7C920B8BD20B8BDB4A4B8"
     63    $"C8B393B8BDB4A4B857BC43B857BC43B84BBD0AB735BDAFB81EBDAFB64CBDAFB6"
     64    $"13BC43B61EBD0AB613BC43B5ACB4A4B5ACB4A4B59BB393B6A720B5B220B6A720"
     65    $"0208B77FC12CB77FC12CB857C12CB8BDC270B8BDC198B8BDC270B8BDC304B8BD"
     66    $"C304B8BDC3DCB77FC448B857C448B77FC448B6EBC448B6EBC448B613C448B5AC"
     67    $"C304B5ACC3DCB5ACC304B5ACC270B5ACC270B5ACC198B6EBC12CB613C12CB6EB"
     68    $"C12C0204B72ACD68B577CD68B8DDCD68BA33CDE5BA33CD9FBA33CE2CB72ACE63"
     69    $"B8DDCE63B577CE63B421CDE5B421CE2CB421CD9F130A000100000A010101000A"
     70    $"020102000A030103000A040104000A050105000A060106000A070107000A0801"
     71    $"08000A090109000A0A010A000A0B010B000A0C010C000A0D010D000A0E010E00"
     72    $"0A0F010F000A100110000A09021112000A11011300"
     73};
  • new file src/preferences/notifications/NotificationsView.cpp

    diff --git a/src/preferences/notifications/NotificationsView.cpp b/src/preferences/notifications/NotificationsView.cpp
    new file mode 100644
    index 0000000..bcde7ce
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <Alert.h>
     11#include <Directory.h>
     12#include <FindDirectory.h>
     13#include <GroupLayout.h>
     14#include <GroupLayoutBuilder.h>
     15#include <Window.h>
     16#include <CheckBox.h>
     17#include <TextControl.h>
     18#include <Path.h>
     19#include <Notification.h>
     20#include <notification/Notifications.h>
     21#include <notification/NotificationReceived.h>
     22
     23#include <ColumnListView.h>
     24#include <ColumnTypes.h>
     25
     26#include "NotificationsView.h"
     27
     28#define _T(str) (str)
     29
     30const float kEdgePadding = 5.0;
     31const float kCLVTitlePadding = 8.0;
     32
     33const int32 kApplicationSelected = '_ASL';
     34const int32 kNotificationSelected = '_NSL';
     35
     36const int32 kCLVDeleteRow = 'av02';
     37
     38// Applications column indexes
     39const int32 kAppIndex = 0;
     40const int32 kAppEnabledIndex = 1;
     41
     42// Notifications column indexes
     43const int32 kTitleIndex = 0;
     44const int32 kDateIndex = 1;
     45const int32 kTypeIndex = 2;
     46const int32 kAllowIndex = 3;
     47
     48const int32 kSettingChanged = '_STC';
     49
     50
     51NotificationsView::NotificationsView()
     52    :
     53    BView("apps", B_WILL_DRAW)
     54{
     55    BRect rect(0, 0, 100, 100);
     56
     57    // Search application field
     58    fSearch = new BTextControl(_T("Search:"), NULL,
     59        new BMessage(kSettingChanged));
     60
     61    // Applications list
     62    fApplications = new BColumnListView(rect, _T("Applications"),
     63        0, B_WILL_DRAW, B_FANCY_BORDER, true);
     64    fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
     65
     66    fAppCol = new BStringColumn(_T("Application"), 200,
     67        be_plain_font->StringWidth(_T("Application")) + (kCLVTitlePadding * 2),
     68        rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
     69    fApplications->AddColumn(fAppCol, kAppIndex);
     70
     71    fAppEnabledCol = new BStringColumn(_T("Enabled"), 10,
     72        be_plain_font->StringWidth(_T("Enabled")) + (kCLVTitlePadding * 2),
     73        rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
     74    fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
     75
     76    // Notifications list
     77    fNotifications = new BColumnListView(rect, _T("Notifications"),
     78        0, B_WILL_DRAW, B_FANCY_BORDER, true);
     79    fNotifications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
     80
     81    fTitleCol = new BStringColumn(_T("Title"), 100,
     82        be_plain_font->StringWidth(_T("Title")) + (kCLVTitlePadding * 2),
     83        rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
     84    fNotifications->AddColumn(fTitleCol, kTitleIndex);
     85
     86    fDateCol = new BDateColumn(_T("Last Received"), 100,
     87        be_plain_font->StringWidth(_T("Last Received")) + (kCLVTitlePadding * 2),
     88        rect.Width(), B_ALIGN_LEFT);
     89    fNotifications->AddColumn(fDateCol, kDateIndex);
     90
     91    fTypeCol = new BStringColumn(_T("Type"), 100,
     92        be_plain_font->StringWidth(_T("Type")) + (kCLVTitlePadding * 2),
     93        rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
     94    fNotifications->AddColumn(fTypeCol, kTypeIndex);
     95
     96    fAllowCol = new BStringColumn(_T("Allowed"), 100,
     97        be_plain_font->StringWidth(_T("Allowed")) + (kCLVTitlePadding * 2),
     98        rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
     99    fNotifications->AddColumn(fAllowCol, kAllowIndex);
     100
     101    // Load the applications list
     102    _LoadAppUsage();
     103    _PopulateApplications();
     104
     105    // Calculate inset
     106    float inset = ceilf(be_plain_font->Size() * 0.7f);
     107
     108    // Set layout
     109    SetLayout(new BGroupLayout(B_VERTICAL));
     110
     111    // Add views
     112    AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
     113        .AddGroup(B_HORIZONTAL)
     114            .AddGlue()
     115            .Add(fSearch)
     116        .End()
     117        .Add(fApplications)
     118        .Add(fNotifications)
     119    );
     120}
     121
     122
     123void
     124NotificationsView::AttachedToWindow()
     125{
     126    fApplications->SetTarget(this);
     127    fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
     128
     129    fNotifications->SetTarget(this);
     130    fNotifications->SetInvocationMessage(new BMessage(kNotificationSelected));
     131
     132#if 0
     133    fNotifications->AddFilter(new BMessageFilter(B_ANY_DELIVERY,
     134        B_ANY_SOURCE, B_KEY_DOWN, CatchDelete));
     135#endif
     136}
     137
     138
     139void
     140NotificationsView::MessageReceived(BMessage* msg)
     141{
     142    switch (msg->what) {
     143        case kApplicationSelected: {
     144                BRow *row = fApplications->CurrentSelection();
     145                if (row == NULL)
     146                    return;
     147                BStringField* appname =
     148                    dynamic_cast<BStringField*>(row->GetField(kAppIndex));
     149
     150                appusage_t::iterator it = fAppFilters.find(appname->String());
     151                if (it != fAppFilters.end())
     152                    _Populate(it->second);
     153            } break;
     154        case kNotificationSelected:
     155            break;
     156        default:
     157            BView::MessageReceived(msg);
     158            break;
     159    }
     160}
     161
     162
     163status_t
     164NotificationsView::_LoadAppUsage()
     165{
     166    BPath path;
     167
     168    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     169        return B_ERROR;
     170
     171    path.Append(kSettingsDirectory);
     172
     173    if (create_directory(path.Path(), 0755) != B_OK) {
     174        BAlert* alert = new BAlert("",
     175            _T("There was a problem saving the preferences.\n"
     176                "It's possible you don't have write access to the "
     177                "settings directory."), "OK", NULL, NULL,
     178            B_WIDTH_AS_USUAL, B_STOP_ALERT);
     179        (void)alert->Go();
     180        return B_ERROR;
     181    }
     182
     183    path.Append(kFiltersSettings);
     184
     185    BFile file(path.Path(), B_READ_ONLY);
     186    BMessage settings;
     187    if (settings.Unflatten(&file) != B_OK)
     188        return B_ERROR;
     189
     190    type_code type;
     191    int32 count = 0;
     192
     193    if (settings.GetInfo("app_usage", &type, &count) != B_OK)
     194        return B_ERROR;
     195
     196    // Clean filters
     197    appusage_t::iterator auIt;
     198    for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
     199        delete auIt->second;
     200    fAppFilters.clear();
     201
     202    // Add new filters
     203    for (int32 i = 0; i < count; i++) {
     204        AppUsage* app = new AppUsage();
     205        settings.FindFlat("app_usage", i, app);
     206        fAppFilters[app->Name()] = app;
     207    }
     208
     209    return B_OK;
     210}
     211
     212
     213void
     214NotificationsView::_PopulateApplications()
     215{
     216    appusage_t::iterator it;
     217
     218    fApplications->Clear();
     219
     220    for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
     221        BRow* row = new BRow();
     222        row->SetField(new BStringField(it->first.String()), kAppIndex);
     223        fApplications->AddRow(row);
     224    }
     225}
     226
     227
     228void
     229NotificationsView::_Populate(AppUsage* usage)
     230{
     231    // Sanity check
     232    if (!usage)
     233        return;
     234
     235    int32 size = usage->Notifications();
     236
     237    if (usage->Allowed() == false)
     238        fBlockAll->SetValue(B_CONTROL_ON);
     239
     240    fNotifications->Clear();
     241
     242    for (int32 i = 0; i < size; i++) {
     243        NotificationReceived* notification = usage->NotificationAt(i);
     244        time_t updated = notification->LastReceived();
     245        const char* allow = notification->Allowed() ? _T("Yes") : _T("No");
     246        const char* type = "";
     247
     248        switch (notification->Type()) {
     249            case B_INFORMATION_NOTIFICATION:
     250                type = _T("Information");
     251                break;
     252            case B_IMPORTANT_NOTIFICATION:
     253                type = _T("Important");
     254                break;
     255            case B_ERROR_NOTIFICATION:
     256                type = _T("Error");
     257                break;
     258            case B_PROGRESS_NOTIFICATION:
     259                type = _T("Progress");
     260                break;
     261            default:
     262                type = _T("Unknown");
     263        }
     264
     265        BRow* row = new BRow();
     266        row->SetField(new BStringField(notification->Title()), kTitleIndex);
     267        row->SetField(new BDateField(&updated), kDateIndex);
     268        row->SetField(new BStringField(type), kTypeIndex);
     269        row->SetField(new BStringField(allow), kAllowIndex);
     270
     271        fNotifications->AddRow(row);
     272    }
     273}
  • new file src/preferences/notifications/NotificationsView.h

    diff --git a/src/preferences/notifications/NotificationsView.h b/src/preferences/notifications/NotificationsView.h
    new file mode 100644
    index 0000000..45a09eb
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _APPS_VIEW_H
     7#define _APPS_VIEW_H
     8
     9#include <View.h>
     10
     11#include <notification/AppUsage.h>
     12
     13typedef std::map<BString, AppUsage *> appusage_t;
     14
     15class BCheckBox;
     16class BTextControl;
     17class BColumnListView;
     18class BStringColumn;
     19class BDateColumn;
     20
     21class NotificationsView : public BView {
     22public:
     23                                NotificationsView();
     24
     25    virtual void                AttachedToWindow();
     26    virtual void                MessageReceived(BMessage* msg);
     27
     28private:
     29            status_t            _LoadAppUsage();
     30            void                _PopulateApplications();
     31            void                _Populate(AppUsage* usage);
     32
     33            appusage_t          fAppFilters;
     34            BCheckBox*          fBlockAll;
     35            BTextControl*       fSearch;
     36            BColumnListView*    fApplications;
     37            BStringColumn*      fAppCol;
     38            BStringColumn*      fAppEnabledCol;
     39            BColumnListView*    fNotifications;
     40            BStringColumn*      fTitleCol;
     41            BDateColumn*        fDateCol;
     42            BStringColumn*      fTypeCol;
     43            BStringColumn*      fAllowCol;
     44};
     45
     46#endif // _APPS_VIEW_H
  • new file src/preferences/notifications/PrefletView.cpp

    diff --git a/src/preferences/notifications/PrefletView.cpp b/src/preferences/notifications/PrefletView.cpp
    new file mode 100644
    index 0000000..1482502
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <Message.h>
     11#include <GroupLayout.h>
     12#include <GroupLayoutBuilder.h>
     13#include <CardLayout.h>
     14#include <LayoutItem.h>
     15
     16#include "SettingsHost.h"
     17#include "PrefletView.h"
     18#include "IconRule.h"
     19#include "GeneralView.h"
     20#include "DisplayView.h"
     21#include "NotificationsView.h"
     22
     23#define _T(str) (str)
     24
     25const int32 kPageSelected = '_LCH';
     26
     27
     28PrefletView::PrefletView(SettingsHost* host)
     29    :
     30    BView("pages", B_WILL_DRAW)
     31{
     32    // Page selector
     33    fRule = new BIconRule("icon_rule");
     34    fRule->SetSelectionMessage(new BMessage(kPageSelected));
     35    (void)fRule->AddIcon(_T("General"), NULL);
     36    (void)fRule->AddIcon(_T("Display"), NULL);
     37    //(void)fRule->AddIcon(_T("Notifications"), NULL);
     38
     39    // View for card layout
     40    fPagesView = new BView("pages", B_WILL_DRAW);
     41
     42    // Pages
     43    GeneralView* general = new GeneralView(host);
     44    DisplayView* display = new DisplayView(host);
     45    NotificationsView* apps = new NotificationsView();
     46
     47    // Calculate inset
     48    float inset = ceilf(be_plain_font->Size() * 0.7f);
     49
     50    // Build the layout
     51    SetLayout(new BGroupLayout(B_VERTICAL));
     52
     53    // Card layout for pages
     54    BCardLayout* layout = new BCardLayout();
     55    fPagesView->SetLayout(layout);
     56    layout->AddView(general);
     57    layout->AddView(display);
     58    layout->AddView(apps);
     59
     60    // Add childs
     61    AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
     62        .Add(fRule)
     63        .Add(fPagesView)
     64    );
     65
     66    // Select the first view
     67    Select(0);
     68}
     69
     70
     71void
     72PrefletView::AttachedToWindow()
     73{
     74    fRule->SetTarget(this);
     75}
     76
     77
     78void
     79PrefletView::MessageReceived(BMessage* message)
     80{
     81    switch (message->what) {
     82        case kPageSelected:
     83        {
     84            int32 index = B_ERROR;
     85            if (message->FindInt32("index", &index) != B_OK)
     86                return;
     87
     88            Select(index);
     89            break;
     90        }
     91        default:
     92            BView::MessageReceived(message);
     93    }
     94}
     95
     96
     97void
     98PrefletView::Select(int32 index)
     99{
     100    BCardLayout* layout
     101        = dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
     102    if (layout)
     103        layout->SetVisibleItem(index);
     104}
     105
     106
     107BView*
     108PrefletView::CurrentPage()
     109{
     110    BCardLayout* layout
     111        = dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
     112    if (layout)
     113        return layout->VisibleItem()->View();
     114    return NULL;
     115}
     116
     117
     118int32
     119PrefletView::CountPages() const
     120{
     121    BCardLayout* layout
     122        = dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
     123    if (layout)
     124        return layout->CountItems();
     125    return 0;
     126}
     127
     128
     129BView*
     130PrefletView::PageAt(int32 index)
     131{
     132    BCardLayout* layout
     133        = dynamic_cast<BCardLayout*>(fPagesView->GetLayout());
     134    if (layout)
     135        return layout->ItemAt(index)->View();
     136    return NULL;
     137}
  • new file src/preferences/notifications/PrefletView.h

    diff --git a/src/preferences/notifications/PrefletView.h b/src/preferences/notifications/PrefletView.h
    new file mode 100644
    index 0000000..8b7d484
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _PREFLET_VIEW_H
     7#define _PREFLET_VIEW_H
     8
     9#include <View.h>
     10
     11class BIconRule;
     12
     13class SettingsHost;
     14
     15class PrefletView : public BView {
     16public:
     17                        PrefletView(SettingsHost* host);
     18
     19    virtual void        AttachedToWindow();
     20    virtual void        MessageReceived(BMessage* message);
     21
     22            void        Select(int32 index);
     23            BView*      CurrentPage();
     24            int32       CountPages() const;
     25            BView*      PageAt(int32 index);
     26
     27private:
     28            BIconRule*  fRule;
     29            BView*      fPagesView;
     30};
     31
     32#endif // PREFLETVIEW_H
  • new file src/preferences/notifications/PrefletWin.cpp

    diff --git a/src/preferences/notifications/PrefletWin.cpp b/src/preferences/notifications/PrefletWin.cpp
    new file mode 100644
    index 0000000..6925ac3
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <Application.h>
     11#include <GroupLayout.h>
     12#include <GroupLayoutBuilder.h>
     13#include <Button.h>
     14
     15#include "PrefletWin.h"
     16#include "PrefletView.h"
     17
     18#define _T(str) (str)
     19
     20const int32 kRevert = '_RVT';
     21const int32 kSave = '_SAV';
     22
     23
     24PrefletWin::PrefletWin()
     25    :
     26    BWindow(BRect(0, 0, 1, 1), "Notifications", B_TITLED_WINDOW, B_NOT_ZOOMABLE
     27        | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
     28{
     29    // Preflet container view
     30    fMainView = new PrefletView(this);
     31
     32    // Save and revert buttons
     33    fRevert = new BButton("revert", _T("Revert"), new BMessage(kRevert));
     34    fRevert->SetEnabled(false);
     35    fSave = new BButton("save", _T("Save"), new BMessage(kSave));
     36    fSave->SetEnabled(false);
     37
     38    // Calculate inset
     39    float inset = ceilf(be_plain_font->Size() * 0.7f);
     40
     41    // Build the layout
     42    SetLayout(new BGroupLayout(B_VERTICAL));
     43
     44    // Add childs
     45    AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
     46        .Add(fMainView)
     47
     48        .AddGroup(B_HORIZONTAL, inset)
     49            .AddGlue()
     50            .Add(fRevert)
     51            .Add(fSave)
     52        .End()
     53
     54        .SetInsets(inset, inset, inset, inset)
     55    );
     56
     57    // Center this window on screen and show it
     58    CenterOnScreen();
     59    Show();
     60}
     61
     62
     63void
     64PrefletWin::MessageReceived(BMessage* msg)
     65{
     66    switch (msg->what) {
     67        case kSave:
     68            for (int32 i = 0; i < fMainView->CountPages(); i++) {
     69                SettingsPane* pane =
     70                    dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
     71                if (pane) {
     72                    if (pane->Save() == B_OK) {
     73                        fSave->SetEnabled(false);
     74                        fRevert->SetEnabled(true);
     75                    } else
     76                        break;
     77                }
     78            }
     79            break;
     80        case kRevert:
     81            for (int32 i = 0; i < fMainView->CountPages(); i++) {
     82                SettingsPane* pane =
     83                    dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
     84                if (pane) {
     85                    if (pane->Revert() == B_OK)
     86                        fRevert->SetEnabled(false);
     87                }
     88            }
     89            break;
     90        default:
     91            BWindow::MessageReceived(msg);
     92    }
     93}
     94
     95
     96bool
     97PrefletWin::QuitRequested()
     98{
     99    be_app_messenger.SendMessage(B_QUIT_REQUESTED);
     100    return true;
     101}
     102
     103
     104void
     105PrefletWin::SettingChanged()
     106{
     107    fSave->SetEnabled(true);
     108}
  • new file src/preferences/notifications/PrefletWin.h

    diff --git a/src/preferences/notifications/PrefletWin.h b/src/preferences/notifications/PrefletWin.h
    new file mode 100644
    index 0000000..f2029b8
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _PREFLET_WIN_H
     7#define _PREFLET_WIN_H
     8
     9#include <Window.h>
     10
     11#include "SettingsHost.h"
     12
     13class BButton;
     14
     15class PrefletView;
     16
     17class PrefletWin : public BWindow, public SettingsHost {
     18public:
     19                            PrefletWin();
     20
     21    virtual bool            QuitRequested();
     22    virtual void            MessageReceived(BMessage* msg);
     23
     24    virtual void            SettingChanged();
     25
     26private:
     27            PrefletView*    fMainView;
     28            BButton*        fSave;
     29            BButton*        fRevert;
     30};
     31
     32#endif // _PREFLET_WIN_H
  • new file src/preferences/notifications/SettingsHost.h

    diff --git a/src/preferences/notifications/SettingsHost.h b/src/preferences/notifications/SettingsHost.h
    new file mode 100644
    index 0000000..3774d7d
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _SETTINGS_HOST_H
     7#define _SETTINGS_HOST_H
     8
     9#include <vector>
     10
     11#include "SettingsPane.h"
     12
     13class SettingsHost {
     14public:
     15                    SettingsHost() {}
     16
     17    virtual void    SettingChanged() = 0;
     18};
     19
     20#endif // _SETTINGS_HOST_H
  • new file src/preferences/notifications/SettingsPane.cpp

    diff --git a/src/preferences/notifications/SettingsPane.cpp b/src/preferences/notifications/SettingsPane.cpp
    new file mode 100644
    index 0000000..55e325e
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     8 */
     9
     10#include <Message.h>
     11#include <Node.h>
     12#include <Path.h>
     13#include <FindDirectory.h>
     14#include <Directory.h>
     15
     16#include "SettingsPane.h"
     17#include "SettingsHost.h"
     18
     19
     20SettingsPane::SettingsPane(const char* name, SettingsHost* host)
     21    :
     22    BView(name, B_WILL_DRAW),
     23    fHost(host)
     24{
     25}
     26
     27
     28void
     29SettingsPane::MessageReceived(BMessage* msg)
     30{
     31    switch (msg->what) {
     32        case kSettingChanged:
     33            fHost->SettingChanged();
     34            break;
     35        default:
     36            BView::MessageReceived(msg);
     37    }
     38}
  • new file src/preferences/notifications/SettingsPane.h

    diff --git a/src/preferences/notifications/SettingsPane.h b/src/preferences/notifications/SettingsPane.h
    new file mode 100644
    index 0000000..6908900
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2009, Pier Luigi Fiorini.
     4 * Distributed under the terms of the MIT License.
     5 */
     6#ifndef _SETTINGS_PANE_H
     7#define _SETTINGS_PANE_H
     8
     9#include <View.h>
     10
     11class BNode;
     12
     13class SettingsHost;
     14
     15const int32 kSettingChanged = '_STC';
     16
     17class SettingsPane : public BView {
     18public:
     19                            SettingsPane(const char* name, SettingsHost* host);
     20
     21    virtual void            MessageReceived(BMessage* msg);
     22
     23    virtual status_t        Load() = 0;
     24    virtual status_t        Save() = 0;
     25    virtual status_t        Revert() = 0;
     26
     27protected:
     28            SettingsHost*   fHost;
     29};
     30
     31#endif // _SETTINGS_PANE_H
  • src/servers/Jamfile

    diff --git a/src/servers/Jamfile b/src/servers/Jamfile
    index 1d05594..77f3dc7 100644
    a b SubInclude HAIKU_TOP src servers media_addon ; 
    1111SubInclude HAIKU_TOP src servers midi ;
    1212SubInclude HAIKU_TOP src servers mount ;
    1313SubInclude HAIKU_TOP src servers net ;
     14SubInclude HAIKU_TOP src servers notification ;
    1415SubInclude HAIKU_TOP src servers power ;
    1516SubInclude HAIKU_TOP src servers print ;
    1617SubInclude HAIKU_TOP src servers registrar ;
  • new file src/servers/notification/AppGroupView.cpp

    diff --git a/src/servers/notification/AppGroupView.cpp b/src/servers/notification/AppGroupView.cpp
    new file mode 100644
    index 0000000..9612f54
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include <algorithm>
     15
     16#include "AppGroupView.h"
     17
     18#include "NotificationWindow.h"
     19#include "NotificationView.h"
     20
     21
     22AppGroupView::AppGroupView(NotificationWindow* win, const char* label)
     23    :
     24    BView(BRect(0, 0, win->ViewWidth(), 1), label, B_FOLLOW_LEFT_RIGHT,
     25        B_WILL_DRAW|B_FULL_UPDATE_ON_RESIZE|B_FRAME_EVENTS),
     26    fLabel(label),
     27    fParent(win),
     28    fCollapsed(false)
     29{
     30    Show();
     31}
     32
     33
     34AppGroupView::~AppGroupView()
     35{
     36}
     37
     38
     39void
     40AppGroupView::AttachedToWindow()
     41{
     42    SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
     43    SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
     44    SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
     45}
     46
     47
     48void
     49AppGroupView::Draw(BRect updateRect)
     50{
     51    FillRect(Bounds(), B_SOLID_LOW);
     52
     53    BString label = fLabel;
     54    if (fCollapsed)
     55        label << " (" << fInfo.size() << ")";
     56
     57    font_height fh;
     58    be_bold_font->GetHeight(&fh);
     59    float labelOffset = fh.ascent + fh.leading;
     60
     61    BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding);
     62    borderRect.top = labelOffset;
     63
     64    BRect textRect = borderRect;
     65    textRect.left = kEdgePadding * 2;
     66    textRect.right = textRect.left + be_bold_font->StringWidth(label.String())
     67        + (kEdgePadding * 3);
     68    textRect.bottom = labelOffset;
     69
     70    BRect closeCross = fCloseRect;
     71    closeCross.InsetBy(kSmallPadding, kSmallPadding);
     72
     73    rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
     74    detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
     75    // detailCol = tint_color(detailCol, B_LIGHTEN_1_TINT);
     76
     77    if (fCollapsed) {
     78        PushState();
     79            SetFont(be_bold_font);
     80            SetPenSize(kPenSize);
     81            float linePos = textRect.top + textRect.Height() / 2;
     82
     83            // Draw the line to the expand widget           
     84            PushState();
     85                SetHighColor(detailCol);               
     86                StrokeLine(BPoint(kEdgePadding, linePos), BPoint(fCollapseRect.left, linePos));
     87            PopState();
     88           
     89            // Draw the expand widget
     90            PushState();
     91                SetHighColor(detailCol);
     92                StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding);
     93               
     94                BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
     95                BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);           
     96                StrokeLine(expandHorStart, expandHorEnd);
     97               
     98                BPoint expandVerStart(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.top + kSmallPadding);
     99                BPoint expandVerEnd(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.bottom - kSmallPadding);             
     100                StrokeLine(expandVerStart, expandVerEnd);
     101            PopState();
     102           
     103            // Draw the app title
     104            DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding));
     105           
     106            // Draw the line from the label to the close widget
     107            PushState();
     108                SetHighColor(detailCol);
     109               
     110                BPoint lineSeg2Start(textRect.right + kSmallPadding / 2, linePos);
     111                BPoint lineSeg2End(fCloseRect.left, linePos);
     112                StrokeLine(lineSeg2Start, lineSeg2End);
     113            PopState();
     114
     115            // Draw the dismiss widget
     116            PushState();
     117                SetHighColor(detailCol);
     118
     119                StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding);
     120
     121                StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
     122                StrokeLine(closeCross.RightTop(), closeCross.LeftBottom());
     123            PopState();
     124
     125            // Draw the line from the dismiss widget
     126            PushState();
     127                SetHighColor(detailCol);
     128
     129                BPoint lineSeg3Start(fCloseRect.right, linePos);
     130                BPoint lineSeg3End(borderRect.right, linePos);
     131                StrokeLine(lineSeg3Start, lineSeg3End);
     132            PopState();
     133
     134        PopState();
     135    } else {
     136        PushState();
     137            SetFont(be_bold_font);
     138            SetPenSize(kPenSize);
     139
     140            // Draw the border
     141            PushState();
     142                SetHighColor(detailCol);
     143                // StrokeRoundRect(borderRect, kEdgePadding, kEdgePadding * 2);
     144                StrokeRect(borderRect);
     145            PopState();
     146
     147            FillRect(textRect, B_SOLID_LOW);
     148
     149            // Draw the collapse widget
     150            PushState();
     151                SetHighColor(detailCol);
     152                StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding);
     153
     154                BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
     155                BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top);
     156
     157                StrokeLine(expandHorStart, expandHorEnd);
     158            PopState();
     159
     160            // Draw the dismiss widget
     161            PushState();
     162                SetHighColor(detailCol);           
     163                FillRect(fCloseRect, B_SOLID_LOW);
     164
     165                StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding);
     166
     167                StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
     168                StrokeLine(closeCross.RightTop(), closeCross.LeftBottom());
     169            PopState();
     170
     171            // Draw the label
     172            DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding));
     173        PopState();
     174    }
     175
     176    Sync();
     177}
     178
     179
     180void
     181AppGroupView::MouseDown(BPoint point)
     182{
     183    bool changed = false;
     184    if (fCloseRect.Contains(point)) {
     185        changed = true;
     186
     187        int32 children = fInfo.size();
     188        for (int32 i = 0; i < children; i++) {
     189            fInfo[i]->RemoveSelf();
     190            delete fInfo[i];
     191        }
     192        fInfo.clear();
     193    }
     194
     195    if (fCollapseRect.Contains(point)) {
     196        fCollapsed = !fCollapsed;
     197        changed = true;
     198    }
     199
     200    if (changed) {
     201        ResizeViews();
     202        Invalidate();
     203    }
     204}
     205
     206
     207void
     208AppGroupView::GetPreferredSize(float* width, float* height)
     209{
     210    font_height fh;
     211    be_bold_font->GetHeight(&fh);
     212
     213    float h = fh.ascent + fh.leading + fh.leading;
     214    h += kEdgePadding * 2; // Padding between top and bottom of label
     215
     216    if (!fCollapsed) {
     217        int32 children = fInfo.size();
     218
     219        for (int32 i = 0; i < children; i++) {
     220            float childHeight = 0;
     221            float childWidth = 0;
     222           
     223            fInfo[i]->GetPreferredSize(&childWidth, &childHeight);
     224           
     225            h += childHeight;
     226        }
     227    }
     228
     229    h += kEdgePadding;
     230
     231    *width = fParent->ViewWidth();
     232    *height = h;
     233}
     234
     235
     236void
     237AppGroupView::MessageReceived(BMessage* msg)
     238{
     239    switch (msg->what) {
     240        case kRemoveView:
     241        {
     242            NotificationView* view = NULL;
     243            if (msg->FindPointer("view", (void**)&view) != B_OK)
     244                return;
     245
     246            infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view);
     247
     248            if (vIt != fInfo.end()) {
     249                fInfo.erase(vIt);
     250                view->RemoveSelf();
     251                delete view;
     252            }
     253
     254            ResizeViews();
     255            Invalidate();
     256
     257            // When all the views are destroy, save app filters
     258            if (fInfo.size() == 0)
     259                dynamic_cast<NotificationWindow*>(Window())->SaveAppFilters();
     260            break;
     261        }
     262        default:
     263            BView::MessageReceived(msg);
     264    }
     265}
     266
     267
     268void
     269AppGroupView::AddInfo(NotificationView* view)
     270{
     271    BString id = view->MessageID();
     272    if (id.Length() > 0) {
     273        int32 children = fInfo.size();
     274        bool found = false;
     275
     276        for (int32 i = 0; i < children; i++) {
     277            if (fInfo[i]->HasMessageID(id.String())) {
     278                fInfo[i]->RemoveSelf();
     279                delete fInfo[i];
     280               
     281                fInfo[i] = view;
     282                found = true;
     283               
     284                break;
     285            }
     286        }
     287
     288        if (!found)
     289            fInfo.push_back(view);
     290    } else
     291        fInfo.push_back(view);
     292
     293    if (fParent->IsHidden())
     294        fParent->Show();
     295    if (IsHidden())
     296        Show();
     297    if (view->IsHidden())
     298        view->Show();
     299
     300    AddChild(view);
     301
     302    ResizeViews();
     303    Invalidate();
     304}
     305
     306
     307void
     308AppGroupView::ResizeViews()
     309{
     310    font_height fh;
     311    be_bold_font->GetHeight(&fh);
     312
     313    float offset = fh.ascent + fh.leading + fh.descent;
     314    int32 children = fInfo.size();
     315
     316    if (!fCollapsed) {
     317        offset += kEdgePadding + kPenSize;
     318
     319        for (int32 i = 0; i < children; i++) {
     320            fInfo[i]->ResizeToPreferred();
     321            fInfo[i]->MoveTo(kEdgePadding + kPenSize, offset);
     322           
     323            offset += fInfo[i]->Bounds().Height();
     324            if (fInfo[i]->IsHidden())
     325                fInfo[i]->Show();
     326            fInfo[i]->SetPosition(false, false);
     327        };
     328    } else {
     329        for (int32 i = 0; i < children; i++)
     330            if (!fInfo[i]->IsHidden())
     331                fInfo[i]->Hide();
     332    }
     333
     334    if (children == 1)
     335        fInfo[0]->SetPosition(true, true);
     336    else if (children > 1) {
     337        fInfo[0]->SetPosition(true, false);
     338        fInfo[children - 1]->SetPosition(false, true);
     339    }
     340
     341    ResizeTo(fParent->ViewWidth(), offset);
     342    float labelOffset = fh.ascent + fh.leading;
     343
     344    BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding);
     345    borderRect.top = labelOffset;
     346
     347    fCollapseRect = borderRect;
     348    fCollapseRect.right = fCollapseRect.left + kExpandSize;
     349    fCollapseRect.bottom = fCollapseRect.top + kExpandSize;
     350    fCollapseRect.OffsetTo(kEdgePadding * 2, kEdgePadding * 1.5);
     351
     352    fCloseRect = borderRect;
     353    fCloseRect.right -= kEdgePadding * 4;
     354    fCloseRect.left = fCloseRect.right - kCloseSize;
     355    fCloseRect.bottom = fCloseRect.top + kCloseSize;
     356    fCloseRect.OffsetTo(fCloseRect.left, kEdgePadding * 1.5);
     357
     358    fParent->ResizeAll();
     359}
     360
     361
     362bool
     363AppGroupView::HasChildren()
     364{
     365    return !fInfo.empty();
     366}
  • new file src/servers/notification/AppGroupView.h

    diff --git a/src/servers/notification/AppGroupView.h b/src/servers/notification/AppGroupView.h
    new file mode 100644
    index 0000000..eeb9a81
    - +  
     1/*
     2 * Copyright 2005-2008, Mikael Eiman.
     3 * Copyright 2005-2008, Michael Davidson.
     4 * Distributed under the terms of the MIT License.
     5 *
     6 * Authors:
     7 *              Mikael Eiman <mikael@eiman.tv>
     8 *              Michael Davidson <slaad@bong.com.au>
     9 */
     10
     11#ifndef APPGROUPVIEW_H
     12#define APPGROUPVIEW_H
     13
     14#include <vector>
     15
     16#include <String.h>
     17#include <View.h>
     18
     19class NotificationWindow;
     20class NotificationView;
     21
     22typedef std::vector<NotificationView *> infoview_t;
     23
     24class AppGroupView : public BView {
     25    public:
     26                            AppGroupView(NotificationWindow *win, const char *label);
     27                            ~AppGroupView(void);
     28   
     29        // Hooks
     30        void                AttachedToWindow(void);
     31        void                Draw(BRect bounds);
     32        void                MouseDown(BPoint point);
     33        void                GetPreferredSize(float *width, float *height);
     34        void                MessageReceived(BMessage *msg);
     35   
     36        // Public
     37        void                AddInfo(NotificationView *view);
     38        void                ResizeViews(void);
     39        bool                HasChildren(void);
     40   
     41    private:
     42        BString             fLabel;
     43        NotificationWindow          *fParent;
     44        infoview_t          fInfo;
     45        bool                fCollapsed;
     46        BRect               fCloseRect;
     47        BRect               fCollapseRect;
     48};
     49
     50#endif
  • new file src/servers/notification/BorderView.cpp

    diff --git a/src/servers/notification/BorderView.cpp b/src/servers/notification/BorderView.cpp
    new file mode 100644
    index 0000000..e19fb86
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include "BorderView.h"
     15
     16const float kBorderWidth    = 2.0f;
     17const int32 kTitleSize      = 14;
     18
     19
     20BorderView::BorderView(BRect rect, const char* text)
     21    :
     22    BView(rect, "NotificationBorderView", B_FOLLOW_ALL,
     23        B_WILL_DRAW | B_FRAME_EVENTS),
     24    fTitle(text)
     25{
     26    SetViewColor(B_TRANSPARENT_COLOR);
     27}
     28
     29
     30BorderView::~BorderView()
     31{
     32}
     33
     34
     35void
     36BorderView::FrameResized(float, float)
     37{
     38    Invalidate();
     39}
     40
     41
     42void
     43BorderView::Draw(BRect rect)
     44{
     45    rgb_color col_bg = ui_color(B_PANEL_BACKGROUND_COLOR);
     46    rgb_color col_text = tint_color(col_bg, B_LIGHTEN_MAX_TINT);
     47    rgb_color col_text_shadow = tint_color(col_bg, B_DARKEN_MAX_TINT);
     48
     49    // Background
     50    SetDrawingMode(B_OP_COPY);
     51    SetHighColor(col_bg);
     52    FillRect(rect);
     53
     54    // Text
     55    SetLowColor(col_bg);
     56    SetDrawingMode(B_OP_ALPHA);
     57    SetFont(be_bold_font);
     58    SetFontSize(kTitleSize);
     59
     60    BFont font;
     61    GetFont(&font);
     62
     63    font_height fh;
     64    font.GetHeight(&fh);
     65
     66    // float line_height = fh.ascent + fh.descent + fh.leading;
     67
     68    float text_pos = fh.ascent;
     69
     70    SetHighColor(col_text);
     71    DrawString(fTitle.String(), BPoint(kBorderWidth, text_pos));
     72
     73    SetHighColor(col_text_shadow);
     74    DrawString(fTitle.String(), BPoint(kBorderWidth + 1, text_pos + 1));
     75
     76    // Content border
     77    SetHighColor(tint_color(col_bg, B_DARKEN_2_TINT));
     78
     79    BRect content = Bounds();
     80    content.InsetBy(kBorderWidth, kBorderWidth);
     81    content.top += text_pos + fh.descent + 2;
     82
     83    BRect content_line(content);
     84    content_line.InsetBy(-1, -1);
     85
     86    StrokeRect(content_line);
     87}
     88
     89
     90void
     91BorderView::GetPreferredSize(float* w, float* h)
     92{
     93    SetFont(be_bold_font);
     94    SetFontSize(kTitleSize);
     95
     96    BFont font;
     97    GetFont(&font);
     98
     99    font_height fh;
     100    font.GetHeight(&fh);
     101
     102    float line_height = fh.ascent + fh.descent;
     103
     104    *w = 2 * kBorderWidth + StringWidth(fTitle.String());
     105    *h = 2 * kBorderWidth + line_height + 3;
     106    *w = *h = 0;
     107}
     108
     109
     110float
     111BorderView::BorderSize()
     112{
     113    return 0;
     114}
  • new file src/servers/notification/BorderView.h

    diff --git a/src/servers/notification/BorderView.h b/src/servers/notification/BorderView.h
    new file mode 100644
    index 0000000..2e1bea9
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 */
     8#ifndef _BORDER_VIEW_H
     9#define _BORDER_VIEW_H
     10
     11#include <View.h>
     12#include <String.h>
     13
     14class BorderView : public BView {
     15public:
     16                    BorderView(BRect, const char*);
     17    virtual         ~BorderView();
     18
     19    virtual void    Draw(BRect updateRect);
     20
     21    virtual void    GetPreferredSize(float*, float*);
     22
     23    virtual void    FrameResized(float, float);
     24
     25            float   BorderSize();
     26
     27private:
     28            BString fTitle;
     29};
     30
     31#endif  // _BORDER_VIEW_H
  • new file src/servers/notification/Jamfile

    diff --git a/src/servers/notification/Jamfile b/src/servers/notification/Jamfile
    new file mode 100644
    index 0000000..26d0652
    - +  
     1SubDir HAIKU_TOP src servers notification ;
     2
     3UsePrivateHeaders notification ;
     4UseLibraryHeaders icon ;
     5
     6Server notification_server :
     7    AppGroupView.cpp
     8    BorderView.cpp
     9    NotificationServer.cpp
     10    NotificationView.cpp
     11    NotificationWindow.cpp
     12    : be $(TARGET_LIBSTDC++) libicon.a libnotification.a
     13    : notification_server.rdef
     14;
     15
     16Depends notification_server : libicon.a ;
     17Depends notification_server : libnotification.a ;
  • new file src/servers/notification/NotificationServer.cpp

    diff --git a/src/servers/notification/NotificationServer.cpp b/src/servers/notification/NotificationServer.cpp
    new file mode 100644
    index 0000000..bd874e3
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Distributed under the terms of the MIT License.
     4 *
     5 * Authors:
     6 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     7 */
     8
     9#include <Beep.h>
     10#include <Notifications.h>
     11
     12#include "NotificationWindow.h"
     13#include "NotificationServer.h"
     14
     15const char* kSoundNames[] = {
     16    "Information notification",
     17    "Important notification",
     18    "Error notification",
     19    "Progress notification",
     20    NULL
     21};
     22
     23
     24NotificationServer::NotificationServer()
     25    : BApplication(kNotificationServerSignature)
     26{
     27    fWindow = new NotificationWindow();
     28}
     29
     30
     31NotificationServer::~NotificationServer()
     32{
     33}
     34
     35
     36void
     37NotificationServer::MessageReceived(BMessage* message)
     38{
     39    switch (message->what) {
     40        case kNotificationMessage:
     41        {
     42            // Skip this message if we don't have the window
     43            if (!fWindow)
     44                return;
     45
     46            int32 type = 0;
     47
     48            // Emit a sound for this event
     49            if (message->FindInt32("type", &type) == B_OK) {
     50                if (type < (int32)(sizeof(kSoundNames) / sizeof(const char*)))
     51                    system_beep(kSoundNames[type]);
     52            }
     53
     54            // Let the notification window handle this message
     55            BMessenger(fWindow).SendMessage(message);
     56            break;
     57        }
     58        default:
     59            BApplication::MessageReceived(message);
     60    }
     61}
     62
     63
     64bool
     65NotificationServer::QuitRequested()
     66{
     67    if (fWindow && fWindow->Lock()) {
     68        fWindow->Quit();
     69        fWindow = NULL;
     70    }
     71    return true;
     72}
     73
     74
     75status_t
     76NotificationServer::GetSupportedSuites(BMessage* msg)
     77{
     78    msg->AddString("suites", "suite/x-vnd.Haiku-notification_server");
     79
     80    BPropertyInfo info(main_prop_list);
     81    msg->AddFlat("messages", &info);
     82
     83    return BApplication::GetSupportedSuites(msg);
     84}
     85
     86
     87BHandler*
     88NotificationServer::ResolveSpecifier(BMessage* msg, int32 index,
     89    BMessage* spec, int32 from, const char* prop)
     90{
     91    BPropertyInfo info(main_prop_list);
     92
     93    if (strcmp(prop, "message") == 0) {
     94        BMessenger messenger(fWindow);
     95        messenger.SendMessage(msg, fWindow);
     96        return NULL;
     97    }
     98
     99    return BApplication::ResolveSpecifier(msg, index, spec, from, prop);
     100}
     101
     102
     103int
     104main(int argc, char* argv[])
     105{
     106    int32 i = 0;
     107
     108    // Add system sounds
     109    while (kSoundNames[i] != NULL)
     110        add_system_beep_event(kSoundNames[i++], 0);
     111
     112    // Start!
     113    NotificationServer* server = new NotificationServer();
     114    server->Run();
     115
     116    return 0;
     117}
  • new file src/servers/notification/NotificationServer.h

    diff --git a/src/servers/notification/NotificationServer.h b/src/servers/notification/NotificationServer.h
    new file mode 100644
    index 0000000..aff0da1
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef _NOTIFICATION_SERVER_H
     6#define _NOTIFICATION_SERVER_H
     7
     8#include <Application.h>
     9
     10class NotificationWindow;
     11
     12class NotificationServer : public BApplication {
     13public:
     14                            NotificationServer();
     15    virtual                 ~NotificationServer();
     16
     17    virtual void            MessageReceived(BMessage* message);
     18    virtual bool            QuitRequested();
     19
     20    virtual status_t        GetSupportedSuites(BMessage* msg);
     21    virtual BHandler*       ResolveSpecifier(BMessage* msg, int32 index, BMessage* spec,
     22                                        int32 form, const char* prop);
     23
     24private:
     25        NotificationWindow* fWindow;
     26};
     27
     28#endif  // _NOTIFICATION_SERVER_H
  • new file src/servers/notification/NotificationView.cpp

    diff --git a/src/servers/notification/NotificationView.cpp b/src/servers/notification/NotificationView.cpp
    new file mode 100644
    index 0000000..fee1853
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include <stdlib.h>
     15
     16#include <Font.h>
     17#include <IconUtils.h>
     18#include <Messenger.h>
     19#include <Picture.h>
     20#include <PropertyInfo.h>
     21#include <Region.h>
     22#include <Resources.h>
     23#include <Roster.h>
     24#include <StringView.h>
     25#include <TranslationUtils.h>
     26
     27#include "NotificationView.h"
     28#include "NotificationWindow.h"
     29
     30const char* kSmallIconAttribute = "BEOS:M:STD_ICON";
     31const char* kLargeIconAttribute = "BEOS:L:STD_ICON";
     32const char* kIconAttribute      = "BEOS:ICON";
     33
     34property_info message_prop_list[] = {
     35    { "type", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
     36        {B_DIRECT_SPECIFIER, 0}, "get the notification type"},
     37    { "app", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
     38        {B_DIRECT_SPECIFIER, 0}, "get notification's app"},
     39    { "title", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
     40        {B_DIRECT_SPECIFIER, 0}, "get notification's title"},
     41    { "content", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
     42        {B_DIRECT_SPECIFIER, 0}, "get notification's contents"},
     43    { "icon", {B_GET_PROPERTY, 0},
     44        {B_DIRECT_SPECIFIER, 0}, "get icon as an archived bitmap"},
     45    { "progress", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
     46        {B_DIRECT_SPECIFIER, 0}, "get the progress (between 0.0 and 1.0)"},
     47    NULL
     48};
     49
     50
     51NotificationView::NotificationView(NotificationWindow* win,
     52    notification_type type, const char* app, const char* title, const char* text,
     53    BMessage* details)
     54    :
     55    BView(BRect(0, 0, win->ViewWidth(), 1), "NotificationView",
     56        B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
     57        | B_FRAME_EVENTS),
     58    fParent(win),
     59    fType(type),
     60    fRunner(NULL),
     61    fProgress(0.0f),
     62    fMessageID(""),
     63    fDetails(details),
     64    fBitmap(NULL),
     65    fIsFirst(false),
     66    fIsLast(false)
     67{
     68    BMessage iconMsg;
     69    if (fDetails->FindMessage("icon", &iconMsg) == B_OK)
     70        fBitmap = new BBitmap(&iconMsg);
     71
     72    if (!fBitmap)
     73        _LoadIcon();
     74
     75    const char* messageID = NULL;
     76    if (fDetails->FindString("messageID", &messageID) == B_OK)
     77        fMessageID = messageID;
     78
     79    if (fDetails->FindFloat("progress", &fProgress) != B_OK)
     80        fProgress = 0.0f;
     81
     82    // Progress is between 0 and 1
     83    if (fProgress < 0.0f)
     84        fProgress = 0.0f;
     85    if (fProgress > 1.0f)
     86        fProgress = 1.0f;
     87
     88    SetText(app, title, text);
     89    ResizeToPreferred();
     90
     91    switch (type) {
     92        case B_IMPORTANT_NOTIFICATION:
     93            SetViewColor(255, 255, 255);
     94            SetLowColor(255, 255, 255);
     95            break;
     96        case B_ERROR_NOTIFICATION:
     97            SetViewColor(ui_color(B_FAILURE_COLOR));
     98            SetLowColor(ui_color(B_FAILURE_COLOR));
     99            break;
     100        default:
     101            SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
     102            SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
     103            break;
     104    }
     105}
     106
     107
     108NotificationView::~NotificationView()
     109{
     110    delete fRunner;
     111    delete fDetails;
     112    delete fBitmap;
     113
     114    vline::iterator lIt;
     115    for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
     116        delete (*lIt);
     117}
     118
     119
     120void
     121NotificationView::AttachedToWindow()
     122{
     123    BMessage msg(kRemoveView);
     124    msg.AddPointer("view", this);
     125    int32 timeout = -1;
     126
     127    if (fDetails->FindInt32("timeout", &timeout) != B_OK)
     128        timeout = fParent->Timeout();
     129    bigtime_t delay = timeout * 1000 * 1000;
     130   
     131    if (delay > 0)
     132        fRunner = new BMessageRunner(BMessenger(Parent()), &msg, delay, 1);
     133}
     134
     135
     136void
     137NotificationView::MessageReceived(BMessage* msg)
     138{
     139    switch (msg->what) {
     140        case B_GET_PROPERTY:
     141        {
     142            BMessage specifier;
     143            const char* property;
     144            BMessage reply(B_REPLY);
     145            bool msgOkay = true;
     146
     147            if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
     148                msgOkay = false;
     149            if (specifier.FindString("property", &property) != B_OK)
     150                msgOkay = false;
     151
     152            if (msgOkay) {
     153                if (strcmp(property, "type") == 0)
     154                    reply.AddInt32("result", fType);
     155
     156                if (strcmp(property, "app") == 0)
     157                    reply.AddString("result", fApp);
     158
     159                if (strcmp(property, "title") == 0)
     160                    reply.AddString("result", fTitle);
     161
     162                if (strcmp(property, "content") == 0)
     163                    reply.AddString("result", fText);
     164
     165                if (strcmp(property, "progress") == 0)
     166                    reply.AddFloat("result", fProgress);
     167
     168                if ((strcmp(property, "icon") == 0) && fBitmap) {
     169                    BMessage archive;
     170                    if (fBitmap->Archive(&archive) == B_OK)
     171                        reply.AddMessage("result", &archive);
     172                }
     173
     174                reply.AddInt32("error", B_OK);
     175            } else {
     176                reply.what = B_MESSAGE_NOT_UNDERSTOOD;
     177                reply.AddInt32("error", B_ERROR);
     178            }
     179
     180            msg->SendReply(&reply);
     181            break;
     182        }
     183        case B_SET_PROPERTY:
     184        {
     185            BMessage specifier;
     186            const char* property;
     187            BMessage reply(B_REPLY);
     188            bool msgOkay = true;
     189
     190            if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
     191                msgOkay = false;
     192            if (specifier.FindString("property", &property) != B_OK)
     193                msgOkay = false;
     194
     195            if (msgOkay) {
     196                if (strcmp(property, "app") == 0)
     197                    msg->FindString("data", &fApp);
     198
     199                if (strcmp(property, "title") == 0)
     200                    msg->FindString("data", &fTitle);
     201
     202                if (strcmp(property, "content") == 0)
     203                    msg->FindString("data", &fText);
     204
     205                if (strcmp(property, "icon") == 0) {
     206                    BMessage archive;
     207                    if (msg->FindMessage("data", &archive) == B_OK) {
     208                        delete fBitmap;
     209                        fBitmap = new BBitmap(&archive);
     210                    }
     211                }
     212
     213                SetText(Application(), Title(), Text());
     214                Invalidate();
     215
     216                reply.AddInt32("error", B_OK);
     217            } else {
     218                reply.what = B_MESSAGE_NOT_UNDERSTOOD;
     219                reply.AddInt32("error", B_ERROR);
     220            }
     221
     222            msg->SendReply(&reply);
     223            break;
     224        }
     225        case kRemoveView:
     226        {
     227            BMessage remove(kRemoveView);
     228            remove.AddPointer("view", this);
     229            BMessenger msgr(Window());
     230            msgr.SendMessage( &remove );
     231            break;
     232        }
     233        default:
     234            BView::MessageReceived(msg);
     235    }
     236}
     237
     238
     239void
     240NotificationView::GetPreferredSize(float* w, float* h)
     241{
     242    // Parent width, minus the edge padding, minus the pensize
     243    *w = fParent->ViewWidth() - (kEdgePadding * 2) - (kPenSize * 2);
     244    *h = fHeight;
     245
     246    if (fType == B_PROGRESS_NOTIFICATION) {
     247        font_height fh;
     248        be_plain_font->GetHeight(&fh);
     249        float fontHeight = fh.ascent + fh.descent + fh.leading;
     250        *h += (kSmallPadding * 2) + (kEdgePadding * 1) + fontHeight;
     251    }
     252}
     253
     254
     255void
     256NotificationView::Draw(BRect updateRect)
     257{
     258    BRect progRect;
     259
     260    // Draw progress background
     261    if (fType == B_PROGRESS_NOTIFICATION) {
     262        PushState();
     263
     264        font_height fh;
     265        be_plain_font->GetHeight(&fh);
     266        float fontHeight = fh.ascent + fh.descent + fh.leading;
     267
     268        progRect = Bounds();
     269        progRect.InsetBy(kEdgePadding, kEdgePadding);
     270        progRect.top = progRect.bottom - (kSmallPadding * 2) - fontHeight;
     271        StrokeRect(progRect);
     272
     273        BRect barRect = progRect;       
     274        barRect.InsetBy(1.0, 1.0);
     275        barRect.right *= fProgress;
     276        SetHighColor(ui_color(B_CONTROL_HIGHLIGHT_COLOR));
     277        FillRect(barRect);
     278
     279        SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
     280
     281        BString label = "";
     282        label << (int)(fProgress * 100) << " %";
     283
     284        float labelWidth = be_plain_font->StringWidth(label.String());
     285        float labelX = progRect.left + (progRect.IntegerWidth() / 2) - (labelWidth / 2);
     286
     287        SetLowColor(B_TRANSPARENT_COLOR);
     288        SetDrawingMode(B_OP_ALPHA);
     289        DrawString(label.String(), label.Length(),
     290            BPoint(labelX, progRect.top + fh.ascent + fh.leading + kSmallPadding));
     291
     292        PopState();
     293    }
     294
     295    SetDrawingMode(B_OP_ALPHA);
     296    SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
     297
     298    // Icon size
     299    float iconSize = (float)fParent->IconSize();
     300
     301    // Rectangle for icon and overlay icon
     302    BRect iconRect(0, 0, 0, 0);
     303
     304    // Draw icon
     305    if (fBitmap) {
     306        lineinfo* appLine = fLines.back();
     307        font_height fh;
     308        appLine->font.GetHeight(&fh);
     309
     310        float title_bottom = appLine->location.y + fh.descent;
     311
     312        float ix = kEdgePadding;
     313        float iy = 0;
     314        if (fParent->Layout() == TitleAboveIcon)
     315            iy = title_bottom + kEdgePadding + (Bounds().Height() - title_bottom
     316                - kEdgePadding * 2 - iconSize) / 2;
     317        else
     318            iy = (Bounds().Height() - iconSize) / 2.0;
     319
     320        if (fType == B_PROGRESS_NOTIFICATION)
     321            // Move icon up by half progress bar height if it's present
     322            iy -= (progRect.Height() + kEdgePadding) / 2.0;
     323
     324        iconRect.left = ix;
     325        iconRect.top = iy;
     326        iconRect.right = ix + iconSize;
     327        iconRect.bottom = iy + iconSize;
     328
     329        DrawBitmapAsync(fBitmap, fBitmap->Bounds(),
     330            iconRect, B_FILTER_BITMAP_BILINEAR);
     331    }
     332
     333    // Draw content
     334    vline::iterator lIt;
     335    for (lIt = fLines.begin(); lIt != fLines.end(); lIt++) {
     336        lineinfo *l = (*lIt);
     337   
     338        SetFont(&l->font);
     339        DrawString(l->text.String(), l->text.Length(), l->location);
     340    }
     341
     342    rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
     343    detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
     344
     345    // Draw the close widget
     346    BRect closeRect = Bounds();
     347    closeRect.InsetBy(kEdgePadding, kEdgePadding);
     348    closeRect.left = closeRect.right - kCloseSize;
     349    closeRect.bottom = closeRect.top + kCloseSize;
     350
     351    PushState();
     352        SetHighColor(detailCol);
     353        StrokeRoundRect(closeRect, kSmallPadding, kSmallPadding);
     354        BRect closeCross = closeRect.InsetByCopy(kSmallPadding, kSmallPadding);
     355        StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
     356        StrokeLine(closeCross.LeftBottom(), closeCross.RightTop());
     357    PopState();
     358
     359    Sync();
     360}
     361
     362
     363void
     364NotificationView::MouseDown(BPoint point)
     365{
     366    int32 buttons;
     367    Window()->CurrentMessage()->FindInt32("buttons", &buttons);
     368
     369    switch (buttons) {
     370        case B_PRIMARY_MOUSE_BUTTON:
     371        {
     372            BRect closeRect = Bounds().InsetByCopy(2,2);
     373            closeRect.left = closeRect.right - kCloseSize;
     374            closeRect.bottom = closeRect.top + kCloseSize; 
     375
     376            if (!closeRect.Contains(point)) {
     377                entry_ref launchRef;
     378                BString launchString;
     379                BMessage argMsg(B_ARGV_RECEIVED);
     380                BMessage refMsg(B_REFS_RECEIVED);
     381                entry_ref appRef;
     382                bool useArgv = false;
     383                BList messages;
     384                entry_ref ref;
     385
     386                if (fDetails->FindString("onClickApp", &launchString) == B_OK)
     387                    if (be_roster->FindApp(launchString.String(), &appRef) == B_OK)
     388                        useArgv = true;
     389                if (fDetails->FindRef("onClickFile", &launchRef) == B_OK) {
     390                    if (be_roster->FindApp(&launchRef, &appRef) == B_OK)
     391                        useArgv = true;
     392                }
     393
     394                if (fDetails->FindRef("onClickRef", &ref) == B_OK) {           
     395                    for (int32 i = 0; fDetails->FindRef("onClickRef", i, &ref) == B_OK; i++)
     396                        refMsg.AddRef("refs", &ref);
     397
     398                    messages.AddItem((void*)&refMsg);
     399                }
     400
     401                if (useArgv) {
     402                    type_code type;
     403                    int32 argc = 0;
     404                    BString arg;
     405
     406                    BPath p(&appRef);
     407                    argMsg.AddString("argv", p.Path());
     408
     409                    fDetails->GetInfo("onClickArgv", &type, &argc);
     410                    argMsg.AddInt32("argc", argc + 1);
     411
     412                    for (int32 i = 0; fDetails->FindString("onClickArgv", i, &arg) == B_OK; i++)
     413                        argMsg.AddString("argv", arg);
     414
     415                    messages.AddItem((void*)&argMsg);
     416                }
     417
     418                BMessage tmp;
     419                for (int32 i = 0; fDetails->FindMessage("onClickMsg", i, &tmp) == B_OK; i++)
     420                    messages.AddItem((void*)&tmp);
     421
     422                if (fDetails->FindString("onClickApp", &launchString) == B_OK)
     423                    be_roster->Launch(launchString.String(), &messages);
     424                else
     425                    be_roster->Launch(&launchRef, &messages);
     426            }
     427
     428            // Remove the info view after a click
     429            BMessage remove_msg(kRemoveView);
     430            remove_msg.AddPointer("view", this);
     431
     432            BMessenger msgr(Parent());
     433            msgr.SendMessage(&remove_msg);
     434            break;
     435        }
     436    }
     437}
     438
     439
     440void
     441NotificationView::FrameResized( float w, float /*h*/)
     442{
     443    SetText(Application(), Title(), Text());
     444}
     445
     446
     447BHandler*
     448NotificationView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* spec, int32 form, const char* prop)
     449{
     450    BPropertyInfo prop_info(message_prop_list);
     451    if (prop_info.FindMatch(msg, index, spec, form, prop) >= 0) {
     452        msg->PopSpecifier();
     453        return this;
     454    }
     455
     456    return BView::ResolveSpecifier(msg, index, spec, form, prop);
     457}
     458
     459
     460status_t
     461NotificationView::GetSupportedSuites(BMessage* msg)
     462{
     463    msg->AddString("suites", "suite/x-vnd.Haiku-notification_server");
     464    BPropertyInfo prop_info(message_prop_list);
     465    msg->AddFlat("messages", &prop_info);
     466    return BView::GetSupportedSuites(msg);     
     467}
     468
     469
     470const char*
     471NotificationView::Application() const
     472{
     473    return fApp.Length() > 0 ? fApp.String() : NULL;
     474}
     475
     476
     477const char*
     478NotificationView::Title() const
     479{
     480    return fTitle.Length() > 0 ? fTitle.String() : NULL;
     481}
     482
     483
     484const char*
     485NotificationView::Text() const
     486{
     487    return fText.Length() > 0 ? fText.String() : NULL;
     488}
     489
     490
     491void
     492NotificationView::SetText(const char *app, const char *title,
     493    const char *text, float newMaxWidth)
     494{
     495    if (newMaxWidth < 0)
     496        newMaxWidth = Bounds().Width() - (kEdgePadding * 2);
     497
     498    // Delete old lines
     499    vline::iterator lIt;
     500    for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
     501        delete (*lIt);
     502    fLines.clear();
     503
     504    fApp = app;
     505    fTitle = title;
     506    fText = text;
     507
     508    font_height fh;
     509    float fontHeight = 0;
     510    float iconRight = kEdgePadding + kEdgePadding;
     511    float y = 0;
     512
     513    if (fBitmap)
     514        iconRight += fParent->IconSize();
     515
     516    be_bold_font->GetHeight(&fh);
     517    fontHeight = fh.leading + fh.descent + fh.ascent;
     518    y += fontHeight;
     519
     520    // Title
     521    lineinfo* titleLine = new lineinfo;
     522    titleLine->text = fTitle.String();
     523    titleLine->font = be_bold_font;
     524
     525    if (fParent->Layout() == AllTextRightOfIcon)
     526        titleLine->location = BPoint(iconRight, y);
     527    else
     528        titleLine->location = BPoint(kEdgePadding, y);
     529
     530    fLines.push_front(titleLine);
     531    y += fontHeight;
     532
     533    // Text
     534    const char spacers[] = " \n-\\/";
     535    BString textBuffer = fText;
     536    textBuffer.ReplaceAll("\t", "    ");
     537    const char* _text = textBuffer.String();
     538
     539    size_t offset = 0;
     540    size_t n = 0;
     541    int16 count = 0;
     542    int16 length = strlen(_text);
     543    int16* spaces = NULL;
     544    int16 index = 0;
     545
     546    lineinfo* tempLine = NULL;
     547
     548    while ((n = strcspn(_text + offset, spacers)) < (length - offset)) {
     549        ++count;
     550        offset += n + 1;
     551    }
     552
     553    spaces = (int16*)calloc(count, sizeof(int16));
     554    offset = 0;
     555
     556    while ((n = strcspn(_text + offset, spacers)) < (length - offset)) {
     557        spaces[index++] = n + offset;
     558        offset += n + 1;
     559    }
     560
     561    offset = 0;
     562    float maxWidth = newMaxWidth - kEdgePadding - iconRight;
     563    bool wasNewline = false;
     564
     565    for (int32 i = 0; i < count; i++) {
     566        if (_text[spaces[i]] == '\n') {
     567            lineinfo *tempLine = new lineinfo;
     568            tempLine->text = "";
     569            tempLine->text.Append(_text + offset, spaces[i] - offset);
     570            wasNewline = true;
     571            tempLine->font = be_plain_font;
     572            tempLine->location = BPoint(iconRight + kEdgePadding, y);
     573            y += fontHeight;
     574           
     575            offset = spaces[i] + 1;
     576           
     577            fLines.push_front(tempLine);
     578        } else if (
     579            ((i+1 < count) && (StringWidth(_text + offset, spaces[i+1] - offset) > maxWidth)) ||
     580            ((i+1 == count) && (StringWidth(_text + offset) > maxWidth))
     581        )
     582
     583        tempLine = new lineinfo();
     584        tempLine->font = be_plain_font;
     585        if (!wasNewline)
     586            tempLine->location = BPoint(iconRight + (kEdgePadding * 2), y);
     587        else {
     588            tempLine->location = BPoint(iconRight, y);
     589            wasNewline = false;
     590        }
     591
     592        y += fontHeight;
     593        tempLine->text = "";
     594        tempLine->text.Append(_text + offset, spaces[i] - offset);
     595
     596        // Strip first space
     597        if ((tempLine->text.String())[0] == ' ')
     598            tempLine->text.RemoveFirst(" ");
     599
     600        fLines.push_front(tempLine);
     601
     602        offset = spaces[i];
     603    }
     604
     605    tempLine = new lineinfo();
     606    tempLine->text = "";
     607    tempLine->text.Append(_text + offset, strlen(_text) - offset);
     608    tempLine->font = be_plain_font;
     609    if (wasNewline)
     610        tempLine->location = BPoint(iconRight, y);
     611    else
     612        tempLine->location = BPoint(iconRight + (kEdgePadding * 2), y);
     613
     614    // Strip first space
     615    if ((tempLine->text.String())[0] == ' ')
     616        tempLine->text.RemoveFirst(" ");
     617
     618    free(spaces);       
     619
     620    fLines.push_front(tempLine);
     621
     622    fHeight = y + (kEdgePadding * 2);
     623
     624    // Make sure icon fits
     625    if (fBitmap) {
     626        lineinfo* appLine = fLines.back();
     627        font_height fh;
     628        appLine->font.GetHeight( &fh );
     629
     630        float title_bottom = fLines.back()->location.y + fh.descent;
     631
     632        if (fParent->Layout() == TitleAboveIcon) {
     633            if (fHeight < title_bottom + fBitmap->Bounds().Height() + 2 * kEdgePadding)
     634                fHeight = title_bottom + fBitmap->Bounds().Height() + 2 * kEdgePadding;
     635        } else {
     636            if (fHeight < fBitmap->Bounds().Height() + kEdgePadding * 2)
     637                fHeight = fBitmap->Bounds().Height() + kEdgePadding * 2;
     638        }
     639    }
     640
     641    BMessenger msgr(Parent());
     642    msgr.SendMessage(kResizeToFit);
     643}
     644
     645
     646bool
     647NotificationView::HasMessageID(const char* id)
     648{
     649    return fMessageID == id;
     650}
     651
     652
     653const char*
     654NotificationView::MessageID()
     655{
     656    return fMessageID.String();
     657}
     658
     659
     660void
     661NotificationView::SetPosition(bool first, bool last)
     662{
     663    fIsFirst = first;
     664    fIsLast = last;
     665}
     666
     667
     668BBitmap*
     669NotificationView::_ReadNodeIcon(const char* fileName, icon_size size)
     670{
     671    BEntry entry(fileName, true);
     672
     673    entry_ref ref;
     674    entry.GetRef(&ref);
     675
     676    BNode node(BPath(&ref).Path());
     677
     678    BBitmap* ret = new BBitmap(BRect(0, 0, (float)size - 1, (float)size - 1), B_RGBA32);
     679    if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute,
     680        kLargeIconAttribute, size, ret) != B_OK) {
     681        delete ret;
     682        ret = NULL;
     683    }
     684
     685    return ret;
     686}
     687
     688void
     689NotificationView::_LoadIcon()
     690{
     691    // First try to get the icon from the caller application
     692    app_info info;
     693    BMessenger msgr = fDetails->ReturnAddress();
     694
     695    if (msgr.IsValid())
     696        be_roster->GetRunningAppInfo(msgr.Team(), &info);
     697    else if (fType == B_PROGRESS_NOTIFICATION)
     698        be_roster->GetAppInfo("application/x-vnd.Haiku-notification_server",
     699            &info);
     700
     701    BPath path;
     702    path.SetTo(&info.ref);
     703
     704    fBitmap = _ReadNodeIcon(path.Path(), fParent->IconSize());
     705    if (fBitmap)
     706        return;
     707
     708    // If that failed get icons from app_server
     709    if (find_directory(B_BEOS_SERVERS_DIRECTORY, &path) != B_OK)
     710        return;
     711
     712    path.Append("app_server");
     713
     714    BFile file(path.Path(), B_READ_ONLY);
     715    if (file.InitCheck() != B_OK)
     716        return;
     717
     718    BResources res(&file);
     719    if (res.InitCheck() != B_OK)
     720        return;
     721
     722    // Which one should we choose?
     723    const char* iconName = "";
     724    switch (fType) {
     725        case B_INFORMATION_NOTIFICATION:
     726            iconName = "info";
     727            break;
     728        case B_ERROR_NOTIFICATION:
     729            iconName = "stop";
     730            break;
     731        case B_IMPORTANT_NOTIFICATION:
     732            iconName = "warn";
     733            break;
     734        default:
     735            return;
     736    }
     737
     738    // Allocate the bitmap
     739    fBitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1,
     740        (float)B_LARGE_ICON - 1), B_RGBA32);
     741    if (!fBitmap || fBitmap->InitCheck() != B_OK) {
     742        fBitmap = NULL;
     743        return;
     744    }
     745
     746    // Load raw icon data
     747    size_t size = 0;
     748    const uint8* data = (const uint8*)res.LoadResource(B_VECTOR_ICON_TYPE,
     749        iconName, &size);
     750    if ((data == NULL
     751        || BIconUtils::GetVectorIcon(data, size, fBitmap) != B_OK))
     752        fBitmap = NULL;
     753}
  • new file src/servers/notification/NotificationView.h

    diff --git a/src/servers/notification/NotificationView.h b/src/servers/notification/NotificationView.h
    new file mode 100644
    index 0000000..283b24d
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 */
     8#ifndef _NOTIFICATION_VIEW_H
     9#define _NOTIFICATION_VIEW_H
     10
     11#include <list>
     12
     13#include <Bitmap.h>
     14#include <Entry.h>
     15#include <Message.h>
     16#include <MessageRunner.h>
     17#include <MimeType.h>
     18#include <Notification.h>
     19#include <Path.h>
     20#include <Roster.h>
     21#include <String.h>
     22#include <TextView.h>
     23#include <View.h>
     24
     25class NotificationWindow;
     26
     27typedef struct line_struct {
     28    BFont font;
     29    BString text;
     30    BPoint location;
     31} lineinfo;
     32
     33typedef std::list<lineinfo*> vline;
     34
     35const uint32 kRemoveView = 'ReVi';
     36
     37class NotificationView : public BView {
     38public:
     39                            NotificationView(NotificationWindow* win, notification_type type,
     40                                const char* app, const char* title,
     41                                const char* text, BMessage* details);
     42                            ~NotificationView();
     43
     44    virtual void            AttachedToWindow();
     45    virtual void            MessageReceived(BMessage *msg);
     46    virtual void            GetPreferredSize(float* width, float* height);
     47    virtual void            Draw(BRect updateRect);
     48    virtual void            MouseDown(BPoint point);
     49    virtual void            FrameResized(float width, float height);
     50
     51    virtual BHandler*       ResolveSpecifier(BMessage* msg, int32 index,
     52                                BMessage* spec, int32 form, const char* prop);
     53    virtual status_t        GetSupportedSuites(BMessage* msg);
     54
     55            const char*     Application() const;
     56            const char*     Title() const;
     57            const char*     Text() const;
     58
     59            void            SetText(const char* app, const char* title,
     60                                const char* text, float newMaxWidth = -1);
     61            bool            HasMessageID(const char* id);
     62            const char*     MessageID();
     63            void            SetPosition(bool first, bool last);
     64
     65private:
     66    NotificationWindow*     fParent;
     67
     68    notification_type       fType;
     69    BMessageRunner*         fRunner;
     70    float                   fProgress;
     71    BString                 fMessageID;
     72
     73    BMessage*               fDetails;
     74    BBitmap*                fBitmap;
     75
     76    vline                   fLines;
     77
     78    BString                 fApp;
     79    BString                 fTitle;
     80    BString                 fText;
     81
     82    float                   fHeight;
     83
     84    bool                    fIsFirst;
     85    bool                    fIsLast;
     86
     87    BBitmap*                _ReadNodeIcon(const char* fileName, icon_size size);
     88    void                    _LoadIcon();
     89};
     90
     91#endif  // _NOTIFICATION_VIEW_H
  • new file src/servers/notification/NotificationWindow.cpp

    diff --git a/src/servers/notification/NotificationWindow.cpp b/src/servers/notification/NotificationWindow.cpp
    new file mode 100644
    index 0000000..74b54be
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 *
     8 * Authors:
     9 *      Michael Davidson, slaad@bong.com.au
     10 *      Mikael Eiman, mikael@eiman.tv
     11 *      Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
     12 */
     13
     14#include <algorithm>
     15
     16#include <Alert.h>
     17#include <Application.h>
     18#include <Debug.h>
     19#include <File.h>
     20#include <NodeMonitor.h>
     21#include <PropertyInfo.h>
     22
     23#include "AppGroupView.h"
     24#include "AppUsage.h"
     25#include "BorderView.h"
     26#include "NotificationWindow.h"
     27
     28property_info main_prop_list[] = {
     29    { "message", {B_GET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0}, "get a message"},
     30    { "message", {B_COUNT_PROPERTIES, 0}, {B_DIRECT_SPECIFIER, 0}, "count messages"},
     31    { "message", {B_CREATE_PROPERTY, 0}, {B_DIRECT_SPECIFIER, 0}, "create a message"},
     32    { "message", {B_SET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0 }, "modify a message" },
     33    0
     34};
     35
     36const float kCloseSize              = 8;
     37const float kExpandSize             = 8;
     38const float kPenSize                = 1;
     39const float kEdgePadding            = 5;
     40const float kSmallPadding           = 2;
     41
     42
     43NotificationWindow::NotificationWindow()
     44    :
     45    BWindow(BRect(10, 10, 30, 30), "Notification", B_BORDERED_WINDOW,
     46        B_AVOID_FRONT | B_AVOID_FOCUS | B_NOT_CLOSABLE | B_NOT_ZOOMABLE
     47            | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES)
     48{
     49    fBorder = new BorderView(Bounds(), "Notification");
     50
     51    AddChild(fBorder);
     52
     53    Show();
     54    Hide();
     55
     56    LoadSettings(true);
     57    LoadAppFilters(true);
     58}
     59
     60
     61NotificationWindow::~NotificationWindow()
     62{
     63    appfilter_t::iterator aIt;
     64    for (aIt = fAppFilters.begin(); aIt != fAppFilters.end(); aIt++)
     65        delete aIt->second;
     66}
     67
     68
     69bool
     70NotificationWindow::QuitRequested()
     71{
     72    appview_t::iterator aIt;
     73    for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
     74        aIt->second->RemoveSelf();
     75        delete aIt->second;
     76    }
     77
     78    BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
     79    return BWindow::QuitRequested();
     80}
     81
     82
     83void
     84NotificationWindow::WorkspaceActivated(int32 /*workspace*/, bool active)
     85{
     86    // Ensure window is in the correct position
     87    if (active)
     88        ResizeAll();
     89}
     90
     91
     92void
     93NotificationWindow::MessageReceived(BMessage* message)
     94{
     95    switch (message->what) {
     96        case B_NODE_MONITOR:
     97        {
     98            LoadSettings();
     99            LoadAppFilters();
     100            break;
     101        }
     102        case kResizeToFit:
     103            ResizeAll();
     104            break;
     105        case B_COUNT_PROPERTIES:
     106        {
     107            BMessage reply(B_REPLY);
     108            BMessage specifier;
     109            const char* property = NULL;
     110            bool messageOkay = true;
     111
     112            if (message->FindMessage("specifiers", 0, &specifier) != B_OK)
     113                messageOkay = false;
     114            if (specifier.FindString("property", &property) != B_OK)
     115                messageOkay = false;
     116            if (strcmp(property, "message") != 0)
     117                messageOkay = false;
     118
     119            if (messageOkay)
     120                reply.AddInt32("result", fViews.size());
     121            else {
     122                reply.what = B_MESSAGE_NOT_UNDERSTOOD;
     123                reply.AddInt32("error", B_ERROR);
     124            }
     125
     126            message->SendReply(&reply);
     127            break;
     128        }
     129        case B_CREATE_PROPERTY:
     130        case kNotificationMessage:
     131        {
     132            int32 type;
     133            const char* content = NULL;
     134            const char* title = NULL;
     135            const char* app = NULL;
     136            BMessage reply(B_REPLY);
     137            bool messageOkay = true;
     138
     139            if (message->FindInt32("type", &type) != B_OK)
     140                type = B_INFORMATION_NOTIFICATION;
     141            if (message->FindString("content", &content) != B_OK)
     142                messageOkay = false;
     143            if (message->FindString("title", &title) != B_OK)
     144                messageOkay = false;
     145            if (message->FindString("app", &app) != B_OK && message->FindString("appTitle", &app) != B_OK)
     146                messageOkay = false;
     147
     148            if (messageOkay) {
     149                NotificationView* view = new NotificationView(this, (notification_type)type, app,
     150                    title, content, new BMessage(*message));
     151
     152                appfilter_t::iterator fIt = fAppFilters.find(app);
     153                bool allow = false;
     154                if (fIt == fAppFilters.end()) {
     155                    app_info info;
     156                    BMessenger messenger = message->ReturnAddress();
     157                    if (messenger.IsValid())
     158                        be_roster->GetRunningAppInfo(messenger.Team(), &info);
     159                    else
     160                        be_roster->GetAppInfo("application/x-vnd.Be-SHEL", &info);
     161
     162                    AppUsage* appUsage = new AppUsage(info.ref, app, true);
     163                    fAppFilters[app] = appUsage;
     164
     165                    appUsage->Allowed(title, (notification_type)type);
     166
     167                    allow = true;
     168                } else
     169                    allow = fIt->second->Allowed(title, (notification_type)type);
     170
     171                if (allow) {
     172                    appview_t::iterator aIt = fAppViews.find(app);
     173                    AppGroupView *group = NULL;
     174                    if (aIt == fAppViews.end()) {
     175                        group = new AppGroupView(this, app);
     176                        fAppViews[app] = group;
     177                        fBorder->AddChild(group);
     178                    } else {
     179                        group = aIt->second;
     180                    };
     181                    group->AddInfo(view);
     182                       
     183                    ResizeAll();
     184                   
     185                    reply.AddInt32("error", B_OK);
     186                } else
     187                    reply.AddInt32("Error", B_ERROR);
     188            } else {
     189                reply.what = B_MESSAGE_NOT_UNDERSTOOD;
     190                reply.AddInt32("error", B_ERROR);
     191            }
     192
     193            message->SendReply(&reply);
     194            break;
     195        }
     196        case kRemoveView:
     197        {
     198            void* _ptr;
     199            message->FindPointer("view", &_ptr);
     200
     201            NotificationView* info
     202                = reinterpret_cast<NotificationView*>(_ptr);
     203
     204            fBorder->RemoveChild(info);
     205
     206            std::vector<NotificationView*>::iterator i
     207                = find(fViews.begin(), fViews.end(), info);
     208            if (i != fViews.end())
     209                fViews.erase(i);
     210
     211            delete info;
     212
     213            ResizeAll();
     214            break;
     215        }
     216        default:
     217            BWindow::MessageReceived(message);
     218    }
     219}
     220
     221
     222BHandler*
     223NotificationWindow::ResolveSpecifier(BMessage *msg, int32 index,
     224    BMessage *spec, int32 form, const char *prop)
     225{
     226    BPropertyInfo prop_info(main_prop_list);
     227    BHandler* handler = NULL;
     228
     229    if (strcmp(prop,"message") == 0) {
     230        switch (msg->what) {
     231            case B_CREATE_PROPERTY:
     232            {
     233                msg->PopSpecifier();
     234                handler = this;
     235                break;
     236            }
     237            case B_SET_PROPERTY:
     238            case B_GET_PROPERTY:
     239            {
     240                int32 i;
     241
     242                if (spec->FindInt32("index", &i) != B_OK)
     243                    i = -1;
     244
     245                if (i >= 0 && i < (int32)fViews.size()) {
     246                    msg->PopSpecifier();
     247                    handler = fViews[i];
     248                } else
     249                    handler = NULL;
     250                break;
     251            }
     252            case B_COUNT_PROPERTIES:
     253                msg->PopSpecifier();
     254                handler = this;
     255                break;
     256            default:
     257                break;
     258        }
     259    }
     260
     261    if (!handler)
     262        handler = BWindow::ResolveSpecifier(msg, index, spec, form, prop);
     263
     264    return handler;
     265}
     266
     267
     268icon_size
     269NotificationWindow::IconSize()
     270{
     271    return fIconSize;
     272}
     273
     274
     275int32
     276NotificationWindow::Timeout()
     277{
     278    return fTimeout;
     279}
     280
     281
     282
     283infoview_layout
     284NotificationWindow::Layout()
     285{
     286    return fLayout;
     287}
     288
     289
     290float
     291NotificationWindow::ViewWidth()
     292{
     293    return fWidth;
     294}
     295
     296
     297void
     298NotificationWindow::ResizeAll()
     299{
     300    if (fAppViews.empty()) {
     301        if (!IsHidden())
     302            Hide();
     303        return;
     304    }
     305
     306    appview_t::iterator aIt;
     307    bool shouldHide = true;
     308
     309    for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
     310        AppGroupView *app = aIt->second;
     311        if (app->HasChildren()) {
     312            shouldHide = false;
     313            break;
     314        }
     315    }
     316
     317    if (shouldHide) {
     318        if (!IsHidden())
     319            Hide();
     320        return;
     321    }
     322
     323    if (IsHidden())
     324        Show();
     325
     326    float width = 0;
     327    float height = 0;
     328
     329    for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
     330        AppGroupView* view = aIt->second;
     331        float w = -1;
     332        float h = -1;
     333
     334        if (!view->HasChildren()) {
     335            if (!view->IsHidden())
     336                view->Hide();
     337        } else {
     338            view->GetPreferredSize(&w, &h);
     339            width = max_c(width, h);
     340
     341            view->ResizeToPreferred();
     342            view->MoveTo(0, height);
     343
     344            height += h;
     345
     346            if (view->IsHidden())
     347                view->Show();
     348        }
     349    }
     350
     351    ResizeTo(ViewWidth(), height);
     352    PopupAnimation(Bounds().Width(), Bounds().Height());
     353}
     354
     355
     356void
     357NotificationWindow::PopupAnimation(float width, float height)
     358{
     359    float x = 0, y = 0, sx, sy;
     360    float pad = 0;
     361    BDeskbar deskbar;
     362    BRect frame = deskbar.Frame();
     363
     364    switch (deskbar.Location()) {
     365        case B_DESKBAR_TOP:
     366            // Put it just under, top right corner
     367            sx = frame.right;
     368            sy = frame.bottom+pad;
     369            y = sy;
     370            x = sx-width-pad;
     371            break;
     372        case B_DESKBAR_BOTTOM:
     373            // Put it just above, lower left corner
     374            sx = frame.right;
     375            sy = frame.top-height-pad;
     376            y = sy;
     377            x = sx - width-pad;
     378            break;
     379        case B_DESKBAR_LEFT_TOP:
     380            // Put it just to the right of the deskbar
     381            sx = frame.right+pad;
     382            sy = frame.top-height;
     383            x = sx;
     384            y = frame.top+pad;
     385            break;
     386        case B_DESKBAR_RIGHT_TOP:
     387            // Put it just to the left of the deskbar
     388            sx = frame.left-width-pad;
     389            sy = frame.top-height;
     390            x = sx;
     391            y = frame.top+pad;
     392            break;
     393        case B_DESKBAR_LEFT_BOTTOM:
     394            // Put it to the right of the deskbar.
     395            sx = frame.right+pad;
     396            sy = frame.bottom;
     397            x = sx;
     398            y = sy-height-pad;
     399            break;
     400        case B_DESKBAR_RIGHT_BOTTOM:
     401            // Put it to the left of the deskbar.
     402            sx = frame.left-width-pad;
     403            sy = frame.bottom;
     404            y = sy-height-pad;
     405            x = sx;
     406            break; 
     407        default:
     408            break;
     409    }
     410
     411    MoveTo(x, y);
     412   
     413    if (IsHidden() && fViews.size() != 0)
     414        Show();
     415    //Activate();// it hides floaters from apps :-(
     416}
     417
     418
     419void
     420NotificationWindow::LoadSettings(bool startMonitor)
     421{
     422    _LoadGeneralSettings(startMonitor);
     423    _LoadDisplaySettings(startMonitor);
     424}
     425
     426
     427void
     428NotificationWindow::LoadAppFilters(bool startMonitor)
     429{
     430    BPath path;
     431
     432    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     433        return;
     434
     435    path.Append(kSettingsDirectory);
     436
     437    if (create_directory(path.Path(), 0755) != B_OK)
     438        return;
     439
     440    path.Append(kFiltersSettings);
     441
     442    BFile file(path.Path(), B_READ_ONLY);
     443    BMessage settings;
     444    if (settings.Unflatten(&file) != B_OK)
     445        return;
     446
     447    type_code type;
     448    int32 count = 0;
     449
     450    if (settings.GetInfo("app_usage", &type, &count) != B_OK)
     451        return;
     452
     453    for (int32 i = 0; i < count; i++) {
     454        AppUsage* app = new AppUsage();
     455        settings.FindFlat("app_usage", i, app);
     456        fAppFilters[app->Name()] = app;
     457    }
     458
     459    if (startMonitor) {
     460        node_ref nref;
     461        BEntry entry(path.Path());
     462        entry.GetNodeRef(&nref);
     463
     464        if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
     465            BAlert* alert = new BAlert("", "Couldn't start filter "
     466                " monitor. Live filter changes disabled.", "Darn.");
     467            alert->Go();
     468        }
     469    }
     470}
     471
     472
     473void
     474NotificationWindow::SaveAppFilters()
     475{
     476    BPath path;
     477
     478    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     479        return;
     480
     481    path.Append(kSettingsDirectory);
     482    path.Append(kFiltersSettings);
     483
     484    BMessage settings;
     485    BFile file(path.Path(), B_WRITE_ONLY);
     486
     487    appfilter_t::iterator fIt;
     488    for (fIt = fAppFilters.begin(); fIt != fAppFilters.end(); fIt++)
     489        settings.AddFlat("app_usage", fIt->second);
     490
     491    settings.Flatten(&file);
     492}
     493
     494
     495void
     496NotificationWindow::_LoadGeneralSettings(bool startMonitor)
     497{
     498    BPath path;
     499    BMessage settings;
     500
     501    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     502        return;
     503
     504    path.Append(kSettingsDirectory);
     505    if (create_directory(path.Path(), 0755) == B_OK) {
     506        path.Append(kGeneralSettings);
     507
     508        BFile file(path.Path(), B_READ_ONLY);
     509        settings.Unflatten(&file);
     510    }
     511
     512    if (settings.FindInt32(kTimeoutName, &fTimeout) != B_OK)
     513        fTimeout = kDefaultTimeout;
     514
     515    // Notify the view about the change
     516    views_t::iterator it;
     517    for (it = fViews.begin(); it != fViews.end(); ++it) {
     518        NotificationView* view = (*it);
     519        view->SetText(view->Application(), view->Title(), view->Text());
     520        view->Invalidate();
     521    }
     522
     523    if (startMonitor) {
     524        node_ref nref;
     525        BEntry entry(path.Path());
     526        entry.GetNodeRef(&nref);
     527
     528        if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
     529            BAlert* alert = new BAlert("", "Couldn't start general settings "
     530                " monitor.\nLive filter changes disabled.", "OK");
     531            alert->Go();
     532        }
     533    }
     534}
     535
     536
     537void
     538NotificationWindow::_LoadDisplaySettings(bool startMonitor)
     539{
     540    BPath path;
     541    BMessage settings;
     542
     543    if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
     544        return;
     545
     546    path.Append(kSettingsDirectory);
     547    if (create_directory(path.Path(), 0755) == B_OK) {
     548        path.Append(kDisplaySettings);
     549
     550        BFile file(path.Path(), B_READ_ONLY);
     551        settings.Unflatten(&file);
     552    }
     553
     554    int32 setting;
     555
     556    if (settings.FindFloat(kWidthName, &fWidth) != B_OK)
     557        fWidth = kDefaultWidth;
     558
     559    if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
     560        fIconSize = kDefaultIconSize;
     561    else
     562        fIconSize = (icon_size)setting;
     563
     564    if (settings.FindInt32(kLayoutName, &setting) != B_OK)
     565        fLayout = kDefaultLayout;
     566    else {
     567        switch (setting) {
     568            case 0:
     569                fLayout = TitleAboveIcon;
     570                break;
     571            case 1:
     572                fLayout = AllTextRightOfIcon;
     573                break;
     574            default:
     575                fLayout = kDefaultLayout;
     576        }
     577    }
     578
     579    // Notify the view about the change
     580    views_t::iterator it;
     581    for (it = fViews.begin(); it != fViews.end(); ++it) {
     582        NotificationView* view = (*it);
     583        view->SetText(view->Application(), view->Title(), view->Text());
     584        view->Invalidate();
     585    }
     586
     587    if (startMonitor) {
     588        node_ref nref;
     589        BEntry entry(path.Path());
     590        entry.GetNodeRef(&nref);
     591
     592        if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
     593            BAlert* alert = new BAlert("", "Couldn't start display settings "
     594                " monitor.\nLive filter changes disabled.", "OK");
     595            alert->Go();
     596        }
     597    }
     598}
  • new file src/servers/notification/NotificationWindow.h

    diff --git a/src/servers/notification/NotificationWindow.h b/src/servers/notification/NotificationWindow.h
    new file mode 100644
    index 0000000..a246f7e
    - +  
     1/*
     2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
     3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
     4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
     5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
     6 * Distributed under the terms of the MIT License.
     7 */
     8#ifndef _NOTIFICATION_WINDOW_H
     9#define _NOTIFICATION_WINDOW_H
     10
     11#include <cmath>
     12#include <vector>
     13#include <map>
     14
     15#include <Directory.h>
     16#include <Deskbar.h>
     17#include <Entry.h>
     18#include <FindDirectory.h>
     19#include <Message.h>
     20#include <Notifications.h>
     21#include <PropertyInfo.h>
     22#include <String.h>
     23#include <Window.h>
     24
     25#include "NotificationView.h"
     26
     27class AppGroupView;
     28class AppUsage;
     29class BorderView;
     30class SettingsFile;
     31
     32typedef std::map<BString, AppGroupView*> appview_t;
     33typedef std::map<BString, AppUsage*> appfilter_t;
     34typedef std::vector<NotificationView*> views_t;
     35
     36extern const float kEdgePadding;
     37extern const float kSmallPadding;
     38extern const float kCloseSize;
     39extern const float kExpandSize;
     40extern const float kPenSize;
     41
     42const uint32 kResizeToFit = 'IWrf';
     43
     44class NotificationWindow : public BWindow {
     45public:
     46                                    NotificationWindow();
     47    virtual                         ~NotificationWindow();
     48
     49    virtual bool                    QuitRequested();
     50    virtual void                    MessageReceived(BMessage*);
     51    virtual void                    WorkspaceActivated(int32, bool);
     52    virtual BHandler*               ResolveSpecifier(BMessage*, int32, BMessage*,
     53                                        int32, const char*);
     54
     55            icon_size               IconSize();
     56            int32                   Timeout();
     57            infoview_layout         Layout();
     58            float                   ViewWidth();
     59
     60            void                    ResizeAll();
     61
     62private:
     63    friend class AppGroupView;
     64
     65            void                    PopupAnimation(float, float);
     66            void                    LoadSettings(bool startMonitor = false);
     67            void                    LoadAppFilters(bool startMonitor = false);
     68            void                    SaveAppFilters();
     69            void                    _LoadGeneralSettings(bool startMonitor);
     70            void                    _LoadDisplaySettings(bool startMonitor);
     71
     72            views_t                 fViews;
     73            BorderView*             fBorder;
     74
     75            appview_t               fAppViews;
     76
     77            BString                 fStatusText;
     78            BString                 fMessageText;
     79
     80            float                   fWidth;
     81            icon_size               fIconSize;
     82            int32                   fTimeout;
     83            infoview_layout         fLayout;
     84
     85            appfilter_t             fAppFilters;
     86};
     87
     88extern property_info main_prop_list[];
     89
     90#endif  // _NOTIFICATION_WINDOW_H
  • new file src/servers/notification/notification_server.rdef

    diff --git a/src/servers/notification/notification_server.rdef b/src/servers/notification/notification_server.rdef
    new file mode 100644
    index 0000000..feeaf4a
    - +  
     1
     2resource app_signature "application/x-vnd.Haiku-notification_server";
     3
     4resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
     5
     6resource app_version {
     7    major  = 1,
     8    middle = 0,
     9    minor  = 0,
     10
     11    variety = B_APPV_ALPHA,
     12    internal = 0,
     13
     14    short_info = "notification_server",
     15    long_info = "notification_server ©2010 Haiku Inc."
     16};
     17
     18resource vector_icon {
     19    $"6E6369661201B0AEB6330200160734E2E73823CEB823CE34E2E74AE8E44C3C6C"
     20    $"000020054715732EA251D37EE38F03B89B9B0200160734000B36F73FB6F73F34"
     21    $"000B4AF7984C1AC8000020054715732EA251D37EE38F039F9FAA0200160730F6"
     22    $"C6343295B4329530F6C64B14D64C04C5000020054715732EA251D37EE38F0200"
     23    $"16073104ED343D08B43D083104ED4B14734C02F8000020054715732EA251D37E"
     24    $"E38F039F9FBD020116023C322F0000000000003C322F4B471E4AA0B10000FF8F"
     25    $"0500020116073C263B0000000000003C263B4AA2864A01D4000020054715732E"
     26    $"A251D37EE38F039997AE0312031903B0AEB603292F3D0200160736FA5E3A3533"
     27    $"BA353336FA5E4945B24A4CD9000020054715732EA251D37EE38F039B9C9F0400"
     28    $"19140204CAC5CED7CAC5CE14CAC5CF99BF00D037CB4CD062B85FD02020CED720"
     29    $"CF9920CE14BF00CD76B85FCD76C5A0CD76060B7FF413CACFCF60CACFCF60CACF"
     30    $"CF5FCAD0CF5CCAD0CF5DCAD0CF5ACACFCF57CACFCF58CACFCF57CE42C3A0CF66"
     31    $"C3A2CF66C3A2CF66C3B8CFDAC738D037C54AD037C926D037CACECF66CAB8CFDA"
     32    $"CACECF66CACFCF600204C738CD66C535CD66C93BCD66CAD0CE42CAD0CDC7CAD0"
     33    $"CEBDC738CF1DC93BCF1DC535CF1DC3A0CE42C3A0CEBDC3A0CDC7060847FFC914"
     34    $"CDEAC914CDECC914CDEACCCCC514CDEBC515CDEBC515CDEBC515CDEDC514CDF1"
     35    $"C514CDEFC514CE31C714CE65C5F9CE65C830CE65C915CDF1C915CE31C915CDEF"
     36    $"0204C714CC50C5F5CC50C833CC50C915CCC4C915CC83C915CD05C714CD39C833"
     37    $"CD39C5F5CD39C514CCC4C514CD05C514CC830204C714CC51C668CC51C7C0CC51"
     38    $"C848CC97C848CC70C848CCBEC714CCDCC7C0CCDCC668CCDCC5E1CC97C5E1CCBE"
     39    $"C5E1CC700A04C5E1CC67C847CC67C847CC97C5E1CC970204C714CC26C668CC26"
     40    $"C7C0CC26C848CC6BC848CC44C848CC92C714CCB1C7C0CCB1C668CCB1C5E1CC6B"
     41    $"C5E1CC92C5E1CC440611FEFFEDFF02C394BADFC73EBD9CC613BD06C869BE32CB"
     42    $"07BFE3CA26BF02CBE7C0C3CC45C2CFCC45C20ACC45C394CB56C5A4CC23C4D7CA"
     43    $"89C671CA0BC6CFCAD0C651C946C74DC82EC865C8A0C7A4C7BCC926C77DCBC4C7"
     44    $"7DCA4AC77DCD3ECC61C70CCC7DC762CC7DC6B6CC7DC6A8CC61C681C926C681C9"
     45    $"F3C681C859C77DC6DFC6FBC761C7FFC65DC9E4C394CA37C503C991C22AC852C2"
     46    $"A8C8F7C271C7ACC2DFC56DC42AC61A4AC4C0C498C385C56D0204C2A9B8AAC14F"
     47    $"B8AAC403B8AAC513BFF8C513BBE1C513C40FC2A9C746C403C746C14FC746C03F"
     48    $"BFF8C03FC40FC03FBBE10204C298B8E9C15BB8E9C3D6B8E9C4D0BFF8C4D0BC04"
     49    $"C4D0C3ECC298C706C3D6C706C15BC706C061BFF8C061C3ECC061BC040607FF3E"
     50    $"CAFCC32ACB30C35DCAFCC32ACA65C4DBCAF7C449C9D4C56DC936C5F5C990C5AB"
     51    $"C8DBC640C80EC6FEC864C680C7D3C754C77DC875C90BC698C7FCC73EC9A2C63B"
     52    $"CB36C4BCCB04C529CB86C40D0205C517C3A4C517C369C517C3A4C817C271C78A"
     53    $"C29EC8A8C242C9EBC27CC938C1F7CA40C2BCCA3EC1E7CAADC23EC9BAC17EC6E7"
     54    $"C23DC87AC199C5B8C2BA0608FEFAC26DB961C2EBBA45C29CB9CFC33ABABBC41A"
     55    $"BCDBC3EFBAB7C446BEFEC3F7C39CC4A0C0FEC34EC63AC30BC67DC31EC690C491"
     56    $"BE5DC4FFC3D7C43DBA33C2D7B95DC379B994C236B9260205C186BD52C1BABCA9"
     57    $"C186BD52C14BC116C12ABF53C16CC2D9C254C45BC1CAC46BC2DDC44BC2DDC2AC"
     58    $"C33FC335C27BC222C1B3C0E5C1B3C1EEC1B3BFDD0205C0EFBD9CC12EBCE8C0EF"
     59    $"BD9CC0A8C19DC081BFBEC0CFC37CC1E7C516C142C528C28DC505C28DC34CC303"
     60    $"C3DEC216C2BAC126C169C126C282C126C0500205C2B8C588C2D2C5A2C2B8C588"
     61    $"C252C68CC252C655C252C6C3C2A4C6D7C281C6D7C2C8C6D7C2F9C6A6C316C6C3"
     62    $"C2DBC688C2ACC680C2ACC6A4C2ACC65D0207B7C920B7C920B8BD20B8BDB4A4B8"
     63    $"C8B393B8BDB4A4B857BC43B857BC43B84BBD0AB735BDAFB81EBDAFB64CBDAFB6"
     64    $"13BC43B61EBD0AB613BC43B5ACB4A4B5ACB4A4B59BB393B6A720B5B220B6A720"
     65    $"0208B77FC12CB77FC12CB857C12CB8BDC270B8BDC198B8BDC270B8BDC304B8BD"
     66    $"C304B8BDC3DCB77FC448B857C448B77FC448B6EBC448B6EBC448B613C448B5AC"
     67    $"C304B5ACC3DCB5ACC304B5ACC270B5ACC270B5ACC198B6EBC12CB613C12CB6EB"
     68    $"C12C0204B72ACD68B577CD68B8DDCD68BA33CDE5BA33CD9FBA33CE2CB72ACE63"
     69    $"B8DDCE63B577CE63B421CDE5B421CE2CB421CD9F130A00010002400000000000"
     70    $"0000003F71070000003D00000A010101024000000000000000003F7107000000"
     71    $"3D00000A020102024000000000000000003F71070000003D00000A0301030240"
     72    $"00000000000000003F71070000003D00000A040104024000000000000000003F"
     73    $"71070000003D00000A050105024000000000000000003F71070000003D00000A"
     74    $"060106024000000000000000003F71070000003D00000A070107024000000000"
     75    $"000000003F71070000003D00000A080108024000000000000000003F71070000"
     76    $"003D00000A090109024000000000000000003F71070000003D00000A0A010A02"
     77    $"4000000000000000003F71070000003D00000A0B010B02400000000000000000"
     78    $"3F71070000003D00000A0C010C024000000000000000003F71070000003D0000"
     79    $"0A0D010D024000000000000000003F71070000003D00000A0E010E0240000000"
     80    $"00000000003F71070000003D00000A0F010F024000000000000000003F710700"
     81    $"00003D00000A100110024000000000000000003F71070000003D00000A090211"
     82    $"12024000000000000000003F71070000003D00000A1101130240000000000000"
     83    $"00003F71070000003D0000"
     84};