You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/widget/relations/kexirelationview.cpp

640 lines
17 KiB

/* This file is part of the KDE project
Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <kdebug.h>
#include <qstringlist.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qheader.h>
#include <qevent.h>
#include <qpainter.h>
#include <qstyle.h>
#include <qlineedit.h>
#include <qpopupmenu.h>
#include <klocale.h>
#include <kaction.h>
#include <kpopupmenu.h>
#include <kglobalsettings.h>
#include <kmessagebox.h>
#include <kexidb/tableschema.h>
#include <kexidb/indexschema.h>
#include <kexidb/utils.h>
#include "kexirelationview.h"
#include "kexirelationviewtable.h"
#include "kexirelationviewconnection.h"
#include <kexi.h>
KexiRelationView::KexiRelationView(QWidget *parent, const char *name)
: QScrollView(parent, name, WStaticContents)
{
// m_relation=relation;
// m_relation->incUsageCount();
m_selectedConnection = 0;
m_readOnly=false;
m_focusedTableView = 0;
setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
// connect(relation, SIGNAL(relationListUpdated(QObject *)), this, SLOT(slotListUpdate(QObject *)));
viewport()->setPaletteBackgroundColor(colorGroup().mid());
setFocusPolicy(WheelFocus);
setResizePolicy(Manual);
/*MOVED TO KexiRelationDialog
//actions
m_tableQueryPopup = new KPopupMenu(this, "m_popup");
m_tableQueryPopup->insertTitle(i18n("Table"));
m_connectionPopup = new KPopupMenu(this, "m_connectionPopup");
m_connectionPopup->insertTitle(i18n("Relation"));
m_areaPopup = new KPopupMenu(this, "m_areaPopup");
plugSharedAction("edit_delete", i18n("Hide Table"), m_tableQueryPopup);
plugSharedAction("edit_delete",m_connectionPopup);
plugSharedAction("edit_delete",this, SLOT(removeSelectedObject()));
*/
#if 0
m_removeSelectedTableQueryAction = new KAction(i18n("&Hide Selected Table/Query"), "editdelete", "",
this, SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery");
m_removeSelectedConnectionAction = new KAction(i18n("&Remove Selected Relationship"), "button_cancel", "",
this, SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection");
m_openSelectedTableQueryAction = new KAction(i18n("&Open Selected Table/Query"), "", "",
this, SLOT(openSelectedTableQuery()), 0/*parent->actionCollection()*/, "relationsview_openSelectedTableQuery");
#endif
// invalidateActions();
#if 0
m_popup = new KPopupMenu(this, "m_popup");
m_openSelectedTableQueryAction->plug( m_popup );
m_removeSelectedTableQueryAction->plug( m_popup );
m_removeSelectedConnectionAction->plug( m_popup );
invalidateActions();
#endif
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, true);
}
KexiRelationView::~KexiRelationView()
{
}
/*KexiRelationViewTableContainer*
KexiRelationView::containerForTable(KexiDB::TableSchema* tableSchema)
{
if (!tableSchema)
return 0;
for (TablesDictIterator it(m_tables); it.current(); ++it) {
if (it.current()->schema()->table()==tableSchema)
return it.current();
}
return 0;
}*/
KexiRelationViewTableContainer *
KexiRelationView::tableContainer(KexiDB::TableSchema *t) const
{
return t ? m_tables.find(t->name()) : 0;
}
KexiRelationViewTableContainer*
KexiRelationView::addTableContainer(KexiDB::TableSchema *t, const QRect &rect)
{
if(!t)
return 0;
kdDebug() << "KexiRelationView::addTable(): " << t->name() << ", " << viewport() << endl;
KexiRelationViewTableContainer* c = tableContainer(t);
if (c) {
kdWarning() << "KexiRelationView::addTable(): table already added" << endl;
return c;
}
c = new KexiRelationViewTableContainer(this,
/*! @todo what about query? */
new KexiDB::TableOrQuerySchema(t)
);
connect(c, SIGNAL(endDrag()), this, SLOT(slotTableViewEndDrag()));
connect(c, SIGNAL(gotFocus()), this, SLOT(slotTableViewGotFocus()));
// connect(c, SIGNAL(headerContextMenuRequest(const QPoint&)),
// this, SLOT(tableHeaderContextMenuRequest(const QPoint&)));
connect(c, SIGNAL(contextMenuRequest(const QPoint&)),
this, SIGNAL(tableContextMenuRequest(const QPoint&)));
addChild(c, 100,100);
if (rect.isValid()) {//predefined size
QSize finalSize = c->size().expandedTo( c->sizeHint() );
QRect r = rect;
r.setSize( finalSize + QSize(0,10) );
moveChild( c, rect.left(), rect.top() );
//we're doing this instead of setGeometry(rect)
//because the geomenty might be saved on other system with bigger fonts :)
c->resize(c->sizeHint());
// c->setGeometry(r);
//TODO
// moveChild( c, rect.left(), rect.top() ); // setGeometry(rect);
// c->resize( finalSize );
// c->updateGeometry();
}
c->show();
updateGeometry();
if (!rect.isValid()) {
c->updateGeometry();
c->resize(c->sizeHint());
}
int x, y;
if(m_tables.count() > 0)
{
int place = -10;
QDictIterator<KexiRelationViewTableContainer> it(m_tables);
for(; it.current(); ++it)
{
int right = (*it)->x() + (*it)->width();
if(right > place)
place = right;
}
x = place + 30;
}
else
{
x = 5;
}
y = 5;
QPoint p = viewportToContents(QPoint(x, y));
recalculateSize(p.x() + c->width(), p.y() + c->height());
if (!rect.isValid()) {
moveChild(c, x, y);
}
m_tables.insert(t->name(), c);
connect(c, SIGNAL(moved(KexiRelationViewTableContainer *)), this,
SLOT(containerMoved(KexiRelationViewTableContainer *)));
if (hasFocus()) //ok?
c->setFocus();
return c;
}
void
KexiRelationView::addConnection(const SourceConnection& _conn)
{
SourceConnection conn = _conn;
kdDebug() << "KexiRelationView::addConnection()" << endl;
KexiRelationViewTableContainer *master = m_tables[conn.masterTable];
KexiRelationViewTableContainer *details = m_tables[conn.detailsTable];
if (!master || !details)
return;
/*! @todo what about query? */
KexiDB::TableSchema *masterTable = master->schema()->table();
/*! @todo what about query? */
KexiDB::TableSchema *detailsTable = details->schema()->table();
if (!masterTable || !detailsTable)
return;
// ok, but we need to know where is the 'master' and where is the 'details' side:
KexiDB::Field *masterFld = masterTable->field(conn.masterField);
KexiDB::Field *detailsFld = detailsTable->field(conn.detailsField);
if (!masterFld || !detailsFld)
return;
if (!masterFld->isUniqueKey()) {
if (detailsFld->isUniqueKey()) {
//SWAP:
KexiDB::Field *tmpFld = masterFld;
masterFld = detailsFld;
detailsFld = tmpFld;
KexiDB::TableSchema *tmpTable = masterTable;
masterTable = detailsTable;
detailsTable = tmpTable;
KexiRelationViewTableContainer *tmp = master;
master = details;
details = tmp;
QString tmp_masterTable = conn.masterTable;
conn.masterTable = conn.detailsTable;
conn.detailsTable = tmp_masterTable;
QString tmp_masterField = conn.masterField;
conn.masterField = conn.detailsField;
conn.detailsField = tmp_masterField;
}
}
// kdDebug() << "KexiRelationView::addConnection(): finalSRC = " << m_tables[conn.srcTable] << endl;
KexiRelationViewConnection *connView = new KexiRelationViewConnection(master, details, conn, this);
m_connectionViews.append(connView);
updateContents(connView->connectionRect());
/*js: will be moved up to relation/query part as this is only visual class
KexiDB::TableSchema *mtable = m_conn->tableSchema(conn.srcTable);
KexiDB::TableSchema *ftable = m_conn->tableSchema(conn.rcvTable);
KexiDB::IndexSchema *forign = new KexiDB::IndexSchema(ftable);
forign->addField(mtable->field(conn.srcField));
new KexiDB::Reference(forign, mtable->primaryKey());
*/
#if 0
if(!interactive)
{
kdDebug() << "KexiRelationView::addConnection: adding self" << endl;
RelationList l = m_relation->projectRelations();
l.append(conn);
m_relation->updateRelationList(this, l);
}
#endif
}
void
KexiRelationView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
{
KexiRelationViewConnection *cview;
// p->translate(0, (double)contentsY());
QRect clipping(cx, cy, cw, ch);
for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
{
if(clipping.intersects(cview->oldRect() | cview->connectionRect()))
cview->drawConnection(p);
}
}
void
KexiRelationView::slotTableScrolling(const QString& table)
{
KexiRelationViewTableContainer *c = m_tables[table];
if(c)
containerMoved(c);
}
void
KexiRelationView::containerMoved(KexiRelationViewTableContainer *c)
{
KexiRelationViewConnection *cview;
QRect r;
for (ConnectionListIterator it(m_connectionViews); ((cview=it.current())); ++it) {
//! @todo optimize
if(cview->masterTable() == c || cview->detailsTable() == c
|| cview->connectionRect().intersects(r))
{
r |= cview->oldRect();
kdDebug() << r << endl;
r |= cview->connectionRect();
kdDebug() << r << endl;
}
// updateContents(cview->oldRect());
// updateContents(cview->connectionRect());
// }
}
//! @todo optimize!
//didn't work well: updateContents(r);
updateContents();
// QRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5);
// updateContents(w);
QPoint p = viewportToContents(QPoint(c->x(), c->y()));
recalculateSize(p.x() + c->width(), p.y() + c->height());
emit tablePositionChanged(c);
}
void
KexiRelationView::setReadOnly(bool b)
{
m_readOnly=b;
//TODO
// invalidateActions();
/* TableList::Iterator it, end( m_tables.end() );
for ( it=m_tables.begin(); it != end; ++it)
{
// (*it)->setReadOnly(b);
#ifndef Q_WS_WIN
#warning readonly needed
#endif
}*/
}
void
KexiRelationView::slotListUpdate(QObject *)
{
#if 0
if(s != this)
{
m_connectionViews.clear();
RelationList rl = m_relation->projectRelations();
if(!rl.isEmpty())
{
RelationList::ConstIterator it, end( rl.constEnd() );
for( it = rl.begin(); it != end; ++it)
{
addConnection((*it), true);
}
}
}
updateContents();
#endif
}
void
KexiRelationView::contentsMousePressEvent(QMouseEvent *ev)
{
KexiRelationViewConnection *cview;
for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
{
if(!cview->matchesPoint(ev->pos(), 3))
continue;
clearSelection();
setFocus();
cview->setSelected(true);
updateContents(cview->connectionRect());
m_selectedConnection = cview;
emit connectionViewGotFocus();
// invalidateActions();
if(ev->button() == RightButton) {//show popup
kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl;
// QPopupMenu m;
// m_removeSelectedTableQueryAction->plug( &m );
// m_removeSelectedConnectionAction->plug( &m );
emit connectionContextMenuRequest( ev->globalPos() );
// executePopup( ev->globalPos() );
}
return;
}
//connection not found
clearSelection();
// invalidateActions();
if(ev->button() == RightButton) {//show popup on view background area
// QPopupMenu m;
// m_removeSelectedConnectionAction->plug( &m );
emit emptyAreaContextMenuRequest( ev->globalPos() );
// executePopup(ev->globalPos());
}
else {
emit emptyAreaGotFocus();
}
setFocus();
// QScrollView::contentsMousePressEvent(ev);
}
void KexiRelationView::clearSelection()
{
if (m_focusedTableView) {
m_focusedTableView->unsetFocus();
m_focusedTableView = 0;
// setFocus();
// invalidateActions();
}
if (m_selectedConnection) {
m_selectedConnection->setSelected(false);
updateContents(m_selectedConnection->connectionRect());
m_selectedConnection = 0;
// invalidateActions();
}
}
void
KexiRelationView::keyPressEvent(QKeyEvent *ev)
{
kdDebug() << "KexiRelationView::keyPressEvent()" << endl;
if (ev->key()==KGlobalSettings::contextMenuKey()) {
if (m_selectedConnection) {
emit connectionContextMenuRequest(
mapToGlobal(m_selectedConnection->connectionRect().center()) );
}
// m_popup->exec( mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() ) );
// executePopup();
}
else {
if(ev->key() == Key_Delete)
removeSelectedObject();
}
}
void
KexiRelationView::recalculateSize(int width, int height)
{
kdDebug() << "recalculateSize(" << width << ", " << height << ")" << endl;
int newW = contentsWidth(), newH = contentsHeight();
kdDebug() << "contentsSize(" << newW << ", " << newH << ")" << endl;
if(newW < width)
newW = width;
if(newH < height)
newH = height;
resizeContents(newW, newH);
}
/*! Resizes contents to size exactly enough to fit tableViews.
Executed on every tableView's drop event.
*/
void
KexiRelationView::stretchExpandSize()
{
int max_x=-1, max_y=-1;
QDictIterator<KexiRelationViewTableContainer> it(m_tables);
for (;it.current(); ++it) {
if (it.current()->right()>max_x)
max_x = it.current()->right();
if (it.current()->bottom()>max_y)
max_y = it.current()->bottom();
}
QPoint p = viewportToContents(QPoint(max_x, max_y) + QPoint(3,3)); //3 pixels margin
resizeContents(p.x(), p.y());
}
void KexiRelationView::slotTableViewEndDrag()
{
kdDebug() << "END DRAG!" <<endl;
stretchExpandSize();
}
void
KexiRelationView::removeSelectedObject()
{
if (m_selectedConnection) {
removeConnection(m_selectedConnection);
#if 0
RelationList l = m_relation->projectRelations();
RelationList nl;
for(RelationList::Iterator it = l.begin(); it != l.end(); ++it)
{
if((*it).srcTable == m_selectedConnection->connection().srcTable
&& (*it).rcvTable == m_selectedConnection->connection().rcvTable
&& (*it).srcField == m_selectedConnection->connection().srcField
&& (*it).rcvField == m_selectedConnection->connection().rcvField)
{
kdDebug() << "KexiRelationView::removeSelectedConnection(): matching found!" << endl;
// l.remove(it);
}
else
{
nl.append(*it);
}
}
kdDebug() << "KexiRelationView::removeSelectedConnection(): d2" << endl;
m_relation->updateRelationList(this, nl);
kdDebug() << "KexiRelationView::removeSelectedConnection(): d3" << endl;
#endif
delete m_selectedConnection;
m_selectedConnection = 0;
// invalidateActions();
}
else if (m_focusedTableView) {
KexiRelationViewTableContainer *tmp = m_focusedTableView;
m_focusedTableView = 0;
hideTable(tmp);
}
}
void
KexiRelationView::hideTable(KexiRelationViewTableContainer* tableView)
{
/*! @todo what about query? */
KexiDB::TableSchema *ts = tableView->schema()->table();
//for all connections: find and remove all connected with this table
QPtrListIterator<KexiRelationViewConnection> it(m_connectionViews);
for (;it.current();) {
if (it.current()->masterTable() == tableView
|| it.current()->detailsTable() == tableView)
{
//remove this
removeConnection(it.current());
}
else {
++it;
}
}
m_tables.take(tableView->schema()->name());
delete tableView;
emit tableHidden( *ts );
}
void
KexiRelationView::hideAllTablesExcept( KexiDB::TableSchema::List* tables )
{
//! @todo what about queries?
for (TablesDictIterator it(m_tables); it.current();) {
KexiDB::TableSchema *table = it.current()->schema()->table();
if (!table || tables->findRef( table )!=-1) {
++it;
continue;
}
hideTable(it.current());
}
}
void
KexiRelationView::removeConnection(KexiRelationViewConnection *conn)
{
emit aboutConnectionRemove(conn);
m_connectionViews.remove(conn);
updateContents(conn->connectionRect());
kdDebug() << "KexiRelationView::removeConnection()" << endl;
}
void KexiRelationView::slotTableViewGotFocus()
{
if (m_focusedTableView == sender())
return;
kdDebug() << "GOT FOCUS!" <<endl;
clearSelection();
// if (m_focusedTableView)
// m_focusedTableView->unsetFocus();
m_focusedTableView = (KexiRelationViewTableContainer*)sender();
// invalidateActions();
emit tableViewGotFocus();
}
QSize KexiRelationView::sizeHint() const
{
return QSize(QScrollView::sizeHint());//.width(), 600);
}
void KexiRelationView::clear()
{
removeAllConnections();
m_tables.setAutoDelete(true);
m_tables.clear();
m_tables.setAutoDelete(false);
updateContents();
}
void KexiRelationView::removeAllConnections()
{
clearSelection(); //sanity
m_connectionViews.setAutoDelete(true);
m_connectionViews.clear();
m_connectionViews.setAutoDelete(false);
updateContents();
}
/*
void KexiRelationView::tableHeaderContextMenuRequest(const QPoint& pos)
{
if (m_focusedTableView != sender())
return;
kdDebug() << "HEADER CTXT MENU!" <<endl;
invalidateActions();
m_tableQueryPopup->exec(pos);
}
//! Invalidates all actions availability
void KexiRelationView::invalidateActions()
{
setAvailable("edit_delete", m_selectedConnection || m_focusedTableView);
}
void KexiRelationView::executePopup( QPoint pos )
{
if (pos==QPoint(-1,-1)) {
pos = mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() );
}
if (m_focusedTableView)
m_tableQueryPopup->exec(pos);
else if (m_selectedConnection)
m_connectionPopup->exec(pos);
}
*/
#include "kexirelationview.moc"