root/src/apps/cortex/ParameterView/ParameterWindow.cpp
/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


// ParameterWindow.cpp

#include "ParameterWindow.h"
// ParameterWindow
#include "ParameterContainerView.h"

// Application Kit
#include <Message.h>
#include <Messenger.h>
// Interface Kit
#include <Alert.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Screen.h>
#include <ScrollBar.h>
// Media Kit
#include <MediaRoster.h>
#include <MediaTheme.h>
#include <ParameterWeb.h>
// Storage Kit
#include <FilePanel.h>
#include <Path.h>
// Support Kit
#include <String.h>
// Locale Kit
#undef B_CATALOG
#define B_CATALOG (&sCatalog)
#include <Catalog.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ParameterWindow"

__USE_CORTEX_NAMESPACE

#include <Debug.h>
#define D_ALLOC(x) //PRINT (x)
#define D_HOOK(x) //PRINT (x)
#define D_INTERNAL(x) //PRINT (x)
#define D_MESSAGE(x) //PRINT (x)

static BCatalog sCatalog("x-vnd.Cortex.ParameterView");

// -------------------------------------------------------- //
// ctor/dtor
// -------------------------------------------------------- //

ParameterWindow::ParameterWindow(
        BPoint position,
        live_node_info &nodeInfo,
        BMessenger *notifyTarget)
        : BWindow(BRect(position, position + BPoint(50.0, 50.0)),
                          "parameters", B_DOCUMENT_WINDOW,
                          B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS),
          m_node(nodeInfo.node),
          m_parameters(0),
          m_notifyTarget(0),
          m_zoomed(false),
          m_zooming(false) {
        D_ALLOC(("ParameterWindow::ParameterWindow()\n"));

        // add the nodes name to the title
        {
                BString title = B_TRANSLATE("%nodeinfo% parameters");
                title.ReplaceFirst("%nodeinfo%", nodeInfo.name);
                SetTitle(title);
        }
        // add the menu bar
        BMenuBar *menuBar = new BMenuBar(Bounds(), "ParameterWindow MenuBar");

        BMenu *menu = new BMenu(B_TRANSLATE("Window"));
        menu->AddItem(new BMenuItem(B_TRANSLATE("Start control panel"),
                                                                new BMessage(M_START_CONTROL_PANEL),
                                                                'P', B_COMMAND_KEY | B_SHIFT_KEY));
        menu->AddSeparatorItem();
        menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
                                                                new BMessage(B_QUIT_REQUESTED),
                                                                'W', B_COMMAND_KEY));
        menuBar->AddItem(menu);

        // future Media Theme selection capabilities go here
        menu = new BMenu(B_TRANSLATE("Themes"));
        BMessage *message = new BMessage(M_THEME_SELECTED);
        BMediaTheme *theme = BMediaTheme::PreferredTheme();
        message->AddInt32("themeID", theme->ID());
        BMenuItem *item = new BMenuItem(theme->Name(), message);
        item->SetMarked(true);
        menu->AddItem(item);
        menuBar->AddItem(menu);
        AddChild(menuBar);

        _updateParameterView();
        _init();

        // start watching for parameter web changes
        BMediaRoster *roster = BMediaRoster::CurrentRoster();
        roster->StartWatching(this, nodeInfo.node, B_MEDIA_WILDCARD);

        if (notifyTarget) {
                m_notifyTarget = new BMessenger(*notifyTarget);
        }
}

ParameterWindow::~ParameterWindow() {
        D_ALLOC(("ParameterWindow::~ParameterWindow()\n"));

        if (m_notifyTarget) {
                delete m_notifyTarget;
        }
}

// -------------------------------------------------------- //
// BWindow impl
// -------------------------------------------------------- //

void ParameterWindow::FrameResized(
        float width,
        float height) {
        D_HOOK(("ParameterWindow::FrameResized()\n"));

        if (!m_zooming) {
                m_zoomed = false;
        }
        else {
                m_zooming = false;
        }
}

void ParameterWindow::MessageReceived(
        BMessage *message) {
        D_MESSAGE(("ParameterWindow::MessageReceived()\n"));

        switch (message->what) {
                case M_START_CONTROL_PANEL: {
                        D_MESSAGE((" -> M_START_CONTROL_PANEL\n"));
                        status_t error = _startControlPanel();
                        if (error) {
                                BString s = B_TRANSLATE(
                                        "Could not start control panel (%error%)");
                                s.ReplaceFirst("%error%", strerror(error));
                                BAlert *alert = new BAlert("", s.String(), B_TRANSLATE("OK"),
                                        0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
                                alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
                                alert->Go(0);
                        }
                        bool replace = false;
                        if ((message->FindBool("replace", &replace) == B_OK)
                         && (replace == true)) {
                                PostMessage(B_QUIT_REQUESTED);
                        }
                        break;
                }
                case M_THEME_SELECTED: {
                        D_MESSAGE((" -> M_THEME_SELECTED\n"));
                        int32 themeID;
                        if (message->FindInt32("themeID", &themeID) != B_OK) {
                                return;
                        }
                        // not yet implemented
                        break;
                }
                case B_MEDIA_WEB_CHANGED: {
                        D_MESSAGE((" -> B_MEDIA_WEB_CHANGED\n"));
                        _updateParameterView();
                        _constrainToScreen();
                        break;
                }
                default: {
                        BWindow::MessageReceived(message);
                }
        }
}

bool ParameterWindow::QuitRequested() {
        D_HOOK(("ParameterWindow::QuitRequested()\n"));

        // stop watching the MediaRoster
        BMediaRoster *roster = BMediaRoster::CurrentRoster();
        if (roster)     {
                roster->StopWatching(this, m_node, B_MEDIA_WILDCARD);
        }

        // tell the notification target to forget about us
        if (m_notifyTarget && m_notifyTarget->IsValid()) {
                BMessage message(M_CLOSED);
                message.AddInt32("nodeID", m_node.node);
                status_t error = m_notifyTarget->SendMessage(&message);
                if (error) {
                        D_HOOK((" -> error sending message (%s) !\n", strerror(error)));
                }
        }

        return true;
}

void ParameterWindow::Zoom(
        BPoint origin,
        float width,
        float height) {
        D_HOOK(("ParameterWindow::Zoom()\n"));

        m_zooming = true;

        BScreen screen(this);
        if (!screen.Frame().Contains(Frame())) {
                m_zoomed = false;
        }

        if (!m_zoomed) {
                // resize to the ideal size
                m_manualSize = Bounds();
                ResizeTo(m_idealSize.Width(), m_idealSize.Height());
                _constrainToScreen();
                m_zoomed = true;
        }
        else {
                // resize to the most recent manual size
                ResizeTo(m_manualSize.Width(), m_manualSize.Height());
                m_zoomed = false;
        }
}

// -------------------------------------------------------- //
// internal operations
// -------------------------------------------------------- //

void ParameterWindow::_init() {
        D_INTERNAL(("ParameterWindow::_init()\n"));

        // offset to a new position
        _constrainToScreen();
        m_manualSize = Bounds().OffsetToCopy(0.0, 0.0);

        // add the hidden option to close this window when the
        // control panel has been started successfully
        BMessage *message = new BMessage(M_START_CONTROL_PANEL);
        message->AddBool("replace", true);
        AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY | B_OPTION_KEY,
                                message);
}

void ParameterWindow::_updateParameterView(
        BMediaTheme *theme) {
        D_INTERNAL(("ParameterWindow::_updateParameterView()\n"));

        // clear the old version
        if (m_parameters) {
                ParameterContainerView *view = dynamic_cast<ParameterContainerView *>(FindView("ParameterContainerView"));
                RemoveChild(view);
                delete m_parameters;
                m_parameters = 0;
                delete view;
        }

        // fetch ParameterWeb from the MediaRoster
        BMediaRoster *roster = BMediaRoster::CurrentRoster();
        if (roster) {
                BParameterWeb *web;
                status_t error = roster->GetParameterWebFor(m_node, &web);
                if (!error && (web->CountParameters() || web->CountGroups())) {
                        // if no theme was specified, use the preferred theme
                        if (!theme) {
                                theme = BMediaTheme::PreferredTheme();
                        }
                        // acquire the view
                        m_parameters = BMediaTheme::ViewFor(web, 0, theme);
                        if (m_parameters) {
                                BMenuBar *menuBar = KeyMenuBar();
                                m_idealSize = m_parameters->Bounds();
                                m_idealSize.right += B_V_SCROLL_BAR_WIDTH;
                                m_idealSize.bottom += B_H_SCROLL_BAR_HEIGHT;
                                if (menuBar) {
                                        m_parameters->MoveTo(0.0, menuBar->Bounds().bottom + 1.0);
                                        m_idealSize.bottom += menuBar->Bounds().bottom + 1.0;
                                }
                        }
                }
        }

        // limit min size to avoid funny-looking scrollbars
        float hMin = B_V_SCROLL_BAR_WIDTH*6, vMin = B_H_SCROLL_BAR_HEIGHT*3;
        // limit max size to full extents of the parameter view
        float hMax = m_idealSize.Width(), vMax = m_idealSize.Height();
        SetSizeLimits(hMin, hMax, vMin, vMax);

        // adapt the window to the new dimensions
        ResizeTo(m_idealSize.Width(), m_idealSize.Height());
        m_zoomed = true;

        if (m_parameters) {
                BRect paramRect = m_parameters->Bounds();
                AddChild(new ParameterContainerView(paramRect, m_parameters));
        }
}

void ParameterWindow::_constrainToScreen() {
        D_INTERNAL(("ParameterWindow::_constrainToScreen()\n"));

        BScreen screen(this);
        BRect screenRect = screen.Frame();
        BRect windowRect = Frame();

        // if the window is outside the screen rect
        // move it to the default position
        if (!screenRect.Intersects(windowRect)) {
                windowRect.OffsetTo(screenRect.LeftTop());
                MoveTo(windowRect.LeftTop());
                windowRect = Frame();
        }

        // if the window is larger than the screen rect
        // resize it to fit at each side
        if (!screenRect.Contains(windowRect)) {
                if (windowRect.left < screenRect.left) {
                        windowRect.left = screenRect.left + 5.0;
                        MoveTo(windowRect.LeftTop());
                        windowRect = Frame();
                }
                if (windowRect.top < screenRect.top) {
                        windowRect.top = screenRect.top + 5.0;
                        MoveTo(windowRect.LeftTop());
                        windowRect = Frame();
                }
                if (windowRect.right > screenRect.right) {
                        windowRect.right = screenRect.right - 5.0;
                }
                if (windowRect.bottom > screenRect.bottom) {
                        windowRect.bottom = screenRect.bottom - 5.0;
                }
                ResizeTo(windowRect.Width(), windowRect.Height());
        }
}

status_t ParameterWindow::_startControlPanel() {
        D_INTERNAL(("ParameterWindow::_startControlPanel()\n"));

        // get roster instance
        BMediaRoster *roster = BMediaRoster::CurrentRoster();
        if (!roster) {
                D_INTERNAL((" -> MediaRoster not available\n"));
                return B_ERROR;
        }

        // try to StartControlPanel()
        BMessenger messenger;
        status_t error = roster->StartControlPanel(m_node, &messenger);
        if (error) {
                D_INTERNAL((" -> StartControlPanel() failed (%s)\n", strerror(error)));
                return error;
        }

        // notify the notification target, if any
        if (m_notifyTarget) {
                BMessage message(M_CONTROL_PANEL_STARTED);
                message.AddInt32("nodeID", m_node.node);
                message.AddMessenger("messenger", messenger);
                error = m_notifyTarget->SendMessage(&message);
                if (error) {
                        D_INTERNAL((" -> failed to notify target\n"));
                }
        }

        return B_OK;
}

// END -- ParameterWindow.cpp --