* 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 "TransportView.h"
#include "RouteApp.h"
#include "RouteWindow.h"
#include "RouteAppNodeManager.h"
#include "NodeGroup.h"
#include "NumericValControl.h"
#include "TextControlFloater.h"
#include <Button.h>
#include <Debug.h>
#include <Font.h>
#include <Invoker.h>
#include <StringView.h>
#include <MediaRoster.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <String.h>
#include <StringFormat.h>
#include <TextControl.h>
#include <algorithm>
#include <functional>
#undef B_CATALOG
#define B_CATALOG (&sCatalog)
#include <Catalog.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "TransportView"
using namespace std;
__USE_CORTEX_NAMESPACE
static BCatalog sCatalog("x-vnd.Cortex.TransportView");
__BEGIN_CORTEX_NAMESPACE
class _GroupInfoView :
public BView {
typedef BView _inherited;
public:
_GroupInfoView(
BRect frame,
TransportView* parent,
const char* name,
uint32 resizeMode =B_FOLLOW_LEFT|B_FOLLOW_TOP,
uint32 flags =B_WILL_DRAW|B_FRAME_EVENTS) :
BView(frame, name, resizeMode, flags),
m_parent(parent),
m_plainFont(be_plain_font),
m_boldFont(be_bold_font) {
_initViews();
_initColors();
_updateLayout();
}
public:
virtual void FrameResized(
float width,
float height) {
_inherited::FrameResized(width, height);
_updateLayout();
Invalidate();
}
virtual void GetPreferredSize(
float* width,
float* height) {
font_height fh;
m_plainFont.GetHeight(&fh);
*width = 0.0;
*height = (fh.ascent+fh.descent+fh.leading) * 2;
*height += 4.0;
}
virtual void Draw(
BRect updateRect) {
NodeGroup* g = m_parent->m_group;
BRect b = Bounds();
rgb_color hi = tint_color(ViewColor(), B_LIGHTEN_2_TINT);
rgb_color lo = tint_color(ViewColor(), B_DARKEN_2_TINT);
SetHighColor(lo);
StrokeLine(
b.LeftTop(), b.RightTop());
StrokeLine(
b.LeftTop(), b.LeftBottom());
SetHighColor(hi);
StrokeLine(
b.LeftBottom(), b.RightBottom());
StrokeLine(
b.RightTop(), b.RightBottom());
SetHighColor(255,255,255,255);
BString name = g ? g->name() : B_TRANSLATE("(no group)");
SetFont(&m_boldFont);
DrawString(name.String(), m_namePosition);
SetFont(&m_plainFont);
uint32 count;
if (g != NULL)
count = g->countNodes();
else
count = 0;
BString nodeCount = "";
static BStringFormat format(
B_TRANSLATE("{0, plural, one{# node} other{# nodes}}"));
format.Format(nodeCount, count);
DrawString(nodeCount.String(), m_nodeCountPosition);
BString status = B_TRANSLATE("No errors.");
DrawString(status.String(), m_statusPosition);
}
virtual void MouseDown(
BPoint point) {
NodeGroup* g = m_parent->m_group;
if(!g)
return;
font_height fh;
m_boldFont.GetHeight(&fh);
BRect nameBounds(
m_namePosition.x,
m_namePosition.y - fh.ascent,
m_namePosition.x + m_maxNameWidth,
m_namePosition.y + (fh.ascent+fh.leading-4.0));
if(nameBounds.Contains(point)) {
ConvertToScreen(&nameBounds);
nameBounds.OffsetBy(-7.0, -3.0);
new TextControlFloater(
nameBounds,
B_ALIGN_LEFT,
&m_boldFont,
g->name(),
m_parent,
new BMessage(TransportView::M_SET_NAME));
}
}
public:
void _initViews() {
}
void _initColors() {
SetViewColor(16, 64, 96, 255);
SetLowColor(16, 64, 96, 255);
SetHighColor(255,255,255,255);
}
void _updateLayout() {
float _edge_pad_x = 3.0;
float _edge_pad_y = 1.0;
BRect b = Bounds();
font_height fh;
m_plainFont.GetHeight(&fh);
float realWidth = b.Width() - (_edge_pad_x * 2);
m_maxNameWidth = realWidth * 0.7;
m_maxNodeCountWidth = realWidth - m_maxNameWidth;
m_namePosition.x = _edge_pad_x;
m_namePosition.y = _edge_pad_x + fh.ascent - 2.0;
m_nodeCountPosition = m_namePosition;
m_nodeCountPosition.x = m_maxNameWidth;
m_maxStatusWidth = realWidth;
m_statusPosition.x = _edge_pad_x;
m_statusPosition.y = b.Height() - (fh.descent + fh.leading + _edge_pad_y);
}
private:
TransportView* m_parent;
BFont m_plainFont;
BFont m_boldFont;
BPoint m_namePosition;
float m_maxNameWidth;
BPoint m_nodeCountPosition;
float m_maxNodeCountWidth;
BPoint m_statusPosition;
float m_maxStatusWidth;
};
__END_CORTEX_NAMESPACE
TransportView::TransportView(
NodeManager* manager,
const char* name) :
BView(
BRect(),
name,
B_FOLLOW_ALL_SIDES,
B_WILL_DRAW|B_FRAME_EVENTS),
m_manager(manager),
m_group(0) {
_initLayout();
_constructControls();
_disableControls();
SetViewColor(
tint_color(
ui_color(B_PANEL_BACKGROUND_COLOR),
B_LIGHTEN_1_TINT));
}
void TransportView::AttachedToWindow() {
_inherited::AttachedToWindow();
_updateLayout();
RouteApp* app = dynamic_cast<RouteApp*>(be_app);
ASSERT(app);
add_observer(this, app->manager);
}
void TransportView::AllAttached() {
_inherited::AllAttached();
for(target_set::iterator it = m_localTargets.begin();
it != m_localTargets.end(); ++it) {
ASSERT(*it);
(*it)->SetTarget(this);
}
}
void TransportView::DetachedFromWindow() {
_inherited::DetachedFromWindow();
RouteApp* app = dynamic_cast<RouteApp*>(be_app);
ASSERT(app);
remove_observer(this, app->manager);
}
void TransportView::FrameResized(
float width,
float height) {
_inherited::FrameResized(width, height);
}
void TransportView::KeyDown(
const char* bytes,
int32 count) {
switch(bytes[0]) {
case B_SPACE: {
RouteApp* app = dynamic_cast<RouteApp*>(be_app);
ASSERT(app);
BMessenger(app->routeWindow).SendMessage(
RouteWindow::M_TOGGLE_GROUP_ROLLING);
break;
}
default:
_inherited::KeyDown(bytes, count);
}
}
void TransportView::MouseDown(
BPoint where) {
MakeFocus(true);
}
void TransportView::MessageReceived(
BMessage* message) {
status_t err;
uint32 groupID;
switch(message->what) {
case NodeGroup::M_RELEASED:
{
err = message->FindInt32("groupID", (int32*)&groupID);
if(err < B_OK) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n"
" no groupID!\n"));
}
if(!m_group || groupID != m_group->id()) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n"
" mismatched groupID.\n"));
break;
}
_releaseGroup();
}
break;
case NodeGroup::M_OBSERVER_ADDED:
err = message->FindInt32("groupID", (int32*)&groupID);
if(err < B_OK) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n"
" no groupID!\n"));
break;
}
if(!m_group || groupID != m_group->id()) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n"
" mismatched groupID; ignoring.\n"));
break;
}
_enableControls();
break;
case NodeGroup::M_TRANSPORT_STATE_CHANGED:
uint32 groupID;
err = message->FindInt32("groupID", (int32*)&groupID);
if(err < B_OK) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n"
" no groupID!\n"));
break;
}
if(!m_group || groupID != m_group->id()) {
PRINT((
"* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n"
" mismatched groupID; ignoring.\n"));
break;
}
_updateTransportButtons();
break;
case NodeGroup::M_TIME_SOURCE_CHANGED:
break;
case NodeGroup::M_RUN_MODE_CHANGED:
break;
case NodeGroup::M_SET_START_POSITION: {
if(!m_group)
break;
bigtime_t position = _scalePosition(
m_regionStartView->value());
message->AddInt64("position", position);
BMessenger(m_group).SendMessage(message);
if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED)
_updateTransportButtons();
break;
}
case NodeGroup::M_SET_END_POSITION: {
if(!m_group)
break;
bigtime_t position = _scalePosition(
m_regionEndView->value());
message->AddInt64("position", position);
BMessenger(m_group).SendMessage(message);
if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED)
_updateTransportButtons();
break;
}
case M_SET_NAME:
{
const char* name;
err = message->FindString("_value", &name);
if(err < B_OK) {
PRINT((
"! TransportView::MessageReceived(M_SET_NAME): no _value!\n"));
break;
}
if(m_group) {
m_group->setName(name);
m_infoView->Invalidate();
}
}
break;
case RouteAppNodeManager::M_TIME_SOURCE_CREATED:
_timeSourceCreated(message);
break;
case RouteAppNodeManager::M_TIME_SOURCE_DELETED:
_timeSourceDeleted(message);
break;
default:
_inherited::MessageReceived(message);
break;
}
}
void TransportView::_handleSelectGroup(
BMessage* message) {
uint32 groupID;
status_t err = message->FindInt32("groupID", (int32*)&groupID);
if(err < B_OK) {
PRINT((
"* TransportView::_handleSelectGroup(): no groupID\n"));
return;
}
if(m_group && groupID != m_group->id())
_releaseGroup();
_selectGroup(groupID);
Invalidate();
}
void TransportView::_selectGroup(
uint32 groupID) {
status_t err;
if(m_group)
_releaseGroup();
if(!groupID) {
_disableControls();
return;
}
err = m_manager->findGroup(groupID, &m_group);
if(err < B_OK) {
PRINT((
"* TransportView::_selectGroup(%" B_PRId32 "): findGroup() failed:\n"
" %s\n",
groupID,
strerror(err)));
return;
}
_observeGroup();
}
void TransportView::_observeGroup() {
ASSERT(m_group);
add_observer(this, m_group);
}
void TransportView::_releaseGroup() {
ASSERT(m_group);
remove_observer(this, m_group);
m_group = 0;
}
const char _run_mode_strings[][20] = {
B_TRANSLATE_MARK("Offline"),
B_TRANSLATE_MARK("Decrease precision"),
B_TRANSLATE_MARK("Increase latency"),
B_TRANSLATE_MARK("Drop data"),
B_TRANSLATE_MARK("Recording")
};
const int _run_modes = 5;
const char* _region_start_label = B_TRANSLATE("From:");
const char* _region_end_label = B_TRANSLATE("To:");
void TransportView::_constructControls() {
BMessage* m;
m_infoView = new _GroupInfoView(
BRect(),
this,
"infoView");
AddChild(m_infoView);
m_runModeView = new BMenuField(
BRect(),
"runModeView",
B_TRANSLATE("Run mode:"),
new BPopUpMenu("runModeMenu"));
_populateRunModeMenu(
m_runModeView->Menu());
AddChild(m_runModeView);
m_timeSourceView = new BMenuField(
BRect(),
"timeSourceView",
B_TRANSLATE("Time source:"),
new BPopUpMenu("timeSourceMenu"));
_populateTimeSourceMenu(
m_timeSourceView->Menu());
AddChild(m_timeSourceView);
m_fromLabel = new BStringView(BRect(), 0, B_TRANSLATE("Roll from"));
AddChild(m_fromLabel);
m = new BMessage(NodeGroup::M_SET_START_POSITION);
m_regionStartView = new NumericValControl(
BRect(),
"regionStartView",
m,
2, 4,
false, ValControl::ALIGN_FLUSH_LEFT);
_addLocalTarget(m_regionStartView);
AddChild(m_regionStartView);
m_toLabel = new BStringView(BRect(), 0, B_TRANSLATE("to"));
AddChild(m_toLabel);
m = new BMessage(NodeGroup::M_SET_END_POSITION);
m_regionEndView = new NumericValControl(
BRect(),
"regionEndView",
m,
2, 4,
false, ValControl::ALIGN_FLUSH_LEFT);
_addLocalTarget(m_regionEndView);
AddChild(m_regionEndView);
m = new BMessage(NodeGroup::M_START);
m_startButton = new BButton(
BRect(),
"startButton",
B_TRANSLATE("Start"),
m);
_addGroupTarget(m_startButton);
AddChild(m_startButton);
m = new BMessage(NodeGroup::M_STOP);
m_stopButton = new BButton(
BRect(),
"stopButton",
B_TRANSLATE("Stop"),
m);
_addGroupTarget(m_stopButton);
AddChild(m_stopButton);
m = new BMessage(NodeGroup::M_PREROLL);
m_prerollButton = new BButton(
BRect(),
"prerollButton",
B_TRANSLATE("Preroll"),
m);
_addGroupTarget(m_prerollButton);
AddChild(m_prerollButton);
}
bigtime_t TransportView::_scalePosition(
double value) {
return bigtime_t(value * 1000000.0);
}
void TransportView::_populateRunModeMenu(
BMenu* menu) {
BMessage* m;
for(int n = 0; n < _run_modes; ++n) {
m = new BMessage(NodeGroup::M_SET_RUN_MODE);
m->AddInt32("runMode", n+1);
BMenuItem* i = new BMenuItem(
B_TRANSLATE(_run_mode_strings[n]), m);
menu->AddItem(i);
_addGroupTarget(i);
}
}
void TransportView::_populateTimeSourceMenu(
BMenu* menu) {
status_t err;
media_node dacTimeSource, systemTimeSource;
BMessage* m;
BMenuItem* i;
err = m_manager->roster->GetTimeSource(&dacTimeSource);
if(err == B_OK) {
m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
m->AddData(
"timeSourceNode",
B_RAW_TYPE,
&dacTimeSource,
sizeof(media_node));
i = new BMenuItem(
B_TRANSLATE("DAC time source"),
m);
menu->AddItem(i);
_addGroupTarget(i);
}
err = m_manager->roster->GetSystemTimeSource(&systemTimeSource);
if(err == B_OK) {
m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
m->AddData(
"timeSourceNode",
B_RAW_TYPE,
&systemTimeSource,
sizeof(media_node));
i = new BMenuItem(
B_TRANSLATE("System clock"),
m);
menu->AddItem(i);
_addGroupTarget(i);
}
Autolock _l(m_manager);
void* cookie = 0;
NodeRef* r;
char nameBuffer[B_MEDIA_NAME_LENGTH+16];
bool needSeparator = (menu->CountItems() > 0);
while(m_manager->getNextRef(&r, &cookie) == B_OK) {
if(!(r->kind() & B_TIME_SOURCE))
continue;
if(r->id() == dacTimeSource.node)
continue;
if(r->id() == systemTimeSource.node)
continue;
if(needSeparator) {
needSeparator = false;
menu->AddItem(new BSeparatorItem());
}
m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
m->AddData(
"timeSourceNode",
B_RAW_TYPE,
&r->node(),
sizeof(media_node));
sprintf(nameBuffer, "%s: %" B_PRId32,
r->name(),
r->id());
i = new BMenuItem(
nameBuffer,
m);
menu->AddItem(i);
_addGroupTarget(i);
}
}
void TransportView::_addLocalTarget(
BInvoker* invoker) {
m_localTargets.push_back(invoker);
if(Window())
invoker->SetTarget(this);
}
void TransportView::_addGroupTarget(
BInvoker* invoker) {
m_groupTargets.push_back(invoker);
if(m_group)
invoker->SetTarget(m_group);
}
void TransportView::_refreshTransportSettings() {
if(m_group)
_updateTransportButtons();
}
void TransportView::_disableControls() {
BWindow* w = Window();
if(w)
w->BeginViewTransaction();
m_infoView->Invalidate();
m_runModeView->SetEnabled(false);
m_runModeView->Menu()->Superitem()->SetLabel(B_TRANSLATE("(none)"));
m_timeSourceView->SetEnabled(false);
m_timeSourceView->Menu()->Superitem()->SetLabel(B_TRANSLATE("(none)"));
m_regionStartView->SetEnabled(false);
m_regionStartView->setValue(0);
m_regionEndView->SetEnabled(false);
m_regionEndView->setValue(0);
m_startButton->SetEnabled(false);
m_stopButton->SetEnabled(false);
m_prerollButton->SetEnabled(false);
if(w)
w->EndViewTransaction();
}
void TransportView::_enableControls() {
ASSERT(m_group);
Autolock _l(m_group);
BWindow* w = Window();
if(w) {
w->BeginViewTransaction();
BView* focused = w->CurrentFocus();
if(focused)
focused->MakeFocus(false);
}
m_infoView->Invalidate();
m_runModeView->SetEnabled(true);
_updateRunMode();
m_timeSourceView->SetEnabled(true);
_updateTimeSource();
_updateTransportButtons();
m_regionStartView->SetEnabled(true);
m_regionStartView->setValue(
((double)m_group->startPosition()) / 1000000.0);
m_regionEndView->SetEnabled(true);
m_regionEndView->setValue(
((double)m_group->endPosition()) / 1000000.0);
for(target_set::iterator it = m_groupTargets.begin();
it != m_groupTargets.end(); ++it) {
ASSERT(*it);
(*it)->SetTarget(m_group);
}
if(w) {
w->EndViewTransaction();
}
}
void TransportView::_updateTransportButtons() {
ASSERT(m_group);
switch(m_group->transportState()) {
case NodeGroup::TRANSPORT_INVALID:
case NodeGroup::TRANSPORT_STARTING:
case NodeGroup::TRANSPORT_STOPPING:
m_startButton->SetEnabled(false);
m_stopButton->SetEnabled(false);
m_prerollButton->SetEnabled(false);
break;
case NodeGroup::TRANSPORT_STOPPED:
m_startButton->SetEnabled(true);
m_stopButton->SetEnabled(false);
m_prerollButton->SetEnabled(true);
break;
case NodeGroup::TRANSPORT_RUNNING:
case NodeGroup::TRANSPORT_ROLLING:
m_startButton->SetEnabled(false);
m_stopButton->SetEnabled(true);
m_prerollButton->SetEnabled(false);
break;
}
ASSERT(m_startButton->Message());
bigtime_t startPosition = _scalePosition(m_regionStartView->value());
bigtime_t endPosition = _scalePosition(m_regionEndView->value());
uint32 runMode = 0;
BMenuItem* i = m_runModeView->Menu()->FindMarked();
ASSERT(i);
runMode = m_runModeView->Menu()->IndexOf(i) + 1;
if(
endPosition > startPosition &&
(runMode == BMediaNode::B_OFFLINE ||
!m_group->canCycle())) {
m_startButton->SetLabel(B_TRANSLATE("Roll"));
m_startButton->Message()->what = NodeGroup::M_ROLL;
} else {
m_startButton->SetLabel(B_TRANSLATE("Start"));
m_startButton->Message()->what = NodeGroup::M_START;
}
}
void TransportView::_updateTimeSource() {
ASSERT(m_group);
media_node tsNode;
status_t err = m_group->getTimeSource(&tsNode);
if(err < B_OK) {
PRINT((
"! TransportView::_updateTimeSource(): m_group->getTimeSource():\n"
" %s\n",
strerror(err)));
return;
}
BMenu* menu = m_timeSourceView->Menu();
ASSERT(menu);
if(tsNode == media_node::null) {
menu->Superitem()->SetLabel(B_TRANSLATE("(none)"));
return;
}
int32 n;
for(n = menu->CountItems()-1; n >= 0; --n) {
const void* data;
media_node itemNode;
BMessage* message = menu->ItemAt(n)->Message();
if(!message)
continue;
ssize_t size = sizeof(media_node);
err = message->FindData(
"timeSourceNode",
B_RAW_TYPE,
&data,
&size);
if(err < B_OK)
continue;
itemNode = *((media_node*)data);
if(itemNode != tsNode)
continue;
PRINT((
"### _updateTimeSource: %s\n",
menu->ItemAt(n)->Label()));
menu->ItemAt(n)->SetMarked(true);
break;
}
if(n < 0)
menu->Superitem()->SetLabel(B_TRANSLATE("(\?\?\?)"));
}
void TransportView::_updateRunMode() {
ASSERT(m_group);
BMenu* menu = m_runModeView->Menu();
uint32 runMode = m_group->runMode() - 1;
ASSERT((uint32)menu->CountItems() > runMode);
menu->ItemAt(runMode)->SetMarked(true);
}
void TransportView::_timeSourceCreated(
BMessage* message) {
status_t err;
media_node_id id;
err = message->FindInt32("nodeID", &id);
if(err < B_OK)
return;
NodeRef* ref;
err = m_manager->getNodeRef(id, &ref);
if(err < B_OK) {
PRINT((
"!!! TransportView::_timeSourceCreated(): node %" B_PRId32 " doesn't exist\n",
id));
return;
}
char nameBuffer[B_MEDIA_NAME_LENGTH+16];
BMessage* m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
m->AddData(
"timeSourceNode",
B_RAW_TYPE,
&ref->node(),
sizeof(media_node));
sprintf(nameBuffer, "%s: %" B_PRId32,
ref->name(),
ref->id());
BMenuItem* i = new BMenuItem(
nameBuffer,
m);
BMenu* menu = m_timeSourceView->Menu();
menu->AddItem(i);
_addGroupTarget(i);
}
void TransportView::_timeSourceDeleted(
BMessage* message) {
status_t err;
media_node_id id;
err = message->FindInt32("nodeID", &id);
if(err < B_OK)
return;
BMenu* menu = m_timeSourceView->Menu();
ASSERT(menu);
int32 n;
for(n = menu->CountItems()-1; n >= 0; --n) {
const void* data;
media_node itemNode;
BMessage* message = menu->ItemAt(n)->Message();
if(!message)
continue;
ssize_t size = sizeof(media_node);
err = message->FindData(
"timeSourceNode",
B_RAW_TYPE,
&data,
&size);
if(err < B_OK)
continue;
itemNode = *((media_node*)data);
if(itemNode.node != id)
continue;
menu->RemoveItem(n);
break;
}
}
const float _edge_pad_x = 3.0;
const float _edge_pad_y = 3.0;
const float _column_pad_x = 4.0;
const float _field_pad_x = 20.0;
const float _text_view_pad_x = 10.0;
const float _control_pad_x = 5.0;
const float _control_pad_y = 10.0;
const float _menu_field_pad_x = 30.0;
const float _label_pad_x = 8.0;
const float _transport_pad_y = 4.0;
const float _transport_button_width = 60.0;
const float _transport_button_height = 22.0;
const float _transport_button_pad_x = 4.0;
const float _lock_button_width = 42.0;
const float _lock_button_height = 16.0;
class TransportView::_layout_state {
public:
_layout_state() {}
};
inline float _menu_width(
const BMenu* menu,
const BFont* font) {
float max = 0.0;
int total = menu->CountItems();
for(int n = 0; n < total; ++n) {
float w = font->StringWidth(
menu->ItemAt(n)->Label());
if(w > max)
max = w;
}
return max;
}
void TransportView::_initLayout() {
m_layout = new _layout_state();
}
void TransportView::_updateLayout()
{
BWindow* window = Window();
if(window)
window->BeginViewTransaction();
float maxLabelWidth = 0.0;
float w;
maxLabelWidth = be_bold_font->StringWidth(
m_runModeView->Label());
w = be_bold_font->StringWidth(
m_timeSourceView->Label());
if(w > maxLabelWidth)
maxLabelWidth = w;
float maxFieldWidth = 0.0;
maxFieldWidth = _menu_width(
m_runModeView->Menu(), be_plain_font);
w = _menu_width(
m_timeSourceView->Menu(), be_plain_font);
if(w > maxFieldWidth)
maxFieldWidth = w;
float columnWidth =
maxLabelWidth +
maxFieldWidth + _label_pad_x + _field_pad_x;
float column1_x = _edge_pad_x;
float column2_x = column1_x + columnWidth + _column_pad_x;
float viewWidth =
column2_x + columnWidth + _edge_pad_x;
float buttonSpan =
(_transport_button_width*3) +
(_transport_button_pad_x*2);
if(columnWidth < buttonSpan)
viewWidth += (buttonSpan - columnWidth);
font_height fh;
be_plain_font->GetHeight(&fh);
float lineHeight = fh.ascent + fh.descent + fh.leading;
float prefInfoWidth, prefInfoHeight;
m_infoView->GetPreferredSize(&prefInfoWidth, &prefInfoHeight);
float row_1_height = max(prefInfoHeight, _transport_button_height);
float row1_y = _edge_pad_y;
float row2_y = row1_y + row_1_height + _transport_pad_y;
float row3_y = row2_y + lineHeight + _control_pad_y;
float viewHeight = row3_y + lineHeight + _control_pad_y + _edge_pad_y;
m_infoView->MoveTo(
column1_x+1.0, row1_y+1.0);
m_infoView->ResizeTo(
columnWidth, prefInfoHeight);
BRect br(
column2_x, row1_y,
column2_x+_transport_button_width,
row1_y+_transport_button_height);
if(prefInfoHeight > _transport_button_height)
br.OffsetBy(0.0, (prefInfoHeight - _transport_button_height)/2);
m_startButton->MoveTo(br.LeftTop());
m_startButton->ResizeTo(br.Width(), br.Height());
br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0);
m_stopButton->MoveTo(br.LeftTop());
m_stopButton->ResizeTo(br.Width(), br.Height());
br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0);
m_prerollButton->MoveTo(br.LeftTop());
m_prerollButton->ResizeTo(br.Width(), br.Height());
m_runModeView->MoveTo(
column2_x, row2_y);
m_runModeView->ResizeTo(
columnWidth, lineHeight);
m_runModeView->SetDivider(
maxLabelWidth+_label_pad_x);
m_runModeView->SetAlignment(
B_ALIGN_LEFT);
m_timeSourceView->MoveTo(
column2_x, row3_y);
m_timeSourceView->ResizeTo(
columnWidth, lineHeight);
m_timeSourceView->SetDivider(
maxLabelWidth+_label_pad_x);
m_timeSourceView->SetAlignment(
B_ALIGN_LEFT);
BPoint rtLeftTop(column1_x, row2_y + 5.0);
BPoint rtRightBottom;
m_fromLabel->MoveTo(rtLeftTop);
m_fromLabel->ResizeToPreferred();
rtRightBottom = rtLeftTop + BPoint(
m_fromLabel->Bounds().Width(),
m_fromLabel->Bounds().Height());
rtLeftTop.x = rtRightBottom.x+4;
m_regionStartView->MoveTo(rtLeftTop + BPoint(0.0, 2.0));
m_regionStartView->ResizeToPreferred();
rtRightBottom = rtLeftTop + BPoint(
m_regionStartView->Bounds().Width(),
m_regionStartView->Bounds().Height());
rtLeftTop.x = rtRightBottom.x + 6;
m_toLabel->MoveTo(rtLeftTop);
m_toLabel->ResizeToPreferred();
rtRightBottom = rtLeftTop + BPoint(
m_toLabel->Bounds().Width(),
m_toLabel->Bounds().Height());
rtLeftTop.x = rtRightBottom.x + 4;
m_regionEndView->MoveTo(rtLeftTop + BPoint(0.0, 2.0));
m_regionEndView->ResizeToPreferred();
BRect b = Bounds();
float targetWidth = (b.Width() < viewWidth) ?
viewWidth :
b.Width();
float targetHeight = (b.Height() < viewHeight) ?
viewHeight :
b.Height();
ResizeTo(targetWidth, targetHeight);
if(window) {
window->ResizeTo(targetWidth, targetHeight);
}
if(window)
window->EndViewTransaction();
}
TransportView::~TransportView() {
if(m_group)
_releaseGroup();
if(m_layout)
delete m_layout;
}