1// PatchRow.cpp
2// ------------
3// Implements the PatchRow class.
4//
5// Copyright 1999, Be Incorporated.   All Rights Reserved.
6// This file may be used under the terms of the Be Sample Code License.
7
8#include <stdio.h>
9#include <CheckBox.h>
10#include <Debug.h>
11#include <MidiRoster.h>
12#include <MidiConsumer.h>
13#include <MidiProducer.h>
14#include <Window.h>
15#include "MidiEventMeter.h"
16#include "PatchRow.h"
17
18extern const float ROW_LEFT = 50.0f;
19extern const float ROW_TOP = 50.0f;
20extern const float ROW_HEIGHT = 40.0f;
21extern const float COLUMN_WIDTH = 40.0f;
22extern const float METER_PADDING = 15.0f;
23extern const uint32 MSG_CONNECT_REQUEST = 'mCRQ';
24
25static const BPoint kBoxOffset(8,7);
26
27// PatchCheckBox is the check box that describes a connection
28// between a producer and a consumer.
29class PatchCheckBox : public BCheckBox
30{
31public:
32	PatchCheckBox(BRect r, int32 producerID, int32 consumerID)
33		: BCheckBox(r, "", "", new BMessage(MSG_CONNECT_REQUEST))
34	{
35		m_producerID = producerID;
36		m_consumerID = consumerID;
37	}
38
39	int32 ProducerID() const { return m_producerID; }
40	int32 ConsumerID() const { return m_consumerID; }
41
42	void DoConnect();
43
44private:
45	int32 m_producerID;
46	int32 m_consumerID;
47};
48
49PatchRow::PatchRow(int32 producerID)
50	: BView(BRect(0,0,0,0), "PatchRow", B_FOLLOW_NONE,
51	B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED),
52	m_producerID(producerID), m_eventMeter(NULL)
53{
54	m_eventMeter = new MidiEventMeter(m_producerID);
55}
56
57PatchRow::~PatchRow()
58{
59	delete m_eventMeter;
60}
61
62int32 PatchRow::ID() const
63{
64	return m_producerID;
65}
66
67void PatchRow::Pulse()
68{
69	if (m_eventMeter) m_eventMeter->Pulse(this);
70}
71
72void PatchRow::Draw(BRect)
73{
74	if (m_eventMeter) m_eventMeter->Draw(this);
75}
76
77void PatchRow::AddColumn(int32 consumerID)
78{
79	BRect r;
80	int32 numColumns = CountChildren();
81	r.left = numColumns*COLUMN_WIDTH + METER_PADDING + kBoxOffset.x;
82	r.top = kBoxOffset.y;
83	r.right = r.left + 20;
84	r.bottom = r.top + 20;
85
86	PatchCheckBox* box = new PatchCheckBox(r, m_producerID, consumerID);
87	AddChild(box);
88	box->SetTarget(this);
89}
90
91void PatchRow::RemoveColumn(int32 consumerID)
92{
93	int32 numChildren = CountChildren();
94	for (int32 i=0; i<numChildren; i++) {
95		PatchCheckBox* box = dynamic_cast<PatchCheckBox*>(ChildAt(i));
96		if (box && box->ConsumerID() == consumerID) {
97			RemoveChild(box);
98			delete box;
99			while (i < numChildren) {
100				box = dynamic_cast<PatchCheckBox*>(ChildAt(i++));
101				if (box) {
102					box->MoveBy(-COLUMN_WIDTH, 0);
103				}
104			}
105			break;
106		}
107	}
108}
109
110void PatchRow::Connect(int32 consumerID)
111{
112	int32 numChildren = CountChildren();
113	for (int32 i=0; i<numChildren; i++) {
114		PatchCheckBox* box = dynamic_cast<PatchCheckBox*>(ChildAt(i));
115		if (box && box->ConsumerID() == consumerID) {
116			box->SetValue(1);
117		}
118	}
119}
120
121void PatchRow::Disconnect(int32 consumerID)
122{
123	int32 numChildren = CountChildren();
124	for (int32 i=0; i<numChildren; i++) {
125		PatchCheckBox* box = dynamic_cast<PatchCheckBox*>(ChildAt(i));
126		if (box && box->ConsumerID() == consumerID) {
127			box->SetValue(0);
128		}
129	}
130}
131
132void PatchRow::AttachedToWindow()
133{
134	Window()->SetPulseRate(200000);
135	SetViewColor(Parent()->ViewColor());
136	int32 numChildren = CountChildren();
137	for (int32 i=0; i<numChildren; i++) {
138		PatchCheckBox* box = dynamic_cast<PatchCheckBox*>(ChildAt(i));
139		if (box) {
140			box->SetTarget(this);
141		}
142	}
143}
144
145void PatchRow::MessageReceived(BMessage* msg)
146{
147	switch (msg->what) {
148	case MSG_CONNECT_REQUEST:
149		{
150			BControl* ctrl;
151			if (msg->FindPointer("source", (void**) &ctrl) == B_OK) {
152				PatchCheckBox* box = dynamic_cast<PatchCheckBox*>(ctrl);
153				if (box) {
154					box->DoConnect();
155				}
156			}
157		}
158		break;
159	default:
160		BView::MessageReceived(msg);
161		break;
162	}
163}
164
165void PatchCheckBox::DoConnect()
166{
167	int32 value = Value();
168	int32 inverseValue = (value + 1) % 2;
169
170	BMidiRoster* roster = BMidiRoster::MidiRoster();
171	if (! roster) {
172		SetValue(inverseValue);
173		return;
174	}
175
176	BMidiProducer* producer = roster->FindProducer(m_producerID);
177	BMidiConsumer* consumer = roster->FindConsumer(m_consumerID);
178	if (producer && consumer) {
179		status_t err;
180		if (value) {
181			err = producer->Connect(consumer);
182		} else {
183			err = producer->Disconnect(consumer);
184		}
185
186		if (err != B_OK) {
187			SetValue(inverseValue);
188		}
189	} else {
190		SetValue(inverseValue);
191	}
192	if (producer) producer->Release();
193	if (consumer) consumer->Release();
194}
195