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