/* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch Copyright (C) 2003 Jaroslaw Staniek 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kexirelationview.h" #include "kexirelationviewtable.h" #include "kexirelationviewconnection.h" #include KexiRelationView::KexiRelationView(TQWidget *parent, const char *name) : TQScrollView(parent, name, WStaticContents) { // m_relation=relation; // m_relation->incUsageCount(); m_selectedConnection = 0; m_readOnly=false; m_focusedTableView = 0; setFrameStyle(TQFrame::WinPanel | TQFrame::Sunken); // connect(relation, TQT_SIGNAL(relationListUpdated(TQObject *)), this, TQT_SLOT(slotListUpdate(TQObject *))); viewport()->setPaletteBackgroundColor(tqcolorGroup().mid()); setFocusPolicy(TQ_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, TQT_SLOT(removeSelectedObject())); */ #if 0 m_removeSelectedTableQueryAction = new KAction(i18n("&Hide Selected Table/Query"), "editdelete", "", this, TQT_SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery"); m_removeSelectedConnectionAction = new KAction(i18n("&Remove Selected Relationship"), "button_cancel", "", this, TQT_SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection"); m_openSelectedTableQueryAction = new KAction(i18n("&Open Selected Table/Query"), "", "", this, TQT_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 tqsetSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::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 TQRect &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, TQT_SIGNAL(endDrag()), this, TQT_SLOT(slotTableViewEndDrag())); connect(c, TQT_SIGNAL(gotFocus()), this, TQT_SLOT(slotTableViewGotFocus())); // connect(c, TQT_SIGNAL(headerContextMenuRequest(const TQPoint&)), // this, TQT_SLOT(tableHeaderContextMenuRequest(const TQPoint&))); connect(c, TQT_SIGNAL(contextMenuRequest(const TQPoint&)), this, TQT_SIGNAL(tableContextMenuRequest(const TQPoint&))); addChild(c, 100,100); if (rect.isValid()) {//predefined size TQSize finalSize = c->size().expandedTo( c->tqsizeHint() ); TQRect r = rect; r.setSize( finalSize + TQSize(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->tqsizeHint()); // 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->tqsizeHint()); } int x, y; if(m_tables.count() > 0) { int place = -10; TQDictIterator 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; TQPoint p = viewportToContents(TQPoint(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, TQT_SIGNAL(moved(KexiRelationViewTableContainer *)), this, TQT_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; TQString tmp_masterTable = conn.masterTable; conn.masterTable = conn.detailsTable; conn.detailsTable = tmp_masterTable; TQString 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(TQPainter *p, int cx, int cy, int cw, int ch) { KexiRelationViewConnection *cview; // p->translate(0, (double)contentsY()); TQRect 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 TQString& table) { KexiRelationViewTableContainer *c = m_tables[table]; if(c) containerMoved(c); } void KexiRelationView::containerMoved(KexiRelationViewTableContainer *c) { KexiRelationViewConnection *cview; TQRect 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(); // TQRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5); // updateContents(w); TQPoint p = viewportToContents(TQPoint(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 TQ_WS_WIN #warning readonly needed #endif }*/ } void KexiRelationView::slotListUpdate(TQObject *) { #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(TQMouseEvent *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() == Qt::RightButton) {//show popup kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl; // TQPopupMenu 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() == Qt::RightButton) {//show popup on view background area // TQPopupMenu m; // m_removeSelectedConnectionAction->plug( &m ); emit emptyAreaContextMenuRequest( ev->globalPos() ); // executePopup(ev->globalPos()); } else { emit emptyAreaGotFocus(); } setFocus(); // TQScrollView::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(TQKeyEvent *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; TQDictIterator 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(); } TQPoint p = viewportToContents(TQPoint(max_x, max_y) + TQPoint(3,3)); //3 pixels margin resizeContents(p.x(), p.y()); } void KexiRelationView::slotTableViewEndDrag() { kdDebug() << "END DRAG!" <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 TQPtrListIterator 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!" <unsetFocus(); m_focusedTableView = (KexiRelationViewTableContainer*)sender(); // invalidateActions(); emit tableViewGotFocus(); } TQSize KexiRelationView::tqsizeHint() const { return TQSize(TQScrollView::tqsizeHint());//.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 TQPoint& pos) { if (m_focusedTableView != sender()) return; kdDebug() << "HEADER CTXT MENU!" <exec(pos); } //! Invalidates all actions availability void KexiRelationView::invalidateActions() { setAvailable("edit_delete", m_selectedConnection || m_focusedTableView); } void KexiRelationView::executePopup( TQPoint pos ) { if (pos==TQPoint(-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"