diff --git a/src/tests/kits/midi/Jamfile b/src/tests/kits/midi/Jamfile index 913d0504d8..485c5c8dad 100644 --- a/src/tests/kits/midi/Jamfile +++ b/src/tests/kits/midi/Jamfile @@ -5,4 +5,5 @@ SubDir HAIKU_TOP src tests kits midi ; #SubInclude HAIKU_TOP src tests kits midi midi_player ; #SubInclude HAIKU_TOP src tests kits midi midi_player_replacement ; #SubInclude HAIKU_TOP src tests kits midi synth_file_reader ; +SubInclude HAIKU_TOP src tests kits midi patchbay ; diff --git a/src/tests/kits/midi/patchbay/CountEventConsumer.h b/src/tests/kits/midi/patchbay/CountEventConsumer.h new file mode 100644 index 0000000000..553c1e06f1 --- /dev/null +++ b/src/tests/kits/midi/patchbay/CountEventConsumer.h @@ -0,0 +1,30 @@ +// CountEventConsumer.h +// -------------------- +// A simple MIDI consumer that counts incoming MIDI events. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _CountEventConsumer_h +#define _CountEventConsumer_h + +#include +#include + +class CountEventConsumer : public BMidiLocalConsumer +{ +public: + CountEventConsumer(const char* name) + : BMidiLocalConsumer(name), m_eventCount(0) + {} + void Reset() { m_eventCount = 0; } + int32 CountEvents() { return m_eventCount; } + + void Data(uchar*, size_t, bool, bigtime_t) + { atomic_add(&m_eventCount, 1); } + +private: + int32 m_eventCount; +}; + +#endif /* _CountEventConsumer_h */ diff --git a/src/tests/kits/midi/patchbay/EndpointInfo.cpp b/src/tests/kits/midi/patchbay/EndpointInfo.cpp new file mode 100644 index 0000000000..51d9ddeaa2 --- /dev/null +++ b/src/tests/kits/midi/patchbay/EndpointInfo.cpp @@ -0,0 +1,105 @@ +// EndpointInfo.cpp +// ---------------- +// Implements the EndpointInfo object. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include +#include +#include +#include + +#include "EndpointInfo.h" + +const char* LARGE_ICON_NAME = "be:large_icon"; +const char* MINI_ICON_NAME = "be:mini_icon"; +const uint32 LARGE_ICON_TYPE = 'ICON'; +const uint32 MINI_ICON_TYPE = 'MICN'; +extern const uint8 LARGE_ICON_SIZE = 32; +extern const uint8 MINI_ICON_SIZE = 16; +extern const icon_size DISPLAY_ICON_SIZE = B_LARGE_ICON; +extern const color_space ICON_COLOR_SPACE = B_CMAP8; + +static BBitmap* CreateIcon(const BMessage* msg, icon_size which); + +EndpointInfo::EndpointInfo() + : m_id(-1), m_icon(NULL) +{} + +EndpointInfo::EndpointInfo(int32 id) + : m_id(id), m_icon(NULL) +{ + BMidiRoster* roster = BMidiRoster::MidiRoster(); + if (roster) { + BMidiEndpoint* endpoint = roster->FindEndpoint(id); + if (endpoint) { + printf("endpoint %ld = %p\n", id, endpoint); + BMessage msg; + if (endpoint->GetProperties(&msg) == B_OK) { + m_icon = CreateIcon(&msg, DISPLAY_ICON_SIZE); + } + endpoint->Release(); + } + } +} + +EndpointInfo::EndpointInfo(const EndpointInfo& info) + : m_id(info.m_id) +{ + m_icon = (info.m_icon) ? new BBitmap(info.m_icon) : NULL; +} + +EndpointInfo& EndpointInfo::operator=(const EndpointInfo& info) +{ + if (&info != this) { + m_id = info.m_id; + delete m_icon; + m_icon = (info.m_icon) ? new BBitmap(info.m_icon) : NULL; + } + return *this; +} + +EndpointInfo::~EndpointInfo() +{ + delete m_icon; +} + +void EndpointInfo::UpdateProperties(const BMessage* props) +{ + delete m_icon; + m_icon = CreateIcon(props, DISPLAY_ICON_SIZE); +} + +static BBitmap* CreateIcon(const BMessage* msg, icon_size which) +{ + float iconSize; + uint32 iconType; + const char* iconName; + + if (which == B_LARGE_ICON) { + iconSize = LARGE_ICON_SIZE; + iconType = LARGE_ICON_TYPE; + iconName = LARGE_ICON_NAME; + } else if (which == B_MINI_ICON) { + iconSize = MINI_ICON_SIZE; + iconType = MINI_ICON_TYPE; + iconName = MINI_ICON_NAME; + } else { + return NULL; + } + + const void* data; + ssize_t size; + BBitmap* bitmap = NULL; + + if (msg->FindData(iconName, iconType, &data, &size) == B_OK) + { + BRect r(0, 0, iconSize-1, iconSize-1); + bitmap = new BBitmap(r, ICON_COLOR_SPACE); + ASSERT((bitmap->BitsLength() == size)); + memcpy(bitmap->Bits(), data, size); + } + return bitmap; +} diff --git a/src/tests/kits/midi/patchbay/EndpointInfo.h b/src/tests/kits/midi/patchbay/EndpointInfo.h new file mode 100644 index 0000000000..b75c9a8035 --- /dev/null +++ b/src/tests/kits/midi/patchbay/EndpointInfo.h @@ -0,0 +1,40 @@ +// EndpointInfo.h +// -------------- +// A simple structure that describes a MIDI object. +// Currently, it only contains icon data associated with the object. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _EndpointInfo_h +#define _EndpointInfo_h + +#include /* for icon_size */ +#include /* for color_space */ + +class BMidiEndpoint; + +extern const uint8 LARGE_ICON_SIZE; +extern const uint8 MINI_ICON_SIZE; +extern const icon_size DISPLAY_ICON_SIZE; +extern const color_space ICON_COLOR_SPACE; + +class EndpointInfo +{ +public: + EndpointInfo(); + EndpointInfo(int32 id); + EndpointInfo(const EndpointInfo& info); + EndpointInfo& operator=(const EndpointInfo& info); + ~EndpointInfo(); + + int32 ID() const { return m_id; } + const BBitmap* Icon() const { return m_icon; } + void UpdateProperties(const BMessage* props); + +private: + int32 m_id; + BBitmap* m_icon; +}; + +#endif /* _EndpointInfo_h */ diff --git a/src/tests/kits/midi/patchbay/Jamfile b/src/tests/kits/midi/patchbay/Jamfile new file mode 100644 index 0000000000..0763b13eff --- /dev/null +++ b/src/tests/kits/midi/patchbay/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src tests kits midi patchbay ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +SimpleTest PatchBay + : + PatchApp.cpp + PatchWin.cpp + PatchView.cpp + PatchRow.cpp + EndpointInfo.cpp + MidiEventMeter.cpp + TToolTip.cpp + : + midi midi2 be $(TARGET_LIBSTDC++) +; + diff --git a/src/tests/kits/midi/patchbay/MidiEventMeter.cpp b/src/tests/kits/midi/patchbay/MidiEventMeter.cpp new file mode 100644 index 0000000000..f625be0cfa --- /dev/null +++ b/src/tests/kits/midi/patchbay/MidiEventMeter.cpp @@ -0,0 +1,122 @@ +// MidiEventMeter.cpp +// ------------------ +// Implements the MidiEventMeter class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include +#include +#include +#include +#include "CountEventConsumer.h" +#include "MidiEventMeter.h" + +static const BRect METER_BOUNDS(0,0,7,31); + +// If we get this number of events per pulse or greater, we will +// max out the event meter. +// Value was determined empirically based on my banging on a MIDI +// keyboard controller with a pulse of 200ms. +static const int32 METER_SCALE = 10; + +MidiEventMeter::MidiEventMeter(int32 producerID) + : m_counter(NULL), m_meterLevel(0) +{ + BMidiRoster* roster = BMidiRoster::MidiRoster(); + if (roster) { + BMidiProducer* producer = roster->FindProducer(producerID); + if (producer) { + BString name; + name << producer->Name() << " Event Meter"; + m_counter = new CountEventConsumer(name.String()); + producer->Connect(m_counter); + producer->Release(); + } + } +} + +MidiEventMeter::~MidiEventMeter() +{ + if (m_counter) m_counter->Release(); +} + +void MidiEventMeter::Pulse(BView* view) +{ + int32 newLevel = m_meterLevel; + if (m_counter) { + newLevel = CalcMeterLevel(m_counter->CountEvents()); + m_counter->Reset(); + } + if (newLevel != m_meterLevel) { + m_meterLevel = newLevel; + view->Invalidate(BRect(METER_BOUNDS).InsetBySelf(1,1)); + } +} + +BRect MidiEventMeter::Bounds() const +{ + return METER_BOUNDS; +} + +void MidiEventMeter::Draw(BView* view) +{ + const rgb_color METER_BLACK = { 0, 0, 0, 255 }; + const rgb_color METER_GREY = { 180, 180, 180, 255 }; + const rgb_color METER_GREEN = { 0, 255, 0, 255 }; + + view->PushState(); + + // draw the frame + BPoint lt = METER_BOUNDS.LeftTop(); + BPoint rb = METER_BOUNDS.RightBottom(); + view->BeginLineArray(4); + view->AddLine(BPoint(lt.x, lt.y), BPoint(rb.x - 1, lt.y), METER_BLACK); + view->AddLine(BPoint(rb.x, lt.y), BPoint(rb.x, rb.y - 1), METER_BLACK); + view->AddLine(BPoint(rb.x, rb.y), BPoint(lt.x + 1, rb.y), METER_BLACK); + view->AddLine(BPoint(lt.x, rb.y), BPoint(lt.x, lt.y + 1), METER_BLACK); + view->EndLineArray(); + + // draw the cells + BRect cell = METER_BOUNDS; + cell.InsetBy(1,1); + cell.bottom = cell.top + (cell.Height() + 1) / 5; + cell.bottom--; + + const float kTintArray[] = { B_DARKEN_4_TINT, B_DARKEN_3_TINT, B_DARKEN_2_TINT, B_DARKEN_1_TINT, B_NO_TINT }; + + for (int32 i=4; i>=0; i--) + { + rgb_color color; + if (m_meterLevel > i) { + color = tint_color(METER_GREEN, kTintArray[i]); + } else { + color = METER_GREY; + } + view->SetHighColor(color); + view->FillRect(cell); + cell.OffsetBy(0, cell.Height() + 1); + } + + view->PopState(); +} + +int32 MidiEventMeter::CalcMeterLevel(int32 eventCount) const +{ + // we use an approximately logarithmic scale for determing the actual + // drawn meter level, so that low-density event streams show up well. + if (eventCount == 0) { + return 0; + } else if (eventCount < (int32)(.5*METER_SCALE)) { + return 1; + } else if (eventCount < (int32)(.75*METER_SCALE)) { + return 2; + } else if (eventCount < (int32)(.9*METER_SCALE)) { + return 3; + } else if (eventCount < METER_SCALE) { + return 4; + } else { + return 5; + } +} diff --git a/src/tests/kits/midi/patchbay/MidiEventMeter.h b/src/tests/kits/midi/patchbay/MidiEventMeter.h new file mode 100644 index 0000000000..b154d05c18 --- /dev/null +++ b/src/tests/kits/midi/patchbay/MidiEventMeter.h @@ -0,0 +1,36 @@ +// MidiEventMeter.h +// ---------------- +// A UI widget that measures the amount of MIDI data generated by a +// consumer. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _MidiEventMeter_h +#define _MidiEventMeter_h + +#include +#include + +class BMidiProducer; +class CountEventConsumer; +class BView; + +class MidiEventMeter +{ +public: + MidiEventMeter(int32 producerID); + ~MidiEventMeter(); + + void Pulse(BView* view); + BRect Bounds() const; + void Draw(BView* view); + +private: + int32 CalcMeterLevel(int32 eventCount) const; + + CountEventConsumer* m_counter; + int32 m_meterLevel; +}; + +#endif /* _MidiMeterWidget_h */ diff --git a/src/tests/kits/midi/patchbay/PatchApp.cpp b/src/tests/kits/midi/patchbay/PatchApp.cpp new file mode 100644 index 0000000000..ebe42eab2e --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchApp.cpp @@ -0,0 +1,44 @@ +// PatchApp.cpp +// ------------ +// Implements the PatchBay application class and main(). +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include "PatchApp.h" +#include "PatchWin.h" +#include "TToolTip.h" + +PatchApp::PatchApp() + : BApplication("application/x-vnd.Be-DTS.PatchBay"), m_toolTip(NULL) +{ + m_toolTip = new TToolTip; +} + +void PatchApp::ReadyToRun() +{ + new PatchWin; +} + +void PatchApp::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case B_SOME_APP_ACTIVATED: + case eToolTipStart: + case eToolTipStop: + if (m_toolTip) m_toolTip->PostMessage(msg); + break; + default: + BApplication::MessageReceived(msg); + break; + } +} + +int main() +{ + PatchApp app; + app.Run(); + return 0; +} + diff --git a/src/tests/kits/midi/patchbay/PatchApp.h b/src/tests/kits/midi/patchbay/PatchApp.h new file mode 100644 index 0000000000..b4537a18c1 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchApp.h @@ -0,0 +1,26 @@ +// PatchApp.h +// ---------- +// The PatchBay application class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _PatchApp_h +#define _PatchApp_h + +#include + +class TToolTip; + +class PatchApp : public BApplication +{ +public: + PatchApp(); + void ReadyToRun(); + void MessageReceived(BMessage* msg); + +private: + TToolTip* m_toolTip; +}; + +#endif /* _PatchApp_h */ diff --git a/src/tests/kits/midi/patchbay/PatchBay.rsrc b/src/tests/kits/midi/patchbay/PatchBay.rsrc new file mode 100644 index 0000000000..1fecbbae19 Binary files /dev/null and b/src/tests/kits/midi/patchbay/PatchBay.rsrc differ diff --git a/src/tests/kits/midi/patchbay/PatchRow.cpp b/src/tests/kits/midi/patchbay/PatchRow.cpp new file mode 100644 index 0000000000..5906a29aeb --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchRow.cpp @@ -0,0 +1,194 @@ +// PatchRow.cpp +// ------------ +// Implements the PatchRow class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include +#include +#include +#include +#include +#include +#include "MidiEventMeter.h" +#include "PatchRow.h" + +extern const float ROW_LEFT = 50.0f; +extern const float ROW_TOP = 50.0f; +extern const float ROW_HEIGHT = 40.0f; +extern const float COLUMN_WIDTH = 40.0f; +extern const float METER_PADDING = 15.0f; +extern const uint32 MSG_CONNECT_REQUEST = 'mCRQ'; + +static const BPoint kBoxOffset(8,7); + +// PatchCheckBox is the check box that describes a connection +// between a producer and a consumer. +class PatchCheckBox : public BCheckBox +{ +public: + PatchCheckBox(BRect r, int32 producerID, int32 consumerID) + : BCheckBox(r, "", "", new BMessage(MSG_CONNECT_REQUEST)) + { + m_producerID = producerID; + m_consumerID = consumerID; + } + + int32 ProducerID() const { return m_producerID; } + int32 ConsumerID() const { return m_consumerID; } + + void DoConnect(); + +private: + int32 m_producerID; + int32 m_consumerID; +}; + +PatchRow::PatchRow(int32 producerID) + : BView(BRect(0,0,0,0), "PatchRow", B_FOLLOW_NONE, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED), + m_producerID(producerID), m_eventMeter(NULL) +{ + m_eventMeter = new MidiEventMeter(m_producerID); +} + +PatchRow::~PatchRow() +{ + delete m_eventMeter; +} + +int32 PatchRow::ID() const +{ + return m_producerID; +} + +void PatchRow::Pulse() +{ + if (m_eventMeter) m_eventMeter->Pulse(this); +} + +void PatchRow::Draw(BRect) +{ + if (m_eventMeter) m_eventMeter->Draw(this); +} + +void PatchRow::AddColumn(int32 consumerID) +{ + BRect r; + int32 numColumns = CountChildren(); + r.left = numColumns*COLUMN_WIDTH + METER_PADDING + kBoxOffset.x; + r.top = kBoxOffset.y; + r.right = r.left + 20; + r.bottom = r.top + 20; + + PatchCheckBox* box = new PatchCheckBox(r, m_producerID, consumerID); + AddChild(box); + box->SetTarget(this); +} + +void PatchRow::RemoveColumn(int32 consumerID) +{ + int32 numChildren = CountChildren(); + for (int32 i=0; i(ChildAt(i)); + if (box && box->ConsumerID() == consumerID) { + RemoveChild(box); + delete box; + while (i < numChildren) { + box = dynamic_cast(ChildAt(i++)); + if (box) { + box->MoveBy(-COLUMN_WIDTH, 0); + } + } + break; + } + } +} + +void PatchRow::Connect(int32 consumerID) +{ + int32 numChildren = CountChildren(); + for (int32 i=0; i(ChildAt(i)); + if (box && box->ConsumerID() == consumerID) { + box->SetValue(1); + } + } +} + +void PatchRow::Disconnect(int32 consumerID) +{ + int32 numChildren = CountChildren(); + for (int32 i=0; i(ChildAt(i)); + if (box && box->ConsumerID() == consumerID) { + box->SetValue(0); + } + } +} + +void PatchRow::AttachedToWindow() +{ + Window()->SetPulseRate(200000); + SetViewColor(Parent()->ViewColor()); + int32 numChildren = CountChildren(); + for (int32 i=0; i(ChildAt(i)); + if (box) { + box->SetTarget(this); + } + } +} + +void PatchRow::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case MSG_CONNECT_REQUEST: + { + BControl* ctrl; + if (msg->FindPointer("source", (void**) &ctrl) == B_OK) { + PatchCheckBox* box = dynamic_cast(ctrl); + if (box) { + box->DoConnect(); + } + } + } + break; + default: + BView::MessageReceived(msg); + break; + } +} + +void PatchCheckBox::DoConnect() +{ + int32 value = Value(); + int32 inverseValue = (value + 1) % 2; + + BMidiRoster* roster = BMidiRoster::MidiRoster(); + if (! roster) { + SetValue(inverseValue); + return; + } + + BMidiProducer* producer = roster->FindProducer(m_producerID); + BMidiConsumer* consumer = roster->FindConsumer(m_consumerID); + if (producer && consumer) { + status_t err; + if (value) { + err = producer->Connect(consumer); + } else { + err = producer->Disconnect(consumer); + } + + if (err != B_OK) { + SetValue(inverseValue); + } + } else { + SetValue(inverseValue); + } + if (producer) producer->Release(); + if (consumer) consumer->Release(); +} diff --git a/src/tests/kits/midi/patchbay/PatchRow.h b/src/tests/kits/midi/patchbay/PatchRow.h new file mode 100644 index 0000000000..a534313472 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchRow.h @@ -0,0 +1,38 @@ +#ifndef _PatchRow_h +#define _PatchRow_h + +#include + +extern const float ROW_LEFT; +extern const float ROW_TOP; +extern const float ROW_HEIGHT; +extern const float COLUMN_WIDTH; +extern const float METER_PADDING; +extern const uint32 MSG_CONNECT_REQUEST; + +class MidiEventMeter; + +class PatchRow : public BView +{ +public: + PatchRow(int32 producerID); + ~PatchRow(); + + int32 ID() const; + + void AttachedToWindow(); + void MessageReceived(BMessage* msg); + void Pulse(); + void Draw(BRect updateRect); + + void AddColumn(int32 consumerID); + void RemoveColumn(int32 consumerID); + void Connect(int32 consumerID); + void Disconnect(int32 consumerID); + +private: + int32 m_producerID; + MidiEventMeter* m_eventMeter; +}; + +#endif /* _PatchRow_h */ diff --git a/src/tests/kits/midi/patchbay/PatchView.cpp b/src/tests/kits/midi/patchbay/PatchView.cpp new file mode 100644 index 0000000000..8235577f89 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchView.cpp @@ -0,0 +1,462 @@ +// PatchView.cpp +// ------------- +// Implements the main PatchBay view class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EndpointInfo.h" +#include "PatchView.h" +#include "PatchRow.h" +#include "TToolTip.h" +#include "UnknownDeviceIcons.h" + +PatchView::PatchView(BRect r) + : BView(r, "PatchView", B_FOLLOW_ALL, B_WILL_DRAW), + m_unknownDeviceIcon(NULL), m_trackIndex(-1), m_trackType(TRACK_COLUMN) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + uint8 iconSize; + const uint8* iconData; + + if (DISPLAY_ICON_SIZE == B_LARGE_ICON) { + iconSize = LARGE_ICON_SIZE; + iconData = UnknownDevice::kLargeIconBits; + } else { + iconSize = MINI_ICON_SIZE; + iconData = UnknownDevice::kMiniIconBits; + } + + BRect iconFrame(0, 0, iconSize-1, iconSize-1); + m_unknownDeviceIcon = new BBitmap(iconFrame, ICON_COLOR_SPACE); + memcpy(m_unknownDeviceIcon->Bits(), iconData, m_unknownDeviceIcon->BitsLength()); +} + +PatchView::~PatchView() +{ + delete m_unknownDeviceIcon; +} + +void PatchView::AttachedToWindow() +{ + BMidiRoster* roster = BMidiRoster::MidiRoster(); + if (! roster) { + PRINT(("Couldn't get MIDI roster\n")); + be_app->PostMessage(B_QUIT_REQUESTED); + return; + } + + BMessenger msgr(this); + roster->StartWatching(&msgr); +} + +void PatchView::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case B_MIDI_EVENT: + HandleMidiEvent(msg); + break; + default: + BView::MessageReceived(msg); + break; + } +} + +void PatchView::MouseMoved(BPoint pt, uint32 transit, const BMessage* /* dragMsg */) +{ + if (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW) { + StopTipTracking(); + return; + } + + bool found = false; + int32 size = m_consumers.size(); + for (int32 i=0; (! found) && iFindEndpoint(i->ID()); + if (! obj) + return; + + BString str; + str << "<" << obj->ID() << ">: " << obj->Name(); + obj->Release(); + StartTip(pt, rect, str.String()); +} + +void PatchView::StartTip(BPoint pt, BRect rect, const char* str) +{ + BMessage msg(eToolTipStart); + msg.AddPoint("start", ConvertToScreen(pt)); + msg.AddRect("bounds", ConvertToScreen(rect)); + msg.AddString("string", str); + be_app->PostMessage(&msg); +} + +void PatchView::StopTip() +{ + be_app->PostMessage(eToolTipStop); +} + +void PatchView::Draw(BRect /* updateRect */) +{ + // draw producer icons + SetDrawingMode(B_OP_OVER); + int32 index = 0; + for (list::const_iterator i = m_producers.begin(); i != m_producers.end(); i++) { + const BBitmap* bitmap = (i->Icon()) ? i->Icon() : m_unknownDeviceIcon; + DrawBitmapAsync(bitmap, RowIconFrameAt(index++).LeftTop()); + } + + // draw consumer icons + index = 0; + for (list::const_iterator i = m_consumers.begin(); i != m_consumers.end(); i++) { + const BBitmap* bitmap = (i->Icon()) ? i->Icon() : m_unknownDeviceIcon; + DrawBitmapAsync(bitmap, ColumnIconFrameAt(index++).LeftTop()); + } +} + +BRect PatchView::ColumnIconFrameAt(int32 index) const +{ + BRect r; + r.left = ROW_LEFT + METER_PADDING + index*COLUMN_WIDTH; + r.top = 10; + r.right = r.left + 31; + r.bottom = r.top + 31; + return r; +} + +BRect PatchView::RowIconFrameAt(int32 index) const +{ + BRect r; + r.left = 10; + r.top = ROW_TOP + index*ROW_HEIGHT; + r.right = r.left + 31; + r.bottom = r.top + 31; + return r; +} + +void PatchView::HandleMidiEvent(BMessage* msg) +{ + SET_DEBUG_ENABLED(true); + + int32 op; + if (msg->FindInt32("be:op", &op) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"op\" field not found\n")); + return; + } + + switch (op) { + case B_MIDI_REGISTERED: + { + int32 id; + if (msg->FindInt32("be:id", &id) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:id\" field not found in B_MIDI_REGISTERED event\n")); + break; + } + + const char* type; + if (msg->FindString("be:type", &type) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:type\" field not found in B_MIDI_REGISTERED event\n")); + break; + } + + PRINT(("MIDI Roster Event B_MIDI_REGISTERED: id=%ld, type=%s\n", id, type)); + if (! strcmp(type, "producer")) { + AddProducer(id); + } else if (! strcmp(type, "consumer")) { + AddConsumer(id); + } + } + break; + case B_MIDI_UNREGISTERED: + { + int32 id; + if (msg->FindInt32("be:id", &id) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:id\" field not found in B_MIDI_UNREGISTERED\n")); + break; + } + + const char* type; + if (msg->FindString("be:type", &type) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:type\" field not found in B_MIDI_UNREGISTERED\n")); + break; + } + + PRINT(("MIDI Roster Event B_MIDI_UNREGISTERED: id=%ld, type=%s\n", id, type)); + if (! strcmp(type, "producer")) { + RemoveProducer(id); + } else if (! strcmp(type, "consumer")) { + RemoveConsumer(id); + } + } + break; + case B_MIDI_CHANGED_PROPERTIES: + { + int32 id; + if (msg->FindInt32("be:id", &id) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:id\" field not found in B_MIDI_CHANGED_PROPERTIES\n")); + break; + } + + const char* type; + if (msg->FindString("be:type", &type) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:type\" field not found in B_MIDI_CHANGED_PROPERTIES\n")); + break; + } + + BMessage props; + if (msg->FindMessage("be:properties", &props) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:properties\" field not found in B_MIDI_CHANGED_PROPERTIES\n")); + break; + } + + PRINT(("MIDI Roster Event B_MIDI_CHANGED_PROPERTIES: id=%ld, type=%s\n", id, type)); + if (! strcmp(type, "producer")) { + UpdateProducerProps(id, &props); + } else if (! strcmp(type, "consumer")) { + UpdateConsumerProps(id, &props); + } + + } + break; + case B_MIDI_CHANGED_NAME: + case B_MIDI_CHANGED_LATENCY: + // we don't care about these + break; + case B_MIDI_CONNECTED: + { + int32 prod; + if (msg->FindInt32("be:producer", &prod) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:producer\" field not found in B_MIDI_CONNECTED\n")); + break; + } + + int32 cons; + if (msg->FindInt32("be:consumer", &cons) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:consumer\" field not found in B_MIDI_CONNECTED\n")); + break; + } + PRINT(("MIDI Roster Event B_MIDI_CONNECTED: producer=%ld, consumer=%ld\n", prod, cons)); + Connect(prod, cons); + } + break; + case B_MIDI_DISCONNECTED: + { + int32 prod; + if (msg->FindInt32("be:producer", &prod) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:producer\" field not found in B_MIDI_DISCONNECTED\n")); + break; + } + + int32 cons; + if (msg->FindInt32("be:consumer", &cons) != B_OK) { + PRINT(("PatchView::HandleMidiEvent: \"be:consumer\" field not found in B_MIDI_DISCONNECTED\n")); + break; + } + PRINT(("MIDI Roster Event B_MIDI_DISCONNECTED: producer=%ld, consumer=%ld\n", prod, cons)); + Disconnect(prod, cons); + } + break; + default: + PRINT(("PatchView::HandleMidiEvent: unknown opcode %ld\n", op)); + break; + } +} + +void PatchView::AddProducer(int32 id) +{ + EndpointInfo info(id); + m_producers.push_back(info); + + Window()->BeginViewTransaction(); + PatchRow* row = new PatchRow(id); + m_patchRows.push_back(row); + BPoint p1 = CalcRowOrigin(m_patchRows.size() - 1); + BPoint p2 = CalcRowSize(); + row->MoveTo(p1); + row->ResizeTo(p2.x, p2.y); + for (list::const_iterator i = m_consumers.begin(); i != m_consumers.end(); i++) { + row->AddColumn(i->ID()); + } + AddChild(row); + Invalidate(); + Window()->EndViewTransaction(); +} + +void PatchView::AddConsumer(int32 id) +{ + EndpointInfo info(id); + m_consumers.push_back(info); + + Window()->BeginViewTransaction(); + BPoint newSize = CalcRowSize(); + for (row_itor i = m_patchRows.begin(); i != m_patchRows.end(); i++) { + (*i)->AddColumn(id); + (*i)->ResizeTo(newSize.x, newSize.y - 1); + } + Invalidate(); + Window()->EndViewTransaction(); +} + +void PatchView::RemoveProducer(int32 id) +{ + for (endpoint_itor i = m_producers.begin(); i != m_producers.end(); i++) { + if (i->ID() == id) { + m_producers.erase(i); + break; + } + } + + Window()->BeginViewTransaction(); + for (row_itor i = m_patchRows.begin(); i != m_patchRows.end(); i++) { + if ((*i)->ID() == id) { + PatchRow* row = *i; + i = m_patchRows.erase(i); + RemoveChild(row); + delete row; + float moveBy = -1*CalcRowSize().y; + while (i != m_patchRows.end()) { + (*i++)->MoveBy(0, moveBy); + } + break; + } + } + Invalidate(); + Window()->EndViewTransaction(); +} + +void PatchView::RemoveConsumer(int32 id) +{ + Window()->BeginViewTransaction(); + for (endpoint_itor i = m_consumers.begin(); i != m_consumers.end(); i++) { + if (i->ID() == id) { + m_consumers.erase(i); + break; + } + } + + BPoint newSize = CalcRowSize(); + for (row_itor i = m_patchRows.begin(); i != m_patchRows.end(); i++) { + (*i)->RemoveColumn(id); + (*i)->ResizeTo(newSize.x, newSize.y - 1); + } + Invalidate(); + Window()->EndViewTransaction(); +} + +void PatchView::UpdateProducerProps(int32 id, const BMessage* props) +{ + for (endpoint_itor i = m_producers.begin(); i != m_producers.end(); i++) { + if (i->ID() == id) { + i->UpdateProperties(props); + Invalidate(); + break; + } + } +} + +void PatchView::UpdateConsumerProps(int32 id, const BMessage* props) +{ + for (endpoint_itor i = m_consumers.begin(); i != m_consumers.end(); i++) { + if (i->ID() == id) { + i->UpdateProperties(props); + Invalidate(); + break; + } + } +} + +void PatchView::Connect(int32 prod, int32 cons) +{ + for (row_itor i = m_patchRows.begin(); i != m_patchRows.end(); i++) { + if ((*i)->ID() == prod) { + (*i)->Connect(cons); + break; + } + } +} + +void PatchView::Disconnect(int32 prod, int32 cons) +{ + for (row_itor i = m_patchRows.begin(); i != m_patchRows.end(); i++) { + if ((*i)->ID() == prod) { + (*i)->Disconnect(cons); + break; + } + } +} + +BPoint PatchView::CalcRowOrigin(int32 rowIndex) const +{ + BPoint pt; + pt.x = ROW_LEFT; + pt.y = ROW_TOP + rowIndex*ROW_HEIGHT; + return pt; +} + +BPoint PatchView::CalcRowSize() const +{ + BPoint pt; + pt.x = METER_PADDING + m_consumers.size()*COLUMN_WIDTH; + pt.y = ROW_HEIGHT - 1; + return pt; +} diff --git a/src/tests/kits/midi/patchbay/PatchView.h b/src/tests/kits/midi/patchbay/PatchView.h new file mode 100644 index 0000000000..9b36d55ed9 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchView.h @@ -0,0 +1,75 @@ +// PatchView.h +// ----------- +// The main PatchBay view contains a row of icons along the top and +// left sides representing available consumers and producers, and +// a set of PatchRows which build the matrix of connections. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _PatchView_h +#define _PatchView_h + +#include +#include +#include +#include "EndpointInfo.h" + +class PatchRow; +class BBitmap; +class TToolTip; + +using namespace std; + +class PatchView : public BView +{ +public: + PatchView(BRect r); + ~PatchView(); + + void AttachedToWindow(); + void MessageReceived(BMessage* msg); + void Draw(BRect updateRect); + void MouseMoved(BPoint point, uint32 transit, const BMessage* dragMsg); + +private: + typedef enum { + TRACK_COLUMN, + TRACK_ROW + } track_type; + + BRect ColumnIconFrameAt(int32 index) const; + BRect RowIconFrameAt(int32 index) const; + void StartTipTracking(BPoint pt, BRect rect, int32 index, track_type type=TRACK_COLUMN); + void StopTipTracking(); + void StartTip(BPoint pt, BRect rect, const char* str); + void StopTip(); + + void AddProducer(int32 id); + void AddConsumer(int32 id); + void RemoveProducer(int32 id); + void RemoveConsumer(int32 id); + void UpdateProducerProps(int32 id, const BMessage* props); + void UpdateConsumerProps(int32 id, const BMessage* props); + void Connect(int32 prod, int32 cons); + void Disconnect(int32 prod, int32 cons); + + void HandleMidiEvent(BMessage* msg); + + BPoint CalcRowOrigin(int32 rowIndex) const; + BPoint CalcRowSize() const; + + typedef list::iterator endpoint_itor; + typedef list::const_iterator const_endpoint_itor; + typedef list::iterator row_itor; + + list m_producers; + list m_consumers; + list m_patchRows; + BBitmap* m_unknownDeviceIcon; + int32 m_trackIndex; + track_type m_trackType; + TToolTip* m_toolTip; +}; + +#endif /* _PatchView_h */ diff --git a/src/tests/kits/midi/patchbay/PatchWin.cpp b/src/tests/kits/midi/patchbay/PatchWin.cpp new file mode 100644 index 0000000000..2511b85ab8 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchWin.cpp @@ -0,0 +1,26 @@ +// PatchWin.cpp +// ------------ +// Implements the main PatchBay window class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#include +#include "PatchWin.h" +#include "PatchView.h" + +PatchWin::PatchWin() + : BWindow(BRect(50,50,450,450), "Patch Bay", B_TITLED_WINDOW, 0) +{ + BRect r = Bounds(); + m_patchView = new PatchView(r); + AddChild(m_patchView); + Show(); +} + +bool PatchWin::QuitRequested() +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return true; +} + diff --git a/src/tests/kits/midi/patchbay/PatchWin.h b/src/tests/kits/midi/patchbay/PatchWin.h new file mode 100644 index 0000000000..a7ab58ed63 --- /dev/null +++ b/src/tests/kits/midi/patchbay/PatchWin.h @@ -0,0 +1,24 @@ +// PatchWin.h +// ---------- +// The main PatchBay window class. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _PatchWin_h +#define _PatchWin_h + +#include + +class PatchView; + +class PatchWin : public BWindow +{ +public: + PatchWin(); + bool QuitRequested(); +private: + PatchView* m_patchView; +}; + +#endif /* _PatchWin_h */ diff --git a/src/tests/kits/midi/patchbay/TToolTip.cpp b/src/tests/kits/midi/patchbay/TToolTip.cpp new file mode 100644 index 0000000000..17ee1ca82c --- /dev/null +++ b/src/tests/kits/midi/patchbay/TToolTip.cpp @@ -0,0 +1,357 @@ +//-------------------------------------------------------------------- +// +// TToolTip.cpp +// +// Written by: Robert Polic +// +//-------------------------------------------------------------------- + +#include +#include +#include +#include + +#include +#include + +#include "TToolTip.h" + +#define kHOR_MARGIN 4 // hor. gap between frame and tip +#define kVER_MARGIN 3 // ver. gap between frame and tip +#define kTIP_HOR_OFFSET 10 // tip position right of cursor +#define kTIP_VER_OFFSET 16 // tip position below cursor +#define kSLOP 4 // mouse slop before tip hides + +#define kTOOL_TIP_DELAY_TIME 500000 // default delay time before tip shows (.5 secs.) +#define kTOOL_TIP_HOLD_TIME 3000000 // default hold time of time (3 secs.) + +#define kDRAW_WINDOW_FRAME + +const rgb_color kVIEW_COLOR = {255, 203, 0, 255}; // view background color (light yellow) +const rgb_color kLIGHT_VIEW_COLOR = {255, 255, 80, 255}; // top left frame highlight +const rgb_color kDARK_VIEW_COLOR = {175, 123, 0, 255}; // bottom right frame highlight +const rgb_color kTEXT_COLOR = {0, 0, 0, 255}; // text color (black) + + +//==================================================================== + +TToolTip::TToolTip(tool_tip_settings *settings) + :BWindow(BRect(0, 0, 10, 10), "tool_tip", B_NO_BORDER_WINDOW_LOOK, + B_FLOATING_ALL_WINDOW_FEEL, B_AVOID_FRONT) +{ + // setup the tooltip view + AddChild(fView = new TToolTipView(settings)); + // start the message loop thread + Run(); +} + +//-------------------------------------------------------------------- + +void TToolTip::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + // forward interesting messages to the view + case B_SOME_APP_ACTIVATED: + case eToolTipStart: + case eToolTipStop: + PostMessage(msg, fView); + break; + default: + BWindow::MessageReceived(msg); + } +} + +//-------------------------------------------------------------------- + +void TToolTip::GetSettings(tool_tip_settings *settings) +{ + fView->GetSettings(settings); +} + +//-------------------------------------------------------------------- + +void TToolTip::SetSettings(tool_tip_settings *settings) +{ + fView->SetSettings(settings); +} + + +//==================================================================== + +TToolTipView::TToolTipView(tool_tip_settings *settings) + :BView(BRect(0, 0, 10, 10), "tool_tip", B_FOLLOW_ALL, B_WILL_DRAW) +{ + // initialize tooltip settings + if (settings) + // we should probably sanity-check user defined settings (but we won't) + fTip.settings = *settings; + else { + // use defaults if no settings are passed + fTip.settings.enabled = true; + fTip.settings.one_time_only = false; + fTip.settings.delay = kTOOL_TIP_DELAY_TIME; + fTip.settings.hold = kTOOL_TIP_HOLD_TIME; + fTip.settings.font = be_plain_font; + } + + // initialize the tip + fString = (char *)malloc(1); + fString[0] = 0; + + // initialize the view + SetFont(&fTip.settings.font); + SetViewColor(kVIEW_COLOR); +} + +//-------------------------------------------------------------------- + +TToolTipView::~TToolTipView() +{ + status_t status; + + // kill tool_tip thread + fTip.quit = true; + wait_for_thread(fThread, &status); + + // free tip + free(fString); +} + +//-------------------------------------------------------------------- + +void TToolTipView::AllAttached() +{ + // initialize internal settings + fTip.app_active = true; + fTip.quit = false; + fTip.stopped = true; + + fTip.tool_tip_view = this; + fTip.tool_tip_window = Window(); + + // start tool_tip thread + resume_thread(fThread = spawn_thread((status_t (*)(void *)) ToolTipThread, + "tip_thread", B_DISPLAY_PRIORITY, &fTip)); +} + +//-------------------------------------------------------------------- + +void TToolTipView::Draw(BRect /* where */) +{ + char *src_strings[1]; + char *tmp_string; + char *truncated_strings[1]; + BFont font; + BRect r = Bounds(); + font_height finfo; + + // draw border around window +#ifdef kDRAW_WINDOW_FRAME + SetHighColor(0, 0, 0, 255); + StrokeRect(r); + r.InsetBy(1, 1); +#endif + SetHighColor(kLIGHT_VIEW_COLOR); + StrokeLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top)); + StrokeLine(BPoint(r.left + 1, r.top), BPoint(r.right - 1, r.top)); + SetHighColor(kDARK_VIEW_COLOR); + StrokeLine(BPoint(r.right, r.top), BPoint(r.right, r.bottom)); + StrokeLine(BPoint(r.right - 1, r.bottom), BPoint(r.left + 1, r.bottom)); + + // set pen position + GetFont(&font); + font.GetHeight(&finfo); + MovePenTo(kHOR_MARGIN + 1, kVER_MARGIN + finfo.ascent); + + // truncate string if needed + src_strings[0] = fString; + tmp_string = (char *)malloc(strlen(fString) + 16); + truncated_strings[0] = tmp_string; + font.GetTruncatedStrings((const char **)src_strings, 1, B_TRUNCATE_END, + Bounds().Width() - (2 * kHOR_MARGIN) + 1, truncated_strings); + + // draw string + SetLowColor(kVIEW_COLOR); + SetHighColor(kTEXT_COLOR); + DrawString(tmp_string); + free(tmp_string); +} + +//-------------------------------------------------------------------- + +void TToolTipView::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case B_SOME_APP_ACTIVATED: + msg->FindBool("active", &fTip.app_active); + break; + + case eToolTipStart: + { + const char *str; + + // extract parameters + msg->FindPoint("start", &fTip.start); + msg->FindRect("bounds", &fTip.bounds); + msg->FindString("string", &str); + free(fString); + fString = (char *)malloc(strlen(str) + 1); + strcpy(fString, str); + + // force window to fit new parameters + AdjustWindow(); + + // flag thread to reset + fTip.reset = true; + } + break; + + case eToolTipStop: + // flag thread to stop + fTip.stop = true; + break; + } +} + +//-------------------------------------------------------------------- + +void TToolTipView::GetSettings(tool_tip_settings *settings) +{ + // return current settings + *settings = fTip.settings; +} + +//-------------------------------------------------------------------- + +void TToolTipView::SetSettings(tool_tip_settings *settings) +{ + bool invalidate = fTip.settings.font != settings->font; + + // we should probably sanity-check user defined settings (but we won't) + fTip.settings = *settings; + + // if the font changed, adjust window to fit + if (invalidate) { + Window()->Lock(); + SetFont(&fTip.settings.font); + AdjustWindow(); + Window()->Unlock(); + } +} + +//-------------------------------------------------------------------- + +void TToolTipView::AdjustWindow() +{ + float width; + float height; + float x; + float y; + BScreen s(B_MAIN_SCREEN_ID); + BRect screen = s.Frame(); + BWindow *wind = Window(); + font_height finfo; + + screen.InsetBy(2, 2); // we want a 2-pixel clearance + fTip.settings.font.GetHeight(&finfo); + width = fTip.settings.font.StringWidth(fString) + (kHOR_MARGIN * 2); // string width + height = (finfo.ascent + finfo.descent + finfo.leading) + (kVER_MARGIN * 2); // string height + + // calculate new position and size of window + x = fTip.start.x + kTIP_HOR_OFFSET; + if ((x + width) > screen.right) + x = screen.right - width; + y = fTip.start.y + kTIP_VER_OFFSET; + if ((y + height) > screen.bottom) { + y = screen.bottom - height; + if ((fTip.start.y >= (y - kSLOP)) && (fTip.start.y <= (y + height))) + y = fTip.start.y - kTIP_VER_OFFSET - height; + } + if (x < screen.left) { + width -= screen.left - x; + x = screen.left; + } + if (y < screen.top) { + height -= screen.top - y; + y = screen.top; + } + + wind->MoveTo((int)x, (int)y); + wind->ResizeTo((int)width, (int)height); + + // force an update + Invalidate(Bounds()); +} + +//-------------------------------------------------------------------- + +status_t TToolTipView::ToolTipThread(tool_tip *tip) +{ + uint32 buttons; + BPoint where; + BScreen s(B_MAIN_SCREEN_ID); + BRect screen = s.Frame(); + + screen.InsetBy(2, 2); + while (!tip->quit) { + if (tip->tool_tip_window->LockWithTimeout(0) == B_NO_ERROR) { + tip->tool_tip_view->GetMouse(&where, &buttons); + tip->tool_tip_view->ConvertToScreen(&where); + + tip->stopped = tip->stop; + if (tip->reset) { + if (tip->showing) + tip->tool_tip_window->Hide(); + tip->stop = false; + tip->stopped = false; + tip->reset = false; + tip->shown = false; + tip->showing = false; + tip->start_time = system_time() + tip->settings.delay; + } + else if (tip->showing) { + if ((tip->stop) || + (!tip->settings.enabled) || + (!tip->app_active) || + (!tip->bounds.Contains(where)) || + (tip->expire_time < system_time()) || + (abs((int)tip->start.x - (int)where.x) > kSLOP) || + (abs((int)tip->start.y - (int)where.y) > kSLOP) || + (buttons)) { + tip->tool_tip_window->Hide(); + tip->shown = tip->settings.one_time_only; + tip->showing = false; + tip->tip_timed_out = (tip->expire_time < system_time()); + tip->start_time = system_time() + tip->settings.delay; + } + } + else if ((tip->settings.enabled) && + (!tip->stopped) && + (tip->app_active) && + (!tip->shown) && + (!tip->tip_timed_out) && + (!buttons) && + (tip->bounds.Contains(where)) && + (tip->start_time < system_time())) { + tip->start = where; + tip->tool_tip_view->AdjustWindow(); + tip->tool_tip_window->Show(); + tip->tool_tip_window->Activate(false); + tip->showing = true; + tip->expire_time = system_time() + tip->settings.hold; + tip->start = where; + } + else if ((abs((int)tip->start.x - (int)where.x) > kSLOP) || + (abs((int)tip->start.y - (int)where.y) > kSLOP)) { + tip->start = where; + tip->start_time = system_time() + tip->settings.delay; + tip->tip_timed_out = false; + } + if (buttons) + tip->start_time = system_time() + tip->settings.delay; + tip->tool_tip_window->Unlock(); + } + snooze(50000); + } + return B_NO_ERROR; +} \ No newline at end of file diff --git a/src/tests/kits/midi/patchbay/TToolTip.h b/src/tests/kits/midi/patchbay/TToolTip.h new file mode 100644 index 0000000000..1288d55455 --- /dev/null +++ b/src/tests/kits/midi/patchbay/TToolTip.h @@ -0,0 +1,86 @@ +//-------------------------------------------------------------------- +// +// TToolTip.h +// +// Written by: Robert Polic +// +//-------------------------------------------------------------------- + +#ifndef T_TOOL_TIPS_H +#define T_TOOL_TIPS_H + +#include +#include +#include + +enum TOOL_TIP_MESSAGES {eToolTipStart = 'ttGo', + eToolTipStop = 'ttSp'}; + +// ui settings +struct tool_tip_settings { + bool enabled; // flag whether tips are enables or not + bool one_time_only; // flag to only display the tip once per time in view + bigtime_t delay; // delay before tip is shown in microseconds + bigtime_t hold; // amount of time tip is displayed in microseconds + BFont font; // font tip is drawn in +}; + +// internal settings +struct tool_tip { + bool app_active; + bool quit; + bool stop; + bool stopped; + bool reset; + bool shown; + bool showing; + bool tip_timed_out; + BPoint start; + BRect bounds; + class TToolTipView *tool_tip_view; + BWindow *tool_tip_window; + bigtime_t start_time; + bigtime_t expire_time; + tool_tip_settings settings; +}; + + +//==================================================================== + +class TToolTip : public BWindow { + +public: + TToolTip(tool_tip_settings *settings = NULL); + virtual void MessageReceived(BMessage*); + + void GetSettings(tool_tip_settings*); + void SetSettings(tool_tip_settings*); + +private: + class TToolTipView *fView; +}; + + +//==================================================================== + +class TToolTipView : public BView { + +public: + TToolTipView(tool_tip_settings *settings = NULL); + ~TToolTipView(); + virtual void AllAttached(); + virtual void Draw(BRect); + virtual void MessageReceived(BMessage*); + + void GetSettings(tool_tip_settings*); + void SetSettings(tool_tip_settings*); + +private: + void AdjustWindow(); + static status_t ToolTipThread(tool_tip*); + + char *fString; + thread_id fThread; + tool_tip fTip; +}; +#endif diff --git a/src/tests/kits/midi/patchbay/UnknownDeviceIcons.h b/src/tests/kits/midi/patchbay/UnknownDeviceIcons.h new file mode 100644 index 0000000000..27efec70eb --- /dev/null +++ b/src/tests/kits/midi/patchbay/UnknownDeviceIcons.h @@ -0,0 +1,106 @@ +// UnknownDeviceIcons.h +// -------------------- +// The icons to be used in case a device doesn't supply its icons. +// In the future, this could be better selected if the device supported +// other descriptive property fields. +// +// Large and mini versions are available. PatchBay currently uses the +// large version. +// +// Copyright 1999, Be Incorporated. All Rights Reserved. +// This file may be used under the terms of the Be Sample Code License. + +#ifndef _UnknownDeviceIcons_h +#define _UnknownDeviceIcons_h + +namespace UnknownDevice { + +const unsigned char kLargeIconBits [] = { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xff, + 0xff,0xff,0xff,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, + 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x0c,0x0c, + 0xff,0xff,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x08,0x0c, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x0d,0x04,0x00,0x00,0x04, + 0x0d,0x1b,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x16,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x12,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x16,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x0d,0x00,0x00,0x00,0x0d,0x1b,0x3f,0x16, + 0x04,0x00,0x00,0x09,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x04,0x00,0x00,0x0d,0x3f,0x3f,0x3f,0x3f, + 0x16,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x1b,0x3f,0x3f,0x3f,0x3f, + 0x1b,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x1b, + 0x04,0x00,0x00,0x04,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x0d,0x00, + 0x00,0x00,0x00,0x16,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x1b,0x00,0x00,0x00, + 0x00,0x0d,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x04,0x00,0x00,0x09, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x3f, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0x08,0xff, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x04,0xff,0xff, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +}; + +const unsigned char kMiniIconBits [] = { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xff, + 0xff,0xff,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x0c,0x0c, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x16,0x00,0x00,0x00,0x1b,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x00,0x16,0x3f,0x1b,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x1b,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x09,0x00,0x1b,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x1b,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0x0c, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0x08,0xff, + 0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x00,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +}; + +} + +#endif /* _UnknownDeviceIcons_h */