* 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.
*/
#include "RouteApp.h"
#include "RouteWindow.h"
#include "MediaRoutingView.h"
#include "StatusView.h"
#include "DormantNodeWindow.h"
#include "TransportWindow.h"
#include "RouteAppNodeManager.h"
#include "NodeGroup.h"
#include "TipManager.h"
#include <Alert.h>
#include <Autolock.h>
#include <Debug.h>
#include <Font.h>
#include <MenuBar.h>
#include <Menu.h>
#include <MenuItem.h>
#include <Message.h>
#include <Messenger.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <StringView.h>
#include <algorithm>
#define D_HOOK(x)
#define D_INTERNAL(x)
#include <Catalog.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "CortexRouteApp"
__USE_CORTEX_NAMESPACE
const char* const RouteWindow::s_windowName = B_TRANSLATE("Cortex");
const BRect RouteWindow::s_initFrame(100,100,700,550);
const char* const g_aboutText =
B_TRANSLATE("Cortex/Route 2.1.2\n\n"
"Copyright 1999-2000 Eric Moon\n"
"All rights reserved.\n\n"
"The Cortex Team:\n\n"
"Christopher Lenz: UI\n"
"Eric Moon: UI, back-end\n\n"
"Thanks to:\nJohn Ashmun\nJon Watte\nDoug Wright\n<your name here>\n\n"
"Certain icons used herein are the property of\n"
"Be, Inc. and are used by permission.");
RouteWindow::~RouteWindow()
{
}
RouteWindow::RouteWindow(RouteAppNodeManager* manager)
:
BWindow(s_initFrame, s_windowName, B_DOCUMENT_WINDOW, 0),
m_hScrollBar(0),
m_vScrollBar(0),
m_transportWindow(0),
m_dormantNodeWindow(0),
m_selectedGroupID(0),
m_zoomed(false),
m_zooming(false)
{
BRect b = Bounds();
BMenuBar* pMenuBar = new BMenuBar(b, "menuBar");
BMenu* pFileMenu = new BMenu(B_TRANSLATE("File"));
BMenuItem* item = new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS),
new BMessage(RouteApp::M_SHOW_OPEN_PANEL), 'O');
item->SetTarget(be_app);
pFileMenu->AddItem(item);
pFileMenu->AddItem(new BSeparatorItem());
item = new BMenuItem(B_TRANSLATE("Save nodes" B_UTF8_ELLIPSIS),
new BMessage(RouteApp::M_SHOW_SAVE_PANEL), 'S');
item->SetTarget(be_app);
pFileMenu->AddItem(item);
pFileMenu->AddItem(new BSeparatorItem());
pFileMenu->AddItem(new BMenuItem(
B_TRANSLATE("About Cortex/Route" B_UTF8_ELLIPSIS),
new BMessage(B_ABOUT_REQUESTED)));
pFileMenu->AddItem(new BSeparatorItem());
pFileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
new BMessage(B_QUIT_REQUESTED)));
pMenuBar->AddItem(pFileMenu);
AddChild(pMenuBar);
BRect rvBounds = b;
rvBounds.top = pMenuBar->Frame().bottom+1;
rvBounds.right -= B_V_SCROLL_BAR_WIDTH;
rvBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
m_routingView = new MediaRoutingView(manager, rvBounds, "routingView");
BRect hsBounds = rvBounds;
hsBounds.left = rvBounds.left + 199;
hsBounds.top = hsBounds.bottom + 1;
hsBounds.right++;
hsBounds.bottom = b.bottom + 1;
m_hScrollBar = new BScrollBar(hsBounds, "hScrollBar", m_routingView,
0, 0, B_HORIZONTAL);
AddChild(m_hScrollBar);
BRect vsBounds = rvBounds;
vsBounds.left = vsBounds.right + 1;
vsBounds.top--;
vsBounds.right = b.right + 1;
vsBounds.bottom++;
m_vScrollBar = new BScrollBar(vsBounds, "vScrollBar", m_routingView,
0, 0, B_VERTICAL);
AddChild(m_vScrollBar);
BRect svBounds = rvBounds;
svBounds.left -= 1;
svBounds.right = hsBounds.left - 1;
svBounds.top = svBounds.bottom + 1;
svBounds.bottom = b.bottom + 1;
m_statusView = new StatusView(svBounds, manager, m_hScrollBar);
AddChild(m_statusView);
AddChild(m_routingView);
float minWidth, maxWidth, minHeight, maxHeight;
GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
minWidth = m_statusView->Frame().Width() + 6 * B_V_SCROLL_BAR_WIDTH;
minHeight = 6 * B_H_SCROLL_BAR_HEIGHT;
SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
BMenu* windowMenu = new BMenu(B_TRANSLATE("Window"));
m_transportWindowItem = new BMenuItem(
B_TRANSLATE("Show transport"),
new BMessage(M_TOGGLE_TRANSPORT_WINDOW));
windowMenu->AddItem(m_transportWindowItem);
m_dormantNodeWindowItem = new BMenuItem(
B_TRANSLATE("Show add-ons"),
new BMessage(M_TOGGLE_DORMANT_NODE_WINDOW));
windowMenu->AddItem(m_dormantNodeWindowItem);
windowMenu->AddItem(new BSeparatorItem());
m_pullPalettesItem = new BMenuItem(
B_TRANSLATE("Pull palettes"),
new BMessage(M_TOGGLE_PULLING_PALETTES));
windowMenu->AddItem(m_pullPalettesItem);
pMenuBar->AddItem(windowMenu);
_toggleDormantNodeWindow();
_toggleTransportWindow();
}
window is moved, all palettes follow).
*/
bool
RouteWindow::isPullPalettes() const
{
return m_pullPalettesItem->IsMarked();
}
void
RouteWindow::setPullPalettes(bool enabled)
{
m_pullPalettesItem->SetMarked(enabled);
}
void
RouteWindow::constrainToScreen()
{
BScreen screen(this);
const BRect sr = screen.Frame();
_constrainToScreen();
BRect r = Frame();
BPoint offset(0.0, 0.0);
if(r.left < 0.0)
offset.x = -r.left;
if(r.top < 0.0)
offset.y = -r.top;
if(r.left >= (sr.right - 20.0))
offset.x -= (r.left - (sr.Width()/2));
if(r.top >= (sr.bottom - 20.0))
offset.y -= (r.top - (sr.Height()/2));
if(offset.x != 0.0 || offset.y != 0.0) {
setPullPalettes(false);
MoveBy(offset.x, offset.y);
}*/
BPoint offset = BPoint(0.0, 0.0);
BRect r = (m_transportWindow) ?
m_transportWindow->Frame() :
m_transportWindowFrame;
if(r.left < 0.0)
offset.x = (sr.Width()*.75) - r.left;
if(r.top < 0.0)
offset.y = (sr.Height()*.25) - r.top;
if(r.left >= (sr.right - 20.0))
offset.x -= (r.left - (sr.Width()/2));
if(r.top >= (sr.bottom - 20.0))
offset.y -= (r.top - (sr.Height()/2));
if(offset.x != 0.0 || offset.y != 0.0) {
if(m_transportWindow)
m_transportWindow->MoveBy(offset.x, offset.y);
else
m_transportWindowFrame.OffsetBy(offset.x, offset.y);
}
offset = BPoint(0.0, 0.0);
r = (m_dormantNodeWindow) ?
m_dormantNodeWindow->Frame() :
m_dormantNodeWindowFrame;
if(r.left < 0.0)
offset.x = (sr.Width()*.25) - r.left;
if(r.top < 0.0)
offset.y = (sr.Height()*.125) - r.top;
if(r.left >= (sr.right - 20.0))
offset.x -= (r.left - (sr.Width()/2));
if(r.top >= (sr.bottom - 20.0))
offset.y -= (r.top - (sr.Height()/2));
if(offset.x != 0.0 || offset.y != 0.0) {
if(m_dormantNodeWindow)
m_dormantNodeWindow->MoveBy(offset.x, offset.y);
else
m_dormantNodeWindowFrame.OffsetBy(offset.x, offset.y);
}
}
void
RouteWindow::FrameMoved(BPoint point)
{
if(IsHidden())
return;
BPoint delta = point - m_lastFramePosition;
m_lastFramePosition = point;
if (m_pullPalettesItem->IsMarked())
_movePalettesBy(delta.x, delta.y);
}
void
RouteWindow::FrameResized(float width, float height)
{
D_HOOK(("RouteWindow::FrameResized()\n"));
if (!m_zooming) {
m_zoomed = false;
}
else {
m_zooming = false;
}
}
bool
RouteWindow::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
return false;
}
void
RouteWindow::Zoom(BPoint origin, float width, float height)
{
D_HOOK(("RouteWindow::Zoom()\n"));
m_zooming = true;
BScreen screen(this);
if (!screen.Frame().Contains(Frame())) {
m_zoomed = false;
}
if (!m_zoomed) {
m_manualSize = Bounds();
float width, height;
m_routingView->GetPreferredSize(&width, &height);
width += B_V_SCROLL_BAR_WIDTH;
height += B_H_SCROLL_BAR_HEIGHT;
if (KeyMenuBar()) {
height += KeyMenuBar()->Frame().Height();
}
ResizeTo(width, height);
_constrainToScreen();
m_zoomed = true;
}
else {
ResizeTo(m_manualSize.Width(), m_manualSize.Height());
m_zoomed = false;
}
}
void
RouteWindow::MessageReceived(BMessage* pMsg)
{
switch (pMsg->what) {
case B_ABOUT_REQUESTED:
{
BAlert* alert = new BAlert("About", g_aboutText, B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
break;
}
case MediaRoutingView::M_GROUP_SELECTED:
_handleGroupSelected(pMsg);
break;
case MediaRoutingView::M_SHOW_ERROR_MESSAGE:
_handleShowErrorMessage(pMsg);
break;
case M_TOGGLE_TRANSPORT_WINDOW:
_toggleTransportWindow();
break;
case M_REFRESH_TRANSPORT_SETTINGS:
_refreshTransportSettings(pMsg);
break;
case M_TOGGLE_PULLING_PALETTES:
_togglePullPalettes();
break;
case M_TOGGLE_DORMANT_NODE_WINDOW:
_toggleDormantNodeWindow();
break;
case M_TOGGLE_GROUP_ROLLING:
_toggleGroupRolling();
break;
default:
_inherited::MessageReceived(pMsg);
break;
}
}
status_t
RouteWindow::importState(const BMessage* archive)
{
status_t err;
BRect r;
err = archive->FindRect("frame", &r);
if(err == B_OK) {
MoveTo(r.LeftTop());
ResizeTo(r.Width(), r.Height());
m_lastFramePosition = r.LeftTop();
}
int32 i;
err = archive->FindInt32("statusViewWidth", &i);
if (err == B_OK) {
float diff = i - m_statusView->Bounds().IntegerWidth();
m_statusView->ResizeBy(diff, 0.0);
m_hScrollBar->ResizeBy(-diff, 0.0);
m_hScrollBar->MoveBy(diff, 0.0);
}
bool b;
err = archive->FindBool("pullPalettes", &b);
if(err == B_OK)
m_pullPalettesItem->SetMarked(b);
err = archive->FindRect("addonPaletteFrame", &r);
if (err == B_OK)
m_dormantNodeWindowFrame = r;
err = archive->FindBool("addonPaletteVisible", &b);
if (err == B_OK && (b != (m_dormantNodeWindow != 0))) {
_toggleDormantNodeWindow();
if(!m_dormantNodeWindow)
m_dormantNodeWindowFrame = r;
}
if (m_dormantNodeWindow) {
m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
m_dormantNodeWindow->ResizeTo(
m_dormantNodeWindowFrame.Width(),
m_dormantNodeWindowFrame.Height());
}
err = archive->FindRect("transportFrame", &r);
if (err == B_OK)
m_transportWindowFrame = r;
err = archive->FindBool("transportVisible", &b);
if (err == B_OK && (b != (m_transportWindow != 0))) {
_toggleTransportWindow();
if (!m_transportWindow)
m_transportWindowFrame = r;
}
if (m_transportWindow) {
m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
m_transportWindow->ResizeTo(
m_transportWindowFrame.Width(),
m_transportWindowFrame.Height());
}
return B_OK;
}
status_t
RouteWindow::exportState(BMessage* archive) const
{
BRect r = Frame();
archive->AddRect("frame", r);
archive->AddBool("pullPalettes", m_pullPalettesItem->IsMarked());
bool b = (m_dormantNodeWindow != 0);
r = b ? m_dormantNodeWindow->Frame() : m_dormantNodeWindowFrame;
archive->AddRect("addonPaletteFrame", r);
archive->AddBool("addonPaletteVisible", b);
b = (m_transportWindow != 0);
r = b ? m_transportWindow->Frame() : m_transportWindowFrame;
archive->AddRect("transportFrame", r);
archive->AddBool("transportVisible", b);
int i = m_statusView->Bounds().IntegerWidth();
archive->AddInt32("statusViewWidth", i);
return B_OK;
}
void
RouteWindow::_constrainToScreen()
{
D_INTERNAL(("RouteWindow::_constrainToScreen()\n"));
BScreen screen(this);
BRect screenRect = screen.Frame();
BRect windowRect = Frame();
if (!screenRect.Intersects(windowRect)) {
windowRect.OffsetTo(screenRect.LeftTop());
MoveTo(windowRect.LeftTop());
windowRect = Frame();
}
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());
}
}
void
RouteWindow::_toggleTransportWindow()
{
if (m_transportWindow) {
m_transportWindowFrame = m_transportWindow->Frame();
m_transportWindow->Lock();
m_transportWindow->Quit();
m_transportWindow = 0;
m_transportWindowItem->SetMarked(false);
} else {
m_transportWindow = new TransportWindow(m_routingView->manager,
this, B_TRANSLATE("Transport"));
BMessenger(m_routingView).SendMessage(
MediaRoutingView::M_BROADCAST_SELECTION);
if (m_transportWindowFrame.IsValid()) {
m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
m_transportWindow->ResizeTo(m_transportWindowFrame.Width(),
m_transportWindowFrame.Height());
}
m_transportWindow->Show();
m_transportWindowItem->SetMarked(true);
}
}
void
RouteWindow::_togglePullPalettes()
{
m_pullPalettesItem->SetMarked(!m_pullPalettesItem->IsMarked());
}
void
RouteWindow::_toggleDormantNodeWindow()
{
if (m_dormantNodeWindow) {
m_dormantNodeWindowFrame = m_dormantNodeWindow->Frame();
m_dormantNodeWindow->Lock();
m_dormantNodeWindow->Quit();
m_dormantNodeWindow = 0;
m_dormantNodeWindowItem->SetMarked(false);
} else {
m_dormantNodeWindow = new DormantNodeWindow(this);
if (m_dormantNodeWindowFrame.IsValid()) {
m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
m_dormantNodeWindow->ResizeTo(m_dormantNodeWindowFrame.Width(),
m_dormantNodeWindowFrame.Height());
}
m_dormantNodeWindow->Show();
m_dormantNodeWindowItem->SetMarked(true);
}
}
void
RouteWindow::_handleGroupSelected(BMessage* message)
{
status_t err;
uint32 groupID;
err = message->FindInt32("groupID", (int32*)&groupID);
if (err < B_OK) {
PRINT((
"! RouteWindow::_handleGroupSelected(): no groupID in message!\n"));
return;
}
if (!m_transportWindow)
return;
BMessage m(TransportWindow::M_SELECT_GROUP);
m.AddInt32("groupID", groupID);
BMessenger(m_transportWindow).SendMessage(&m);
m_selectedGroupID = groupID;
}
void
RouteWindow::_handleShowErrorMessage(BMessage* message)
{
status_t err;
BString text;
err = message->FindString("text", &text);
if (err < B_OK) {
PRINT((
"! RouteWindow::_handleShowErrorMessage(): no text in message!\n"));
return;
}
m_statusView->setErrorMessage(text.String(), message->HasBool("error"));
}
void
RouteWindow::_refreshTransportSettings(BMessage* message)
{
status_t err;
uint32 groupID;
err = message->FindInt32("groupID", (int32*)&groupID);
if (err < B_OK) {
PRINT((
"! RouteWindow::_refreshTransportSettings(): no groupID in message!\n"));
return;
}
if(m_transportWindow) {
BMessenger(m_transportWindow).SendMessage(message);
}
}
void
RouteWindow::_closePalettes()
{
BAutolock _l(this);
if (m_transportWindow) {
m_transportWindow->Lock();
m_transportWindow->Quit();
m_transportWindow = 0;
}
}
void RouteWindow::_movePalettesBy(float xDelta, float yDelta)
{
if (m_transportWindow)
m_transportWindow->MoveBy(xDelta, yDelta);
if (m_dormantNodeWindow)
m_dormantNodeWindow->MoveBy(xDelta, yDelta);
}
void
RouteWindow::_toggleGroupRolling()
{
if (!m_selectedGroupID)
return;
NodeGroup* g;
status_t err = m_routingView->manager->findGroup(m_selectedGroupID, &g);
if (err < B_OK)
return;
Autolock _l(g);
uint32 startAction = (g->runMode() == BMediaNode::B_OFFLINE)
? NodeGroup::M_ROLL : NodeGroup::M_START;
BMessenger m(g);
switch (g->transportState()) {
case NodeGroup::TRANSPORT_STOPPED:
m.SendMessage(startAction);
break;
case NodeGroup::TRANSPORT_RUNNING:
case NodeGroup::TRANSPORT_ROLLING:
m.SendMessage(NodeGroup::M_STOP);
break;
default:
break;
}
}