root/src/apps/expander/PasswordAlert.cpp
/*
 * Copyright 2003-2010 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Jérôme Duval
 */


#include "PasswordAlert.h"

#include <string.h>

#include <File.h>
#include <FindDirectory.h>
#include <IconUtils.h>
#include <Path.h>
#include <Resources.h>
#include <Screen.h>
#include <View.h>


static const int kWindowIconOffset = 27;
static const int kIconStripeWidth = 30;
static const int kTextIconOffset = kWindowIconOffset + kIconStripeWidth - 2;
static const int kTextTopOffset = 6;
static const int kSemTimeOut = 50000;


class TAlertView : public BView {
public:
                                                        TAlertView(BRect frame);
                                                        TAlertView(BMessage* archive);
                                                        ~TAlertView();

        virtual void                    Draw(BRect updateRect);

                        void                    SetBitmap(BBitmap* Icon) { fIconBitmap = Icon; }
                        BBitmap*                Bitmap() { return fIconBitmap; }

private:
                        BBitmap*                fIconBitmap;
};


//      #pragma mark - PasswordAlert


PasswordAlert::PasswordAlert(const char* title, const char* text)
        :
        BWindow(BRect(0, 0, 450, 45), title, B_MODAL_WINDOW,
                B_NOT_CLOSABLE | B_NOT_RESIZABLE),
        fTextControl(NULL),
        fAlertSem(-1)
{
        // Set up the "_master_" view
        TAlertView* masterView = new TAlertView(Bounds());
        masterView->SetBitmap(InitIcon());
        AddChild(masterView);

        // Set up the text view
        BRect textControlRect(kTextIconOffset, kTextTopOffset,
                Bounds().right - 5, Bounds().bottom);

        fTextControl = new BTextControl(textControlRect, "_password_", text,
                NULL, new BMessage('pass'), B_FOLLOW_ALL);
        fTextControl->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
        fTextControl->TextView()->HideTyping(true);
        fTextControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
        fTextControl->SetDivider(10 + fTextControl->StringWidth(text));

        masterView->AddChild(fTextControl);

        BRect screenFrame = BScreen(B_MAIN_SCREEN_ID).Frame();
        BPoint point;
        point.x = screenFrame.Width() / 2 - Bounds().Width() / 2;
        point.y = screenFrame.Height() / 2 - Bounds().Height() / 2;
        if (screenFrame.Contains(point))
                MoveTo(point);

        fTextControl->MakeFocus();
}


PasswordAlert::~PasswordAlert()
{
}


BBitmap*
PasswordAlert::InitIcon()
{
        // The alert icons are in the app_server resources
        BBitmap* icon = NULL;
        BPath path;
        if (find_directory(B_BEOS_SERVERS_DIRECTORY, &path) == B_OK) {
                path.Append("app_server");
                BFile file;
                if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK) {
                        BResources resources;
                        if (resources.SetTo(&file) == B_OK) {
                                // Which icon are we trying to load?
                                const char* iconName = "warn";

                                // Load the raw icon data
                                size_t size;
                                const void* rawIcon
                                        = resources.LoadResource(B_VECTOR_ICON_TYPE, iconName,
                                                &size);

                                if (rawIcon != NULL) {
                                        // Now build the bitmap
                                        icon = new BBitmap(BRect(0, 0, 31, 31), B_RGBA32);
                                        if (BIconUtils::GetVectorIcon((const uint8*)rawIcon, size,
                                                        icon) != B_OK) {
                                                delete icon;
                                                return NULL;
                                        }
                                }
                        }
                }
        }

        return icon;
}


void
PasswordAlert::Go(BString& password)
{
        fAlertSem = create_sem(0, "AlertSem");
        if (fAlertSem < B_OK) {
                Quit();
                return;
        }

        // Get the originating window, if it exists
        BWindow* window
                = dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));

        Show();

        // Heavily modified from TextEntryAlert code; the original didn't let the
        // blocked window ever draw.
        if (window != NULL) {
                status_t result;
                for (;;) {
                        do {
                                result = acquire_sem_etc(fAlertSem, 1, B_RELATIVE_TIMEOUT,
                                        kSemTimeOut);
                                // We've (probably) had our time slice taken away from us
                        } while (result == B_INTERRUPTED);

                        if (result == B_BAD_SEM_ID) {
                                // Semaphore was finally nuked in MessageReceived
                                break;
                        }
                        window->UpdateIfNeeded();
                }
        } else {
                // No window to update, so just hang out until we're done.
                while (acquire_sem(fAlertSem) == B_INTERRUPTED) {
                        ;
                }
        }

        // Have to cache the value since we delete on Quit()
        password = fTextControl->Text();
        if (Lock())
                Quit();
}


void
PasswordAlert::MessageReceived(BMessage* msg)
{
        if (msg->what != 'pass')
                return BWindow::MessageReceived(msg);

        delete_sem(fAlertSem);
        fAlertSem = -1;
}


//      #pragma mark - TAlertView


TAlertView::TAlertView(BRect frame)
        :
        BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
        fIconBitmap(NULL)
{
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}


TAlertView::~TAlertView()
{
        delete fIconBitmap;
}


void
TAlertView::Draw(BRect updateRect)
{
        // Here's the fun stuff
        if (fIconBitmap != NULL) {
                BRect stripeRect = Bounds();
                stripeRect.right = kIconStripeWidth;
                SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
                FillRect(stripeRect);

                SetDrawingMode(B_OP_ALPHA);
                DrawBitmapAsync(fIconBitmap, BPoint(18, 6));
                SetDrawingMode(B_OP_COPY);
        }
}