kexirelationview.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
3 Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <kdebug.h>
22
23#include <tqstringlist.h>
24#include <tqlayout.h>
25#include <tqlabel.h>
26#include <tqheader.h>
27#include <tqevent.h>
28#include <tqpainter.h>
29#include <tqstyle.h>
30#include <tqlineedit.h>
31#include <tqpopupmenu.h>
32
33#include <tdelocale.h>
34#include <tdeaction.h>
35#include <tdepopupmenu.h>
36#include <tdeglobalsettings.h>
37#include <tdemessagebox.h>
38
39#include <kexidb/tableschema.h>
40#include <kexidb/indexschema.h>
41#include <kexidb/utils.h>
42
43#include "kexirelationview.h"
44#include "kexirelationviewtable.h"
45#include "kexirelationviewconnection.h"
46#include <kexi.h>
47
48KexiRelationView::KexiRelationView(TQWidget *parent, const char *name)
49 : TQScrollView(parent, name, WStaticContents)
50{
51// m_relation=relation;
52// m_relation->incUsageCount();
53 m_selectedConnection = 0;
54 m_readOnly=false;
55 m_focusedTableView = 0;
56 setFrameStyle(TQFrame::WinPanel | TQFrame::Sunken);
57
58// connect(relation, TQ_SIGNAL(relationListUpdated(TQObject *)), this, TQ_SLOT(slotListUpdate(TQObject *)));
59
60 viewport()->setPaletteBackgroundColor(colorGroup().mid());
61 setFocusPolicy(TQWidget::WheelFocus);
62 setResizePolicy(Manual);
63/*MOVED TO KexiRelationDialog
64 //actions
65 m_tableQueryPopup = new TDEPopupMenu(this, "m_popup");
66 m_tableQueryPopup->insertTitle(i18n("Table"));
67 m_connectionPopup = new TDEPopupMenu(this, "m_connectionPopup");
68 m_connectionPopup->insertTitle(i18n("Relation"));
69 m_areaPopup = new TDEPopupMenu(this, "m_areaPopup");
70
71 plugSharedAction("edit_delete", i18n("Hide Table"), m_tableQueryPopup);
72 plugSharedAction("edit_delete",m_connectionPopup);
73 plugSharedAction("edit_delete",this, TQ_SLOT(removeSelectedObject()));
74*/
75#if 0
76 m_removeSelectedTableQueryAction = new TDEAction(i18n("&Hide Selected Table/Query"), "edit-delete", "",
77 this, TQ_SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery");
78 m_removeSelectedConnectionAction = new TDEAction(i18n("&Remove Selected Relationship"), "button_cancel", "",
79 this, TQ_SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection");
80 m_openSelectedTableQueryAction = new TDEAction(i18n("&Open Selected Table/Query"), "", "",
81 this, TQ_SLOT(openSelectedTableQuery()), 0/*parent->actionCollection()*/, "relationsview_openSelectedTableQuery");
82#endif
83
84// invalidateActions();
85
86#if 0
87
88
89 m_popup = new TDEPopupMenu(this, "m_popup");
90 m_openSelectedTableQueryAction->plug( m_popup );
91 m_removeSelectedTableQueryAction->plug( m_popup );
92 m_removeSelectedConnectionAction->plug( m_popup );
93
94 invalidateActions();
95#endif
96
97 setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding, true);
98}
99
100KexiRelationView::~KexiRelationView()
101{
102}
103
104/*KexiRelationViewTableContainer*
105KexiRelationView::containerForTable(KexiDB::TableSchema* tableSchema)
106{
107 if (!tableSchema)
108 return 0;
109 for (TablesDictIterator it(m_tables); it.current(); ++it) {
110 if (it.current()->schema()->table()==tableSchema)
111 return it.current();
112 }
113 return 0;
114}*/
115
116KexiRelationViewTableContainer *
118{
119 return t ? m_tables.find(t->name()) : 0;
120}
121
122KexiRelationViewTableContainer*
124{
125 if(!t)
126 return 0;
127
128 kdDebug() << "KexiRelationView::addTable(): " << t->name() << ", " << viewport() << endl;
129
130 KexiRelationViewTableContainer* c = tableContainer(t);
131 if (c) {
132 kdWarning() << "KexiRelationView::addTable(): table already added" << endl;
133 return c;
134 }
135
136 c = new KexiRelationViewTableContainer(this,
139 );
140 connect(c, TQ_SIGNAL(endDrag()), this, TQ_SLOT(slotTableViewEndDrag()));
141 connect(c, TQ_SIGNAL(gotFocus()), this, TQ_SLOT(slotTableViewGotFocus()));
142// connect(c, TQ_SIGNAL(headerContextMenuRequest(const TQPoint&)),
143// this, TQ_SLOT(tableHeaderContextMenuRequest(const TQPoint&)));
144 connect(c, TQ_SIGNAL(contextMenuRequest(const TQPoint&)),
145 this, TQ_SIGNAL(tableContextMenuRequest(const TQPoint&)));
146
147 addChild(c, 100,100);
148 if (rect.isValid()) {//predefined size
149 TQSize finalSize = c->size().expandedTo( c->sizeHint() );
150 TQRect r = rect;
151 r.setSize( finalSize + TQSize(0,10) );
152 moveChild( c, rect.left(), rect.top() );
153 //we're doing this instead of setGeometry(rect)
154 //because the geomenty might be saved on other system with bigger fonts :)
155 c->resize(c->sizeHint());
156// c->setGeometry(r);
157//TODO
158
159// moveChild( c, rect.left(), rect.top() ); // setGeometry(rect);
160// c->resize( finalSize );
161// c->updateGeometry();
162 }
163 c->show();
164 updateGeometry();
165 if (!rect.isValid()) {
166 c->updateGeometry();
167 c->resize(c->sizeHint());
168 }
169 int x, y;
170
171 if(m_tables.count() > 0)
172 {
173 int place = -10;
174 TQDictIterator<KexiRelationViewTableContainer> it(m_tables);
175 for(; it.current(); ++it)
176 {
177 int right = (*it)->x() + (*it)->width();
178 if(right > place)
179 place = right;
180 }
181
182 x = place + 30;
183 }
184 else
185 {
186 x = 5;
187 }
188
189 y = 5;
190 TQPoint p = viewportToContents(TQPoint(x, y));
191 recalculateSize(p.x() + c->width(), p.y() + c->height());
192 if (!rect.isValid()) {
193 moveChild(c, x, y);
194 }
195
196 m_tables.insert(t->name(), c);
197
198 connect(c, TQ_SIGNAL(moved(KexiRelationViewTableContainer *)), this,
199 TQ_SLOT(containerMoved(KexiRelationViewTableContainer *)));
200
201 if (hasFocus()) //ok?
202 c->setFocus();
203
204 return c;
205}
206
207void
208KexiRelationView::addConnection(const SourceConnection& _conn)
209{
210 SourceConnection conn = _conn;
211 kdDebug() << "KexiRelationView::addConnection()" << endl;
212
213 KexiRelationViewTableContainer *master = m_tables[conn.masterTable];
214 KexiRelationViewTableContainer *details = m_tables[conn.detailsTable];
215 if (!master || !details)
216 return;
217
219 KexiDB::TableSchema *masterTable = master->schema()->table();
221 KexiDB::TableSchema *detailsTable = details->schema()->table();
222 if (!masterTable || !detailsTable)
223 return;
224
225 // ok, but we need to know where is the 'master' and where is the 'details' side:
226 KexiDB::Field *masterFld = masterTable->field(conn.masterField);
227 KexiDB::Field *detailsFld = detailsTable->field(conn.detailsField);
228 if (!masterFld || !detailsFld)
229 return;
230
231 if (!masterFld->isUniqueKey()) {
232 if (detailsFld->isUniqueKey()) {
233 //SWAP:
234 KexiDB::Field *tmpFld = masterFld;
235 masterFld = detailsFld;
236 detailsFld = tmpFld;
237 KexiDB::TableSchema *tmpTable = masterTable;
238 masterTable = detailsTable;
239 detailsTable = tmpTable;
240 KexiRelationViewTableContainer *tmp = master;
241 master = details;
242 details = tmp;
243 TQString tmp_masterTable = conn.masterTable;
244 conn.masterTable = conn.detailsTable;
245 conn.detailsTable = tmp_masterTable;
246 TQString tmp_masterField = conn.masterField;
247 conn.masterField = conn.detailsField;
248 conn.detailsField = tmp_masterField;
249 }
250 }
251
252// kdDebug() << "KexiRelationView::addConnection(): finalSRC = " << m_tables[conn.srcTable] << endl;
253
254 KexiRelationViewConnection *connView = new KexiRelationViewConnection(master, details, conn, this);
255 m_connectionViews.append(connView);
256 updateContents(connView->connectionRect());
257
258/*js: will be moved up to relation/query part as this is only visual class
259 KexiDB::TableSchema *mtable = m_conn->tableSchema(conn.srcTable);
260 KexiDB::TableSchema *ftable = m_conn->tableSchema(conn.rcvTable);
261 KexiDB::IndexSchema *forign = new KexiDB::IndexSchema(ftable);
262
263 forign->addField(mtable->field(conn.srcField));
264 new KexiDB::Reference(forign, mtable->primaryKey());
265*/
266#if 0
267 if(!interactive)
268 {
269 kdDebug() << "KexiRelationView::addConnection: adding self" << endl;
270 RelationList l = m_relation->projectRelations();
271 l.append(conn);
272 m_relation->updateRelationList(this, l);
273 }
274#endif
275}
276
277void
278KexiRelationView::drawContents(TQPainter *p, int cx, int cy, int cw, int ch)
279{
280 KexiRelationViewConnection *cview;
281// p->translate(0, (double)contentsY());
282
283 TQRect clipping(cx, cy, cw, ch);
284 for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
285 {
286 if(clipping.intersects(cview->oldRect() | cview->connectionRect()))
287 cview->drawConnection(p);
288 }
289}
290
291void
292KexiRelationView::slotTableScrolling(const TQString& table)
293{
294 KexiRelationViewTableContainer *c = m_tables[table];
295
296 if(c)
298}
299
300void
301KexiRelationView::containerMoved(KexiRelationViewTableContainer *c)
302{
303 KexiRelationViewConnection *cview;
304 TQRect r;
305 for (ConnectionListIterator it(m_connectionViews); ((cview=it.current())); ++it) {
307 if(cview->masterTable() == c || cview->detailsTable() == c
308 || cview->connectionRect().intersects(r))
309 {
310 r |= cview->oldRect();
311 kdDebug() << r << endl;
312 r |= cview->connectionRect();
313 kdDebug() << r << endl;
314 }
315// updateContents(cview->oldRect());
316// updateContents(cview->connectionRect());
317// }
318 }
320//didn't work well: updateContents(r);
321 updateContents();
322
323// TQRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5);
324// updateContents(w);
325
326 TQPoint p = viewportToContents(TQPoint(c->x(), c->y()));
327 recalculateSize(p.x() + c->width(), p.y() + c->height());
328
329 emit tablePositionChanged(c);
330}
331
332void
333KexiRelationView::setReadOnly(bool b)
334{
335 m_readOnly=b;
336//TODO
337// invalidateActions();
338/* TableList::Iterator it, end( m_tables.end() );
339 for ( it=m_tables.begin(); it != end; ++it)
340 {
341// (*it)->setReadOnly(b);
342#ifndef TQ_WS_WIN
343 #warning readonly needed
344#endif
345 }*/
346}
347
348void
349KexiRelationView::slotListUpdate(TQObject *)
350{
351#if 0
352 if(s != this)
353 {
354 m_connectionViews.clear();
355 RelationList rl = m_relation->projectRelations();
356 if(!rl.isEmpty())
357 {
358 RelationList::ConstIterator it, end( rl.constEnd() );
359 for( it = rl.begin(); it != end; ++it)
360 {
361 addConnection((*it), true);
362 }
363 }
364 }
365
366 updateContents();
367#endif
368}
369
370void
371KexiRelationView::contentsMousePressEvent(TQMouseEvent *ev)
372{
373 KexiRelationViewConnection *cview;
374 for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
375 {
376 if(!cview->matchesPoint(ev->pos(), 3))
377 continue;
379 setFocus();
380 cview->setSelected(true);
381 updateContents(cview->connectionRect());
382 m_selectedConnection = cview;
383 emit connectionViewGotFocus();
384// invalidateActions();
385
386 if(ev->button() == TQt::RightButton) {//show popup
387 kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl;
388// TQPopupMenu m;
389// m_removeSelectedTableQueryAction->plug( &m );
390// m_removeSelectedConnectionAction->plug( &m );
391 emit connectionContextMenuRequest( ev->globalPos() );
392// executePopup( ev->globalPos() );
393 }
394 return;
395 }
396 //connection not found
398// invalidateActions();
399 if(ev->button() == TQt::RightButton) {//show popup on view background area
400// TQPopupMenu m;
401// m_removeSelectedConnectionAction->plug( &m );
402 emit emptyAreaContextMenuRequest( ev->globalPos() );
403// executePopup(ev->globalPos());
404 }
405 else {
406 emit emptyAreaGotFocus();
407 }
408 setFocus();
409// TQScrollView::contentsMousePressEvent(ev);
410}
411
413{
414 if (m_focusedTableView) {
415 m_focusedTableView->unsetFocus();
416 m_focusedTableView = 0;
417// setFocus();
418// invalidateActions();
419 }
420 if (m_selectedConnection) {
421 m_selectedConnection->setSelected(false);
422 updateContents(m_selectedConnection->connectionRect());
423 m_selectedConnection = 0;
424// invalidateActions();
425 }
426}
427
428void
429KexiRelationView::keyPressEvent(TQKeyEvent *ev)
430{
431 kdDebug() << "KexiRelationView::keyPressEvent()" << endl;
432
433 if (ev->key()==TDEGlobalSettings::contextMenuKey()) {
434 if (m_selectedConnection) {
435 emit connectionContextMenuRequest(
436 mapToGlobal(m_selectedConnection->connectionRect().center()) );
437 }
438// m_popup->exec( mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() ) );
439// executePopup();
440 }
441 else {
442 if(ev->key() == Key_Delete)
444 }
445}
446
447void
448KexiRelationView::recalculateSize(int width, int height)
449{
450 kdDebug() << "recalculateSize(" << width << ", " << height << ")" << endl;
451 int newW = contentsWidth(), newH = contentsHeight();
452 kdDebug() << "contentsSize(" << newW << ", " << newH << ")" << endl;
453
454 if(newW < width)
455 newW = width;
456
457 if(newH < height)
458 newH = height;
459
460 resizeContents(newW, newH);
461}
462
466void
467KexiRelationView::stretchExpandSize()
468{
469 int max_x=-1, max_y=-1;
470 TQDictIterator<KexiRelationViewTableContainer> it(m_tables);
471 for (;it.current(); ++it) {
472 if (it.current()->right()>max_x)
473 max_x = it.current()->right();
474 if (it.current()->bottom()>max_y)
475 max_y = it.current()->bottom();
476 }
477 TQPoint p = viewportToContents(TQPoint(max_x, max_y) + TQPoint(3,3)); //3 pixels margin
478 resizeContents(p.x(), p.y());
479}
480
481void KexiRelationView::slotTableViewEndDrag()
482{
483 kdDebug() << "END DRAG!" <<endl;
484 stretchExpandSize();
485
486}
487
488void
490{
491 if (m_selectedConnection) {
492 removeConnection(m_selectedConnection);
493
494#if 0
495 RelationList l = m_relation->projectRelations();
496 RelationList nl;
497 for(RelationList::Iterator it = l.begin(); it != l.end(); ++it)
498 {
499 if((*it).srcTable == m_selectedConnection->connection().srcTable
500 && (*it).rcvTable == m_selectedConnection->connection().rcvTable
501 && (*it).srcField == m_selectedConnection->connection().srcField
502 && (*it).rcvField == m_selectedConnection->connection().rcvField)
503 {
504 kdDebug() << "KexiRelationView::removeSelectedConnection(): matching found!" << endl;
505// l.remove(it);
506 }
507 else
508 {
509 nl.append(*it);
510 }
511 }
512
513 kdDebug() << "KexiRelationView::removeSelectedConnection(): d2" << endl;
514 m_relation->updateRelationList(this, nl);
515 kdDebug() << "KexiRelationView::removeSelectedConnection(): d3" << endl;
516#endif
517 delete m_selectedConnection;
518 m_selectedConnection = 0;
519// invalidateActions();
520 }
521 else if (m_focusedTableView) {
522 KexiRelationViewTableContainer *tmp = m_focusedTableView;
523 m_focusedTableView = 0;
524 hideTable(tmp);
525 }
526}
527
528void
529KexiRelationView::hideTable(KexiRelationViewTableContainer* tableView)
530{
532 KexiDB::TableSchema *ts = tableView->schema()->table();
533 //for all connections: find and remove all connected with this table
534 TQPtrListIterator<KexiRelationViewConnection> it(m_connectionViews);
535 for (;it.current();) {
536 if (it.current()->masterTable() == tableView
537 || it.current()->detailsTable() == tableView)
538 {
539 //remove this
540 removeConnection(it.current());
541 }
542 else {
543 ++it;
544 }
545 }
546 m_tables.take(tableView->schema()->name());
547 delete tableView;
548 emit tableHidden( *ts );
549}
550
551void
553{
555 for (TablesDictIterator it(m_tables); it.current();) {
556 KexiDB::TableSchema *table = it.current()->schema()->table();
557 if (!table || tables->findRef( table )!=-1) {
558 ++it;
559 continue;
560 }
561 hideTable(it.current());
562 }
563}
564
565void
566KexiRelationView::removeConnection(KexiRelationViewConnection *conn)
567{
568 emit aboutConnectionRemove(conn);
569 m_connectionViews.remove(conn);
570 updateContents(conn->connectionRect());
571 kdDebug() << "KexiRelationView::removeConnection()" << endl;
572}
573
574void KexiRelationView::slotTableViewGotFocus()
575{
576 if (m_focusedTableView == sender())
577 return;
578 kdDebug() << "GOT FOCUS!" <<endl;
580// if (m_focusedTableView)
581// m_focusedTableView->unsetFocus();
582 m_focusedTableView = (KexiRelationViewTableContainer*)sender();
583// invalidateActions();
584 emit tableViewGotFocus();
585}
586
587TQSize KexiRelationView::sizeHint() const
588{
589 return TQSize(TQScrollView::sizeHint());//.width(), 600);
590}
591
593{
595 m_tables.setAutoDelete(true);
596 m_tables.clear();
597 m_tables.setAutoDelete(false);
598 updateContents();
599}
600
602{
603 clearSelection(); //sanity
604 m_connectionViews.setAutoDelete(true);
605 m_connectionViews.clear();
606 m_connectionViews.setAutoDelete(false);
607 updateContents();
608}
609
610/*
611
612void KexiRelationView::tableHeaderContextMenuRequest(const TQPoint& pos)
613{
614 if (m_focusedTableView != sender())
615 return;
616 kdDebug() << "HEADER CTXT MENU!" <<endl;
617 invalidateActions();
618 m_tableQueryPopup->exec(pos);
619}
620
622void KexiRelationView::invalidateActions()
623{
624 setAvailable("edit_delete", m_selectedConnection || m_focusedTableView);
625}
626
627void KexiRelationView::executePopup( TQPoint pos )
628{
629 if (pos==TQPoint(-1,-1)) {
630 pos = mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() );
631 }
632 if (m_focusedTableView)
633 m_tableQueryPopup->exec(pos);
634 else if (m_selectedConnection)
635 m_connectionPopup->exec(pos);
636}
637*/
638
639#include "kexirelationview.moc"
Field * field(uint id)
Definition fieldlist.h:76
Meta-data for a field.
Definition field.h:73
bool isUniqueKey() const
Definition field.h:237
TQPtrList< TableSchema > List
Type of tables list.
Definition tableschema.h:45
void containerMoved(KexiRelationViewTableContainer *c)
KexiRelationViewTableContainer * addTableContainer(KexiDB::TableSchema *t, const TQRect &rect=TQRect())
TablesDict * tables()
void clearSelection()
Clears current selection - table/query or connection.
KexiRelationViewTableContainer * tableContainer(KexiDB::TableSchema *t) const
void hideAllTablesExcept(KexiDB::TableSchema::List *tables)
void addConnection(const SourceConnection &_conn)
Adds a connection con to the area. This changes only visual representation.
void removeSelectedObject()
removes selected table or connection