kexi

kexirelationview.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002   Lucijan Busch <lucijan@gmx.at>
00003    Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl>
00004 
00005    This program is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this program; see the file COPYING.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <kdebug.h>
00022 
00023 #include <qstringlist.h>
00024 #include <qlayout.h>
00025 #include <qlabel.h>
00026 #include <qheader.h>
00027 #include <qevent.h>
00028 #include <qpainter.h>
00029 #include <qstyle.h>
00030 #include <qlineedit.h>
00031 #include <qpopupmenu.h>
00032 
00033 #include <klocale.h>
00034 #include <kaction.h>
00035 #include <kpopupmenu.h>
00036 #include <kglobalsettings.h>
00037 #include <kmessagebox.h>
00038 
00039 #include <kexidb/tableschema.h>
00040 #include <kexidb/indexschema.h>
00041 #include <kexidb/utils.h>
00042 
00043 #include "kexirelationview.h"
00044 #include "kexirelationviewtable.h"
00045 #include "kexirelationviewconnection.h"
00046 #include <kexi.h>
00047 
00048 KexiRelationView::KexiRelationView(QWidget *parent, const char *name)
00049  : QScrollView(parent, name, WStaticContents)
00050 {
00051 //  m_relation=relation;
00052 //  m_relation->incUsageCount();
00053     m_selectedConnection = 0;
00054     m_readOnly=false;
00055     m_focusedTableView = 0;
00056     setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
00057 
00058 //  connect(relation, SIGNAL(relationListUpdated(QObject *)), this, SLOT(slotListUpdate(QObject *)));
00059 
00060     viewport()->setPaletteBackgroundColor(colorGroup().mid());
00061     setFocusPolicy(WheelFocus);
00062     setResizePolicy(Manual);
00063 /*MOVED TO KexiRelationDialog
00064     //actions
00065     m_tableQueryPopup = new KPopupMenu(this, "m_popup");
00066     m_tableQueryPopup->insertTitle(i18n("Table"));
00067     m_connectionPopup = new KPopupMenu(this, "m_connectionPopup");
00068     m_connectionPopup->insertTitle(i18n("Relation"));
00069     m_areaPopup = new KPopupMenu(this, "m_areaPopup");
00070     
00071     plugSharedAction("edit_delete", i18n("Hide Table"), m_tableQueryPopup);
00072     plugSharedAction("edit_delete",m_connectionPopup);
00073     plugSharedAction("edit_delete",this, SLOT(removeSelectedObject()));
00074 */  
00075 #if 0
00076     m_removeSelectedTableQueryAction = new KAction(i18n("&Hide Selected Table/Query"), "editdelete", "",
00077         this, SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery");
00078     m_removeSelectedConnectionAction = new KAction(i18n("&Remove Selected Relationship"), "button_cancel", "",
00079         this, SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection");
00080     m_openSelectedTableQueryAction = new KAction(i18n("&Open Selected Table/Query"), "", "",
00081         this, SLOT(openSelectedTableQuery()), 0/*parent->actionCollection()*/, "relationsview_openSelectedTableQuery");
00082 #endif
00083 
00084 //  invalidateActions();
00085 
00086 #if 0
00087 
00088 
00089     m_popup = new KPopupMenu(this, "m_popup");
00090     m_openSelectedTableQueryAction->plug( m_popup );
00091     m_removeSelectedTableQueryAction->plug( m_popup );
00092     m_removeSelectedConnectionAction->plug( m_popup );
00093 
00094     invalidateActions();
00095 #endif
00096 
00097     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, true);
00098 }
00099 
00100 KexiRelationView::~KexiRelationView()
00101 {
00102 }
00103 
00104 /*KexiRelationViewTableContainer*
00105 KexiRelationView::containerForTable(KexiDB::TableSchema* tableSchema)
00106 {
00107     if (!tableSchema)
00108         return 0;
00109     for (TablesDictIterator it(m_tables); it.current(); ++it) {
00110         if (it.current()->schema()->table()==tableSchema)
00111             return it.current();
00112     }
00113     return 0;
00114 }*/
00115 
00116 KexiRelationViewTableContainer *
00117 KexiRelationView::tableContainer(KexiDB::TableSchema *t) const
00118 {
00119     return t ? m_tables.find(t->name()) : 0;
00120 }
00121 
00122 KexiRelationViewTableContainer*
00123 KexiRelationView::addTableContainer(KexiDB::TableSchema *t, const QRect &rect)
00124 {
00125     if(!t)
00126         return 0;
00127 
00128     kdDebug() << "KexiRelationView::addTable(): " << t->name() << ", " << viewport() << endl;
00129 
00130     KexiRelationViewTableContainer* c = tableContainer(t);
00131     if (c) {
00132         kdWarning() << "KexiRelationView::addTable(): table already added" << endl;
00133         return c;
00134     }
00135 
00136     c = new KexiRelationViewTableContainer(this, 
00138         new KexiDB::TableOrQuerySchema(t)
00139     );
00140     connect(c, SIGNAL(endDrag()), this, SLOT(slotTableViewEndDrag()));
00141     connect(c, SIGNAL(gotFocus()), this, SLOT(slotTableViewGotFocus()));
00142 //  connect(c, SIGNAL(headerContextMenuRequest(const QPoint&)), 
00143 //      this, SLOT(tableHeaderContextMenuRequest(const QPoint&)));
00144     connect(c, SIGNAL(contextMenuRequest(const QPoint&)), 
00145         this, SIGNAL(tableContextMenuRequest(const QPoint&)));
00146     
00147     addChild(c, 100,100);
00148     if (rect.isValid()) {//predefined size
00149         QSize finalSize = c->size().expandedTo( c->sizeHint() );
00150         QRect r = rect;
00151         r.setSize( finalSize + QSize(0,10) );
00152         moveChild( c, rect.left(), rect.top() );
00153         //we're doing this instead of setGeometry(rect)
00154         //because the geomenty might be saved on other system with bigger fonts :)
00155         c->resize(c->sizeHint());
00156 //      c->setGeometry(r);
00157 //TODO
00158 
00159 //      moveChild( c, rect.left(), rect.top() ); // setGeometry(rect);
00160 //      c->resize( finalSize );
00161 //      c->updateGeometry();
00162     }
00163     c->show();
00164     updateGeometry();
00165     if (!rect.isValid()) {
00166         c->updateGeometry();
00167         c->resize(c->sizeHint());
00168     }
00169     int x, y;
00170 
00171     if(m_tables.count() > 0)
00172     {
00173         int place = -10;
00174         QDictIterator<KexiRelationViewTableContainer> it(m_tables);
00175         for(; it.current(); ++it)
00176         {
00177             int right = (*it)->x() + (*it)->width();
00178             if(right > place)
00179                 place = right;
00180         }
00181 
00182         x = place + 30;
00183     }
00184     else
00185     {
00186         x = 5;
00187     }
00188 
00189     y = 5;
00190     QPoint p = viewportToContents(QPoint(x, y));
00191     recalculateSize(p.x() + c->width(), p.y() + c->height());
00192     if (!rect.isValid()) {
00193         moveChild(c, x, y);
00194     }
00195 
00196     m_tables.insert(t->name(), c);
00197 
00198     connect(c, SIGNAL(moved(KexiRelationViewTableContainer *)), this,
00199             SLOT(containerMoved(KexiRelationViewTableContainer *)));
00200     
00201     if (hasFocus()) //ok?
00202         c->setFocus();
00203 
00204     return c;
00205 }
00206 
00207 void
00208 KexiRelationView::addConnection(const SourceConnection& _conn)
00209 {
00210     SourceConnection conn = _conn;
00211     kdDebug() << "KexiRelationView::addConnection()" << endl;
00212 
00213     KexiRelationViewTableContainer *master = m_tables[conn.masterTable];
00214     KexiRelationViewTableContainer *details = m_tables[conn.detailsTable];
00215     if (!master || !details)
00216         return;
00217 
00219     KexiDB::TableSchema *masterTable = master->schema()->table();
00221     KexiDB::TableSchema *detailsTable = details->schema()->table();
00222     if (!masterTable || !detailsTable)
00223         return;
00224 
00225     // ok, but we need to know where is the 'master' and where is the 'details' side:
00226     KexiDB::Field *masterFld = masterTable->field(conn.masterField);
00227     KexiDB::Field *detailsFld = detailsTable->field(conn.detailsField);
00228     if (!masterFld || !detailsFld)
00229         return;
00230     
00231     if (!masterFld->isUniqueKey()) {
00232         if (detailsFld->isUniqueKey()) {
00233             //SWAP:
00234             KexiDB::Field *tmpFld = masterFld;
00235             masterFld = detailsFld;
00236             detailsFld = tmpFld;
00237             KexiDB::TableSchema *tmpTable = masterTable;
00238             masterTable = detailsTable;
00239             detailsTable = tmpTable;
00240             KexiRelationViewTableContainer *tmp = master;
00241             master = details;
00242             details = tmp;
00243             QString tmp_masterTable = conn.masterTable;
00244             conn.masterTable = conn.detailsTable;
00245             conn.detailsTable = tmp_masterTable;
00246             QString tmp_masterField = conn.masterField;
00247             conn.masterField = conn.detailsField;
00248             conn.detailsField = tmp_masterField;
00249         }
00250     }
00251 
00252 //  kdDebug() << "KexiRelationView::addConnection(): finalSRC = " << m_tables[conn.srcTable] << endl;
00253 
00254     KexiRelationViewConnection *connView = new KexiRelationViewConnection(master, details, conn, this);
00255     m_connectionViews.append(connView);
00256     updateContents(connView->connectionRect());
00257 
00258 /*js: will be moved up to relation/query part as this is only visual class
00259     KexiDB::TableSchema *mtable = m_conn->tableSchema(conn.srcTable);
00260     KexiDB::TableSchema *ftable = m_conn->tableSchema(conn.rcvTable);
00261     KexiDB::IndexSchema *forign = new KexiDB::IndexSchema(ftable);
00262 
00263     forign->addField(mtable->field(conn.srcField));
00264     new KexiDB::Reference(forign, mtable->primaryKey());
00265 */
00266 #if 0
00267     if(!interactive)
00268     {
00269         kdDebug() << "KexiRelationView::addConnection: adding self" << endl;
00270         RelationList l = m_relation->projectRelations();
00271         l.append(conn);
00272         m_relation->updateRelationList(this, l);
00273     }
00274 #endif
00275 }
00276 
00277 void
00278 KexiRelationView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
00279 {
00280     KexiRelationViewConnection *cview;
00281 //  p->translate(0, (double)contentsY());
00282 
00283     QRect clipping(cx, cy, cw, ch);
00284     for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
00285     {
00286         if(clipping.intersects(cview->oldRect() | cview->connectionRect()))
00287             cview->drawConnection(p);
00288     }
00289 }
00290 
00291 void
00292 KexiRelationView::slotTableScrolling(const QString& table)
00293 {
00294     KexiRelationViewTableContainer *c = m_tables[table];
00295 
00296     if(c)
00297         containerMoved(c);
00298 }
00299 
00300 void
00301 KexiRelationView::containerMoved(KexiRelationViewTableContainer *c)
00302 {
00303     KexiRelationViewConnection *cview;
00304     QRect r;
00305     for (ConnectionListIterator it(m_connectionViews); ((cview=it.current())); ++it) {
00307         if(cview->masterTable() == c || cview->detailsTable() == c
00308             || cview->connectionRect().intersects(r)) 
00309         {
00310             r |= cview->oldRect();
00311             kdDebug() << r << endl;
00312             r |= cview->connectionRect();
00313             kdDebug() << r << endl;
00314         }
00315 //          updateContents(cview->oldRect());
00316 //          updateContents(cview->connectionRect());
00317 //      }
00318     }
00320 //didn't work well: updateContents(r);
00321     updateContents();
00322 
00323 //  QRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5);
00324 //  updateContents(w);
00325 
00326     QPoint p = viewportToContents(QPoint(c->x(), c->y()));
00327     recalculateSize(p.x() + c->width(), p.y() + c->height());
00328 
00329     emit tablePositionChanged(c);
00330 }
00331 
00332 void
00333 KexiRelationView::setReadOnly(bool b)
00334 {
00335     m_readOnly=b;
00336 //TODO
00337 //  invalidateActions();
00338 /*  TableList::Iterator it, end( m_tables.end() );  
00339     for ( it=m_tables.begin(); it != end; ++it)
00340     {
00341 //      (*it)->setReadOnly(b);
00342 #ifndef Q_WS_WIN
00343         #warning readonly needed
00344 #endif
00345     }*/
00346 }
00347 
00348 void
00349 KexiRelationView::slotListUpdate(QObject *)
00350 {
00351 #if 0
00352     if(s != this)
00353     {
00354         m_connectionViews.clear();
00355         RelationList rl = m_relation->projectRelations();
00356         if(!rl.isEmpty())
00357         {
00358             RelationList::ConstIterator it, end( rl.constEnd() );
00359             for( it = rl.begin(); it != end; ++it)
00360             {
00361                 addConnection((*it), true);
00362             }
00363         }
00364     }
00365 
00366     updateContents();
00367 #endif
00368 }
00369 
00370 void
00371 KexiRelationView::contentsMousePressEvent(QMouseEvent *ev)
00372 {
00373     KexiRelationViewConnection *cview;
00374     for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
00375     {
00376         if(!cview->matchesPoint(ev->pos(), 3))
00377             continue;
00378         clearSelection();
00379         setFocus();
00380         cview->setSelected(true);
00381         updateContents(cview->connectionRect());
00382         m_selectedConnection = cview;
00383         emit connectionViewGotFocus();
00384 //      invalidateActions();
00385 
00386         if(ev->button() == RightButton) {//show popup
00387             kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl;
00388 //          QPopupMenu m;
00389 //              m_removeSelectedTableQueryAction->plug( &m );
00390 //              m_removeSelectedConnectionAction->plug( &m );
00391             emit connectionContextMenuRequest( ev->globalPos() );
00392 //          executePopup( ev->globalPos() );
00393         }
00394         return;
00395     }
00396     //connection not found
00397     clearSelection();
00398 //  invalidateActions();
00399     if(ev->button() == RightButton) {//show popup on view background area
00400 //      QPopupMenu m;
00401 //          m_removeSelectedConnectionAction->plug( &m );
00402         emit emptyAreaContextMenuRequest( ev->globalPos() );
00403 //      executePopup(ev->globalPos());
00404     }
00405     else {
00406         emit emptyAreaGotFocus();
00407     }
00408     setFocus();
00409 //  QScrollView::contentsMousePressEvent(ev);
00410 }
00411 
00412 void KexiRelationView::clearSelection()
00413 {
00414     if (m_focusedTableView) {
00415         m_focusedTableView->unsetFocus();
00416         m_focusedTableView = 0;
00417 //      setFocus();
00418 //      invalidateActions();
00419     }
00420     if (m_selectedConnection) {
00421         m_selectedConnection->setSelected(false);
00422         updateContents(m_selectedConnection->connectionRect());
00423         m_selectedConnection = 0;
00424 //      invalidateActions();
00425     }
00426 }
00427 
00428 void
00429 KexiRelationView::keyPressEvent(QKeyEvent *ev)
00430 {
00431     kdDebug() << "KexiRelationView::keyPressEvent()" << endl;
00432 
00433     if (ev->key()==KGlobalSettings::contextMenuKey()) {
00434         if (m_selectedConnection) {
00435             emit connectionContextMenuRequest(
00436                 mapToGlobal(m_selectedConnection->connectionRect().center()) );
00437         }
00438 //      m_popup->exec( mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() ) );
00439 //      executePopup();
00440     }
00441     else {
00442         if(ev->key() == Key_Delete)
00443             removeSelectedObject();
00444     }
00445 }
00446 
00447 void
00448 KexiRelationView::recalculateSize(int width, int height)
00449 {
00450     kdDebug() << "recalculateSize(" << width << ", " << height << ")" << endl;
00451     int newW = contentsWidth(), newH = contentsHeight();
00452     kdDebug() << "contentsSize(" << newW << ", " << newH << ")" << endl;
00453 
00454     if(newW < width)
00455         newW = width;
00456 
00457     if(newH < height)
00458         newH = height;
00459 
00460     resizeContents(newW, newH);
00461 }
00462 
00466 void
00467 KexiRelationView::stretchExpandSize()
00468 {
00469     int max_x=-1, max_y=-1;
00470     QDictIterator<KexiRelationViewTableContainer> it(m_tables);
00471     for (;it.current(); ++it) {
00472         if (it.current()->right()>max_x)
00473             max_x = it.current()->right();
00474         if (it.current()->bottom()>max_y)
00475             max_y = it.current()->bottom();
00476     }
00477     QPoint p = viewportToContents(QPoint(max_x, max_y) + QPoint(3,3)); //3 pixels margin
00478     resizeContents(p.x(), p.y());
00479 }
00480 
00481 void KexiRelationView::slotTableViewEndDrag()
00482 {
00483     kdDebug() << "END DRAG!" <<endl;
00484     stretchExpandSize();
00485 
00486 }
00487 
00488 void
00489 KexiRelationView::removeSelectedObject()
00490 {
00491     if (m_selectedConnection) {
00492         removeConnection(m_selectedConnection);
00493 
00494 #if 0
00495     RelationList l = m_relation->projectRelations();
00496     RelationList nl;
00497     for(RelationList::Iterator it = l.begin(); it != l.end(); ++it)
00498     {
00499         if((*it).srcTable == m_selectedConnection->connection().srcTable
00500             && (*it).rcvTable == m_selectedConnection->connection().rcvTable
00501             && (*it).srcField == m_selectedConnection->connection().srcField
00502             && (*it).rcvField == m_selectedConnection->connection().rcvField)
00503         {
00504             kdDebug() << "KexiRelationView::removeSelectedConnection(): matching found!" << endl;
00505 //          l.remove(it);
00506         }
00507         else
00508         {
00509             nl.append(*it);
00510         }
00511     }
00512 
00513     kdDebug() << "KexiRelationView::removeSelectedConnection(): d2" << endl;
00514     m_relation->updateRelationList(this, nl);
00515     kdDebug() << "KexiRelationView::removeSelectedConnection(): d3" << endl;
00516 #endif
00517         delete m_selectedConnection;
00518         m_selectedConnection = 0;
00519 //      invalidateActions();
00520     }
00521     else if (m_focusedTableView) {
00522         KexiRelationViewTableContainer *tmp = m_focusedTableView;
00523         m_focusedTableView = 0;
00524         hideTable(tmp);
00525     }
00526 }
00527 
00528 void
00529 KexiRelationView::hideTable(KexiRelationViewTableContainer* tableView)
00530 {
00532     KexiDB::TableSchema *ts = tableView->schema()->table();
00533     //for all connections: find and remove all connected with this table
00534     QPtrListIterator<KexiRelationViewConnection> it(m_connectionViews);
00535     for (;it.current();) {
00536         if (it.current()->masterTable() == tableView 
00537             || it.current()->detailsTable() == tableView)
00538         {
00539             //remove this
00540             removeConnection(it.current());
00541         }
00542         else {
00543             ++it;
00544         }
00545     }
00546     m_tables.take(tableView->schema()->name());
00547     delete tableView;
00548     emit tableHidden( *ts );
00549 }
00550 
00551 void
00552 KexiRelationView::hideAllTablesExcept( KexiDB::TableSchema::List* tables )
00553 {
00555     for (TablesDictIterator it(m_tables); it.current();) {
00556         KexiDB::TableSchema *table = it.current()->schema()->table();
00557         if (!table || tables->findRef( table )!=-1) {
00558             ++it;
00559             continue;
00560         }
00561         hideTable(it.current());
00562     }
00563 }
00564 
00565 void
00566 KexiRelationView::removeConnection(KexiRelationViewConnection *conn)
00567 {
00568     emit aboutConnectionRemove(conn);
00569     m_connectionViews.remove(conn);
00570     updateContents(conn->connectionRect());
00571     kdDebug() << "KexiRelationView::removeConnection()" << endl;
00572 }
00573 
00574 void KexiRelationView::slotTableViewGotFocus()
00575 {
00576     if (m_focusedTableView == sender())
00577         return;
00578     kdDebug() << "GOT FOCUS!" <<endl;
00579     clearSelection();
00580 //  if (m_focusedTableView)
00581 //      m_focusedTableView->unsetFocus();
00582     m_focusedTableView = (KexiRelationViewTableContainer*)sender();
00583 //  invalidateActions();
00584     emit tableViewGotFocus();
00585 }
00586 
00587 QSize KexiRelationView::sizeHint() const
00588 {
00589     return QSize(QScrollView::sizeHint());//.width(), 600);
00590 }
00591 
00592 void KexiRelationView::clear()
00593 {
00594     removeAllConnections();
00595     m_tables.setAutoDelete(true);
00596     m_tables.clear();
00597     m_tables.setAutoDelete(false);
00598     updateContents();
00599 }
00600 
00601 void KexiRelationView::removeAllConnections()
00602 {
00603     clearSelection(); //sanity
00604     m_connectionViews.setAutoDelete(true);
00605     m_connectionViews.clear();
00606     m_connectionViews.setAutoDelete(false);
00607     updateContents();
00608 }
00609 
00610 /*
00611 
00612 void KexiRelationView::tableHeaderContextMenuRequest(const QPoint& pos)
00613 {
00614     if (m_focusedTableView != sender())
00615         return;
00616     kdDebug() << "HEADER CTXT MENU!" <<endl;
00617     invalidateActions();
00618     m_tableQueryPopup->exec(pos);   
00619 }
00620 
00622 void KexiRelationView::invalidateActions()
00623 {
00624     setAvailable("edit_delete", m_selectedConnection || m_focusedTableView);
00625 }
00626 
00627 void KexiRelationView::executePopup( QPoint pos )
00628 {
00629     if (pos==QPoint(-1,-1)) {
00630         pos = mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() );
00631     }
00632     if (m_focusedTableView)
00633         m_tableQueryPopup->exec(pos);
00634     else if (m_selectedConnection)
00635         m_connectionPopup->exec(pos);
00636 }
00637 */
00638 
00639 #include "kexirelationview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys