jsweeper/src/matrix.cpp

741 lines
19 KiB
C++

#include <iostream>
#include <cmath>
#include <unistd.h>
#include "jack.h"
#include "portmanager.h"
#include "matrix.h"
#include "config.h"
const Rect Rect::None = Rect(0, 0, -1, -1);
Matrix::Matrix(PortType port_type) :
m_client_width(0), m_client_height(0), m_port_width(0), m_port_height(0), m_separation(0), m_x_rect(Rect::None), m_y_rect(Rect::None), m_port_type(port_type)
{
set_size_request(0, 0);
show();
add_events(Gdk::BUTTON_PRESS_MASK);
add_events(Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
}
Matrix::~Matrix()
{
}
void Matrix::Refresh()
{
Glib::RefPtr<Gdk::Window> window = get_window();
if (!window) return;
m_client_width = 0;
m_client_height = 0;
m_port_width = 0;
m_port_height = 0;
m_separation = 0;
pm.Sort();
int cols = 0;
int rows = 0;
Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
cr->select_font_face(cfg.FontFace, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL);
cr->set_font_size(cfg.FontSize);
PortList::iterator plit;
for (plit = pm.m_ports.begin(); plit != pm.m_ports.end(); ++plit) {
Port *p = *plit;
if (m_port_type != PT_ALL && m_port_type != (p->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
p->rect = Rect::None;
}
ClientList::iterator clit;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
cr->get_text_extents(c->m_name, c->extents);
int width = c->extents.width + cfg.CellPadding * 2;
if (c->m_is_input) {
if (width > m_client_height) m_client_height = width;
} else {
if (width > m_client_width) m_client_width = width;
}
if (c->extents.height > m_separation) m_separation = c->extents.height;
if (!c->m_expanded) {
if (c->m_is_input) {
cols++;
} else {
rows++;
}
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
pg->rect = Rect::None;
}
continue;
}
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
cr->get_text_extents(pg->m_name, pg->extents);
if (pg->extents.height > m_separation) m_separation = pg->extents.height;
if (!pg->m_expanded && pg->m_name != "") {
int width = pg->extents.width + cfg.CellPadding * 2;
if (c->m_is_input) {
if (width > m_port_height) m_port_height = width;
cols++;
} else {
if (width > m_port_width) m_port_width = width;
rows++;
}
continue;
}
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
cr->get_text_extents(p->m_name, p->extents);
int width = pg->extents.width + p->extents.width + cfg.CellPadding * (pg->extents.width > 0 ? 4 : 2);
if (p->m_is_input) {
if (width > m_port_height) m_port_height = width;
cols++;
} else {
if (width > m_port_width) m_port_width = width;
rows++;
}
if (p->extents.height > m_separation) m_separation = p->extents.height;
}
}
}
/*
PortList::iterator plit;
for (plit = pm.m_ports.begin(); plit != pm.m_ports.end(); ++plit) {
Port *p = *plit;
cr->get_text_extents(p->m_name, p->extents);
if (p->m_flags & JackPortIsInput) {
if (p->extents.width > m_port_height) m_port_height = p->extents.width;
} else {
if (p->extents.width > m_port_width) m_port_width = p->extents.width;
}
if (p->extents.height > m_separation) m_separation = p->extents.height;
}
*/
m_separation += cfg.CellPadding * 2;
// if (m_port_width > m_separation * 5) m_port_width = m_separation * 5;
// if (m_port_height > m_separation * 5) m_port_height = m_separation * 5;
int y = m_client_height + m_port_height + cfg.CellPadding;
int x = m_client_width + m_port_width + cfg.CellPadding;
across.clear();
down.clear();
Rect r;
// ClientList::iterator clit;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
if (c->m_is_input) {
r.left = x;
r.top = cfg.CellPadding;
r.width = -1;
r.height = m_client_height;
c->rect = r;
if (!c->m_expanded) {
c->rect.width = m_separation;
c->rect.height = m_client_height + m_port_height;
across.push_back(c);
x += m_separation;
continue;
}
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
if (pg->m_name != "") {
r.left = x;
r.top = cfg.CellPadding + m_client_height;
r.width = pg->m_expanded ? m_separation * pg->m_ports.size() : m_separation;
r.height = pg->m_expanded ? pg->extents.width + cfg.CellPadding * 2 : m_port_height;
pg->rect = r;
if (!pg->m_expanded) {
c->rect.width = pg->rect.left - c->rect.left + m_separation;
across.push_back(pg);
x += m_separation;
continue;
}
r.top += r.height;
r.width = m_separation;
r.height = m_port_height - r.height;
} else {
pg->rect = Rect::None;
r.top = cfg.CellPadding + m_client_height;
r.width = m_separation;
r.height = m_port_height;
}
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
r.left = x;
p->rect = r;
c->rect.width = p->rect.left - c->rect.left + m_separation;
if (pg->rect != Rect::None) {
pg->rect.width = p->rect.left - pg->rect.left + m_separation;
}
across.push_back(p);
x += m_separation;
}
}
} else {
r.left = cfg.CellPadding;
r.top = y;
r.width = m_client_width;
r.height = -1;
c->rect = r;
if (!c->m_expanded) {
c->rect.width = m_client_width + m_port_width;
c->rect.height = m_separation;
down.push_back(c);
y += m_separation;
continue;
}
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
if (pg->m_name != "") {
r.left = cfg.CellPadding + m_client_width;
r.top = y;
r.width = pg->m_expanded ? pg->extents.width + cfg.CellPadding * 2 : m_port_width;
r.height = pg->m_expanded ? m_separation * pg->m_ports.size() : m_separation;
pg->rect = r;
if (!pg->m_expanded) {
c->rect.height = pg->rect.top - c->rect.top + m_separation;
down.push_back(pg);
y += m_separation;
continue;
}
r.left += r.width;
r.width = m_port_width - r.width;
r.height = m_separation;
} else {
pg->rect = Rect::None;
r.left = cfg.CellPadding + m_client_width;
r.width = m_port_width;
r.height = m_separation;
}
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
r.top = y;
p->rect = r;
c->rect.height = p->rect.top - c->rect.top + m_separation;
if (pg->rect != Rect::None) {
pg->rect.height = p->rect.top - pg->rect.top + m_separation;
}
down.push_back(p);
y += m_separation;
}
}
}
}
int w = cfg.CellPadding * 2 + m_client_width + m_port_width;
int h = cfg.CellPadding * 2 + m_client_height + m_port_height;
w += cols * m_separation;
h += rows * m_separation;
m_width = w;
m_height = h;
set_size_request(w, h);
}
static void SetColour(Cairo::RefPtr<Cairo::Context> cr, Colour c)
{
cr->set_source_rgb(c.r, c.g, c.b);
}
void Matrix::Box(Cairo::RefPtr<Cairo::Context> cr, Colour &c, Rect &rect, bool highlight)
{
SetColour(cr, c * (highlight ? 1.5 : 1.0));
cr->rectangle(rect.left, rect.top, rect.width, rect.height);
cr->fill();
if (highlight) {
cr->set_source_rgb(1.0, 1.0, 1.0);
} else {
cr->set_source_rgb(0.75, 0.75, 0.75);
}
cr->rectangle(rect.left, rect.top, rect.width, rect.height);
cr->stroke();
SetColour(cr, cfg.Text * (highlight ? 1.5 : 1.0));
}
bool Matrix::on_expose_event(GdkEventExpose *event)
{
Glib::RefPtr<Gdk::Window> window = get_window();
if (!window) return true;
if (m_client_width == 0) Refresh();
Gtk::Allocation allocation = get_allocation();
const int alloc_width = allocation.get_width();
const int alloc_height = allocation.get_height();
Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
// Set clipping area
cr->rectangle(event->area.x, event->area.y, event->area.width, event->area.height);
cr->clip();
cr->set_line_width(1.0);
if (!cfg.Antialias) {
cr->set_antialias(Cairo::ANTIALIAS_NONE);
}
cr->translate(0.5, 0.5);
// Clear background
SetColour(cr, cfg.Background);
cr->rectangle(0, 0, alloc_width, alloc_height);
cr->fill();
cr->set_source_rgb(1, 1, 1);
cr->select_font_face(cfg.FontFace, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL);
cr->set_font_size(cfg.FontSize);
Rect tr;
Rect &r = tr;
ClientList::iterator clit;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
r = c->rect;
if (c->m_is_input) {
Box(cr, cfg.TabClient, r, m_y_rect == r);
cr->save();
cr->move_to(r.left + cfg.CellPadding - c->extents.y_bearing, r.top + r.height - cfg.CellPadding - c->extents.x_bearing);
cr->rotate(2 * M_PI * 0.75);
cr->show_text(c->m_name);
cr->restore();
if (!c->m_expanded) {
SetColour(cr, cfg.GridLinesClient);
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(r.left + r.width, m_height - cfg.CellPadding);
cr->stroke();
continue;
}
PortGroupList::iterator pglast = c->m_groups.end();
--pglast;
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
r = pg->rect;
if (pg->m_name != "") {
Box(cr, cfg.TabGroup, r, m_y_rect == r);
cr->save();
cr->move_to(r.left + cfg.CellPadding - pg->extents.y_bearing, r.top + r.height - cfg.CellPadding - pg->extents.x_bearing);
cr->rotate(2 * M_PI * 0.75);
cr->show_text(pg->m_name);
cr->restore();
if (!pg->m_expanded) {
if (pglit == pglast) {
SetColour(cr, cfg.GridLinesClient);
} else {
SetColour(cr, cfg.GridLinesGroup);
}
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(r.left + r.width, m_height - cfg.CellPadding);
cr->stroke();
continue;
}
}
PortList::iterator plast = pg->m_ports.end();
--plast;
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
r = p->rect;
Box(cr, p->m_is_midi ? cfg.TabPortMidi : cfg.TabPortAudio, r, m_y_rect == r);
cr->save();
cr->move_to(r.left + cfg.CellPadding - p->extents.y_bearing, r.top + r.height - cfg.CellPadding - p->extents.x_bearing);
cr->rotate(2 * M_PI * 0.75);
cr->show_text(p->m_name);
cr->restore();
if (pglit == pglast && plit == plast) {
SetColour(cr, cfg.GridLinesClient);
} else if (plit == plast) {
SetColour(cr, cfg.GridLinesGroup);
} else {
SetColour(cr, cfg.GridLinesPort);
}
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(r.left + r.width, m_height - cfg.CellPadding);
cr->stroke();
}
}
} else {
Box(cr, cfg.TabClient, r, m_x_rect == r);
cr->move_to(r.left + cfg.CellPadding - c->extents.x_bearing, r.top + cfg.CellPadding - c->extents.y_bearing);
cr->show_text(c->m_name);
if (!c->m_expanded) {
SetColour(cr, cfg.GridLinesClient);
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(m_width - cfg.CellPadding, r.top + r.height);
cr->stroke();
continue;
}
PortGroupList::iterator pglast = c->m_groups.end();
--pglast;
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
r = pg->rect;
if (pg->m_name != "") {
Box(cr, cfg.TabGroup, r, m_x_rect == r);
cr->move_to(r.left + cfg.CellPadding - pg->extents.x_bearing, r.top + cfg.CellPadding - pg->extents.y_bearing);
cr->show_text(pg->m_name);
if (!pg->m_expanded) {
if (pglit == pglast) {
SetColour(cr, cfg.GridLinesClient);
} else {
SetColour(cr, cfg.GridLinesGroup);
}
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(m_width - cfg.CellPadding, r.top + r.height);
cr->stroke();
continue;
}
}
PortList::iterator plast = pg->m_ports.end();
--plast;
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
r = p->rect;
Box(cr, p->m_is_midi ? cfg.TabPortMidi : cfg.TabPortAudio, r, m_x_rect == r);
cr->move_to(r.left + cfg.CellPadding - p->extents.x_bearing, r.top + cfg.CellPadding - p->extents.y_bearing);
cr->show_text(p->m_name);
if (pglit == pglast && plit == plast) {
SetColour(cr, cfg.GridLinesClient);
} else if (plit == plast) {
SetColour(cr, cfg.GridLinesGroup);
} else {
SetColour(cr, cfg.GridLinesPort);
}
cr->move_to(r.left + r.width, r.top + r.height);
cr->line_to(m_width - cfg.CellPadding, r.top + r.height);
cr->stroke();
}
}
}
}
float radius = (float)(m_separation - cfg.CellPadding) * 0.4;
int x = m_client_width + m_port_width + cfg.CellPadding;
BaseList::iterator bit1;
for (bit1 = across.begin(); bit1 != across.end(); ++bit1) {
BaseList::iterator bit2;
Base *b1 = *bit1;
int y = m_client_height + m_port_height + cfg.CellPadding;
for (bit2 = down.begin(); bit2 != down.end(); ++bit2) {
Base *b2 = *bit2;
ConnectionMode cm = b1->ConnectedTo(b2);
if (cm != CM_NONE) {
if (b1->m_is_midi) {
SetColour(cr, cfg.TabPortMidi * 1.5);
} else {
SetColour(cr, cfg.TabPortAudio * 1.5);
}
//cr->rectangle(x + 1, y + 1, m_separation - 2, m_separation - 2);
cr->arc((float)x + m_separation * 0.5, (float)y + m_separation * 0.5, radius, 0, 2 * M_PI);
cr->fill();
}
y += m_separation;
}
x += m_separation;
}
if (m_x_rect && m_y_rect && m_x_is_midi == m_y_is_midi) {
int x = m_client_width + m_port_width + cfg.CellPadding;
int y = m_client_height + m_port_height + cfg.CellPadding;
float line_x = (float)m_y_rect.left + m_separation * 0.5;
float line_y = (float)m_x_rect.top + m_separation * 0.5;
SetColour(cr, cfg.Text);
cr->move_to(x, line_y);
cr->line_to(line_x - radius, line_y);
cr->move_to(line_x, y);
cr->line_to(line_x, line_y - radius);
cr->stroke();
cr->arc(line_x, line_y, radius, 0, 2 * M_PI);
cr->stroke();
}
return true;
}
bool Matrix::on_button_press_event(GdkEventButton *event)
{
// Adjust for window borders
int x = event->x - 1;
int y = event->y - 1;
// Check expandable boxes
ClientList::iterator clit;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
if (c->rect.Hit(x, y)) {
c->m_expanded = !c->m_expanded;
Refresh();
queue_draw();
return true;
}
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
if (pg->rect.Hit(x, y)) {
pg->m_expanded = !pg->m_expanded;
Refresh();
queue_draw();
return true;
}
}
}
// Nothing selected...
if (!m_x_rect || !m_y_rect) return false;
// Check port hit
PortGroup *pg1 = NULL;
PortGroup *pg2 = NULL;
Port *p1 = NULL;
Port *p2 = NULL;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
PortGroupList::iterator pglit;
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end(); ++pglit) {
PortGroup *pg = *pglit;
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end(); ++plit) {
Port *p = *plit;
if (p->rect.HitX(x)) p1 = p;
if (p->rect.HitY(y)) p2 = p;
if (p1 != NULL && p2 != NULL) break;
if (p1 != NULL && p2 != NULL) {
pm.ToggleConnect(p1, p2);
return true;
}
}
if (pg->rect.HitX(x)) pg1 = pg;
if (pg->rect.HitY(y)) pg2 = pg;
if (pg1 != NULL && pg2 != NULL) break;
}
}
if (event->button == 3) {
// Right button pressed
// Try groups first
if (pg1 != NULL && pg2 != NULL) {
pm.ToggleConnect(pg1, pg2);
return true;
}
if (pg1 != NULL && p2 != NULL) {
pm.ToggleConnect(pg1, p2);
return true;
}
if (p1 != NULL && pg2 != NULL) {
pm.ToggleConnect(p1, pg2);
return true;
}
if (p1 != NULL && p2 != NULL) {
pm.ToggleConnect(p1, p2);
return true;
}
} else if (event->button == 1) {
// Left button pressed
// Try ports first
if (p1 != NULL && p2 != NULL) {
pm.ToggleConnect(p1, p2);
return true;
}
if (p1 != NULL && pg2 != NULL) {
pm.ToggleConnect(p1, pg2);
return true;
}
if (pg1 != NULL && p2 != NULL) {
pm.ToggleConnect(pg1, p2);
return true;
}
if (pg1 != NULL && pg2 != NULL) {
pm.ToggleConnect(pg1, pg2);
return true;
}
}
return false;
}
bool Matrix::on_motion_notify_event(GdkEventMotion *event)
{
int x = event->x;
int y = event->y;
Gdk::ModifierType state = Gdk::ModifierType(event->state);
if (event->is_hint) {
get_window()->get_pointer(x, y, state);
}
x -= 1;
y -= 1;
Rect x_rect = Rect::None;
Rect y_rect = Rect::None;
bool x_set = false;
bool y_set = false;
ClientList::iterator clit;
PortGroupList::iterator pglit;
PortList::iterator plit;
for (clit = pm.m_clients.begin(); clit != pm.m_clients.end(); ++clit) {
Client *c = *clit;
if (m_port_type != PT_ALL && m_port_type != (c->m_is_midi ? PT_MIDI : PT_AUDIO)) continue;
if (c->m_is_input) {
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end() && !y_rect; ++pglit) {
PortGroup *pg = *pglit;
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end() && !y_rect; ++plit) {
Port *p = *plit;
p->rect.SetIfHitX(x, y_rect);
}
pg->rect.SetIfHitX(x, y_rect);
}
c->rect.SetIfHitX(x, y_rect);
if (y_rect && !y_set) { m_y_is_midi = c->m_is_midi; y_set = true; }
} else {
for (pglit = c->m_groups.begin(); pglit != c->m_groups.end() && !x_rect; ++pglit) {
PortGroup *pg = *pglit;
PortList::iterator plit;
for (plit = pg->m_ports.begin(); plit != pg->m_ports.end() && !x_rect; ++plit) {
Port *p = *plit;
p->rect.SetIfHitY(y, x_rect);
}
pg->rect.SetIfHitY(y, x_rect);
}
c->rect.SetIfHitY(y, x_rect);
if (x_rect && !x_set) { m_x_is_midi = c->m_is_midi; x_set = true; }
}
if (x_rect && y_rect) break;
}
// If either box is unselected, make sure both are.
if (!x_rect || !y_rect) {
x_rect = Rect::None;
y_rect = Rect::None;
}
// If the selection has changed, redraw matrix.
if (x_rect != m_x_rect || y_rect != m_y_rect) {
m_x_rect = x_rect;
m_y_rect = y_rect;
queue_draw();
}
return true;
}