1
//////////////////////////////////////////////////////////////////////////
3
// pgAdmin III - PostgreSQL Tools
4
// RCS-ID: $Id: gqbView.cpp 7758 2009-03-26 20:49:59Z dpage $
5
// Copyright (C) 2002 - 2009, The pgAdmin Development Team
6
// This software is released under the BSD Licence
8
// gqbView.cpp - View implementation for MVC Pattern of GQB
10
//////////////////////////////////////////////////////////////////////////
13
// 1. READ MODEL STATE FROM gqbModel TO CREATE THE GRAPHIC REPRESENTATION OF THE QUERY
14
// 2. USE THE CONTROLLER TO CHANGE THE MODEL WITH THE USER INPUT
20
#include <wx/dcbuffer.h>
21
#include <wx/generic/gridctrl.h>
22
#include <wx/notebook.h>
23
#include <wx/choicdlg.h>
26
#include "gqb/gqbModel.h"
27
#include "gqb/gqbEvents.h"
28
#include "gqb/gqbViewController.h"
29
#include "gqb/gqbQueryObjs.h"
30
#include "gqb/gqbGraphSimple.h"
31
#include "gqb/gqbViewPanels.h"
32
#include "gqb/gqbObject.h"
33
#include "gqb/gqbObjectCollection.h"
36
#include "images/gqbJoinCursor.xpm"
38
BEGIN_EVENT_TABLE(gqbView, wxScrolledWindow)
39
EVT_PAINT(gqbView::onPaint)
40
EVT_MOTION(gqbView::onMotion)
41
EVT_LEFT_DOWN(gqbView::onMotion)
42
EVT_RIGHT_DOWN(gqbView::onRightClick)
43
EVT_LEFT_UP(gqbView::onMotion)
44
EVT_LEFT_DCLICK(gqbView::onDoubleClick)
45
EVT_ERASE_BACKGROUND(gqbView::onEraseBackGround) //This erase flicker create by wxStaticText when erasing background but this is not needed
46
EVT_KEY_DOWN(gqbView::OnKeyDown)
47
EVT_MENU(GQB_RMJ_DELETE, gqbView::OnMenuJoinDelete)
48
EVT_MENU(GQB_RMT_DELETE, gqbView::OnMenuTableDelete)
49
EVT_MENU(GQB_RMT_SETALIAS, gqbView::OnMenuTableSetAlias)
52
gqbView::gqbView(wxWindow *gqbParent, wxNotebook *gridParent, wxSize size, gqbController *controller, gqbModel *model)
53
: wxScrolledWindow(gqbParent, wxID_ANY, wxPoint(201,0), size,
54
wxHSCROLL | wxVSCROLL | wxBORDER | wxRETAINED)
56
this->controller=controller;
60
changeTOpressed=false;
62
collectionSelected=NULL;
71
joinCursorImage = wxBitmap(gqbJoinCursor_xpm).ConvertToImage();
72
joinCursor= wxCursor(joinCursorImage);
78
// Assing kind of join Options
79
joinTypeChoices.Add(wxString(wxT(" = ")));
80
joinTypeChoices.Add(wxString(wxT(" > ")));
81
joinTypeChoices.Add(wxString(wxT(" < ")));
82
joinTypeChoices.Add(wxString(wxT(" >= ")));
83
joinTypeChoices.Add(wxString(wxT(" <= ")));
85
// Assign default graphic behavior [skin of forms inside model]
86
this->graphBehavior = new gqbGraphSimple();
88
// Create Projection Panel
89
// GQB-TODO: move model to grid panel constructor
90
this->gridTable = new gqbGridProjTable(this->model->getOrderedColumns(),this->model->getColumnsParents(),this->model->getColumnsAlias());
91
this->projectionPanel = new gqbGridPanel(controller->getTabs(),-1,gridTable);
93
// Create Restrictions Panel
94
this->restrictionsGridTable = new gqbGridRestTable(model->getRestrictions());
95
this->criteriaPanel = new gqbCriteriaPanel(controller->getTabs(),model,restrictionsGridTable);
98
this->joinsGridTable = new gqbGridJoinTable(this->controller);
99
this->joinsPanel = new gqbJoinsPanel(controller->getTabs(), model, joinsGridTable, controller);
101
// Create Order by Panel
102
this->orderByLGridTable = new gqbGridOrderTable(1,model->getOrdByAvailColumns(),model->getOrdByAvailParents(),NULL);
103
this->orderByRGridTable = new gqbGridOrderTable(2,model->getOrdByColumns(), model->getOrdByParents(),model->getOrdByKind());
104
this->orderPanel = new gqbOrderPanel(controller->getTabs(), orderByLGridTable, orderByRGridTable);
111
delete graphBehavior;
116
delete m_rightTables;
123
// Overwrite and disable onEraseBackground Event to avoid Flicker
124
void gqbView::onEraseBackGround(wxEraseEvent& event)
129
// Detect when should be drawn the canvas with the model information
130
void gqbView::onPaint(wxPaintEvent& event)
132
wxPaintDC dcc(this); // Prepare Context for Buffered Draw
133
wxBufferedDC dc(&dcc, canvasSize);
134
drawAll(dc); // Call Function to draw all
138
// GQB-TODO: remove all possible modification to model from here to controller.
139
void gqbView::onRightClick(wxMouseEvent& event)
141
// GQB-TODO: Validate Alias
142
gqbObject *anySelected=NULL;
143
wxPoint pdc=event.GetPosition();
144
pdc.x=event.GetPosition().x;
145
pdc.y=event.GetPosition().y;
146
this->CalcUnscrolledPosition(pdc.x,pdc.y,&pdc.x,&pdc.y);
147
anySelected=controller->getModelSelected(pdc,cTempSelected, jTempSelected, false);
150
if(anySelected->getType()==GQB_QUERYOBJ)
154
m_rightTables = new wxMenu;
155
m_rightTables->Append(GQB_RMT_SETALIAS, _("&Set Alias for table"));
156
m_rightTables->Append(GQB_RMT_DELETE, _("&Delete Table"));
158
cTempSelected=(gqbQueryObject *) (gqbObjectCollection *) anySelected;
160
PopupMenu(m_rightTables, event.GetPosition());
163
if(anySelected->getType()==GQB_JOIN)
167
m_rightJoins = new wxMenu;
168
m_rightJoins->Append(GQB_RMJ_DELETE, _("&Delete Join"));
171
jTempSelected=(gqbQueryJoin *) anySelected;;
172
PopupMenu(m_rightJoins, event.GetPosition());
178
void gqbView::OnMenuJoinDelete(wxCommandEvent& WXUNUSED(event))
182
this->joinsGridTable->removeJoin(joinSelected);
183
controller->removeJoin(jTempSelected);
190
void gqbView::OnMenuTableDelete(wxCommandEvent& WXUNUSED(event))
194
joinsGridTable->removeJoins(cTempSelected);
195
controller->removeTableFromModel(cTempSelected, gridTable, orderByLGridTable, orderByRGridTable);
202
void gqbView::OnMenuTableSetAlias(wxCommandEvent& event)
206
// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
207
// as his parent the main container of the view, and void the bug
208
wxTextEntryDialog dialog(controller->getDialogParent(),
209
wxString::Format(_("Enter an alias for table %s"), cTempSelected->getName().c_str()),
210
_("Please enter an alias for the table."),
212
wxOK | wxCANCEL| wxCENTRE);
213
dialog.SetValue(cTempSelected->getAlias());
214
if (dialog.ShowModal() == wxID_OK)
216
cTempSelected->setAlias(dialog.GetValue());
217
joinsPanel->Refresh();
225
void gqbView::onDoubleClick(wxMouseEvent& event)
227
// GQB-TODO: Validate Alias
228
gqbObject *anySelected=NULL;
229
wxPoint pdc=event.GetPosition();
230
pdc.x=event.GetPosition().x;
231
pdc.y=event.GetPosition().y;
232
this->CalcUnscrolledPosition(pdc.x,pdc.y,&pdc.x,&pdc.y);
234
anySelected=controller->getModelSelected(pdc,cTempSelected, jTempSelected, false);
237
if(anySelected->getType()==GQB_QUERYOBJ)
239
gqbQueryObject *t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
241
// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
242
// as his parent the main container of the view, and void the bug
243
wxTextEntryDialog dialog(controller->getDialogParent(),
244
wxString::Format(_("Enter an alias for table %s"), t->getName().c_str()),
245
_("Please enter an alias for the table."),
247
wxOK | wxCANCEL| wxCENTRE);
248
dialog.SetValue(t->getAlias());
249
if (dialog.ShowModal() == wxID_OK)
251
t->setAlias(dialog.GetValue());
252
joinsPanel->Refresh();
254
// hack to avoid misplaced joins anchors after insert an alias that trigger a table graph resize (bigger)
256
this->Update(); //force refresh
257
graphBehavior->UpdatePosObject(t,t->position.x,t->position.y,0);
260
else if(anySelected->getType()==GQB_JOIN)
262
gqbQueryJoin *j = (gqbQueryJoin *) anySelected;
264
controller->getTabs()->ChangeSelection(ti_joinsPanel);
265
gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
266
jPanel->selectJoin(j);
273
// Manages user input [Mouse click, drag & drop] over the Canvas
274
void gqbView::onMotion(wxMouseEvent& event)
276
static int refresh=1; // refresh counter, everytime this values reaches
277
// "refreshRate" value then Refresh while dragging
278
// Discover area where event ocurrs
279
pos.x=event.GetPosition().x;
280
pos.y=event.GetPosition().y;
281
this->CalcUnscrolledPosition(pos.x,pos.y,&pos.x,&pos.y);
282
gqbObject *anySelected=NULL;
284
// Button Down Event is triggered
285
if(event.ButtonDown()&& !changeTOpressed)
289
// Which kind of button down was? join creation [click on any column at the
290
// right of checkbox and drag & drop] or table moving [click on title and drag & drop]
291
anySelected=controller->getModelSelected(pos,collectionSelected, joinSelected, false);
294
// Anything before just forget about it
295
changeTOpressed=false;
303
if(anySelected->getType()==GQB_QUERYOBJ)
305
gqbQueryObject* t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
307
// If click on the title area AND don't click on the columns selection checkbox
308
if( (pos.y-t->position.y <= graphBehavior->getTitleRowHeight()))
309
controller->setPointerMode(pt_normal);
311
if(pos.x - t->position.x <= 17)
312
controller->setPointerMode(pt_normal);
314
controller->setPointerMode(pt_join);
323
if(mode==pt_normal) // pointer is used to move tables & select/unselect columns
325
// getSelected Item [Mark it as selected if possible]
326
anySelected=controller->getModelSelected(pos,collectionSelected, joinSelected, true);
327
changeTOpressed=true;
329
// Do conversion of type object if any found
332
if(anySelected->getType()==GQB_QUERYOBJ)
334
collectionSelected = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
337
else if(anySelected->getType()==GQB_JOIN)
339
joinSelected = (gqbQueryJoin *) anySelected;
340
collectionSelected = NULL;
345
collectionSelected = NULL;
349
if(!collectionSelected)
351
// none selected temp unselect all items
352
controller->unsetModelSelected(true);
356
gqbColumn *col=graphBehavior->getColumnAtPosition(&pos,collectionSelected);
359
// Add or remove column from model & observers (ex: Grid) (projection part of SQL sentence)
360
controller->processColumnInModel(collectionSelected,col,gridTable);
366
controller->unsetModelSelected(false);
370
// Pointer is used to add joins
371
else if(mode==pt_join)
373
anySelected=controller->getModelSelected(pos,collectionSelected, joinSelected, false);
375
// Even if I get an object check that it isn't a join
376
if( (anySelected) && anySelected->getType()==GQB_QUERYOBJ)
377
joinSource = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
383
// creation of join starts
391
joinSCol=graphBehavior->getColumnAtPosition(&pos,joinSource,joinSource->getWidth());
394
// GQB-TODO then draw line between column & pointer
401
// Button Up Event is triggered
404
// Pointer is used to move tables & select/unselect columns
407
changeTOpressed=false;
408
anySelected=controller->getModelSelected(pos, collectionSelected, joinSelected, false);
409
if (anySelected && anySelected->getType() == GQB_JOIN)
411
gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
412
jPanel->selectJoin((gqbQueryJoin *)anySelected);
415
// Pointer is used to add joins
416
else if(mode==pt_join)
418
anySelected=controller->getModelSelected(pos,collectionSelected, joinSelected, false);
420
// Even if I get an object check that it isn't a join
421
if( (anySelected) && anySelected->getType()==GQB_QUERYOBJ)
423
joinDest = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
424
// Validate not self joins [in this version tables can be duplicated to create same effect]
425
if(joinDest==joinSource)
432
// Creation of join starts
444
joinDCol=graphBehavior->getColumnAtPosition(&pos,joinDest,joinDest->getWidth());
447
// GQB-TODO: Allow other type of joins
448
gqbQueryJoin *qj=controller->addJoin(joinSource,joinSCol,joinDest,joinDCol,_equally);
449
graphBehavior->calcAnchorPoint(qj);
450
this->joinsGridTable->AppendJoin(qj);
452
// Let the temporary join line to be draw again [Don't destroy anything because all object where own by other objects this are just pointers]
462
controller->setPointerMode(pt_normal); //when button is up, pointer mode should be only normal
466
// Mouse is Dragged while mouse button is down
467
if (event.Dragging()&&pressed)
471
if(collectionSelected)
473
// GQB-TODO: same as gqbGraphBehavior.h [find a way to not hard code the 17 default value]
474
if((pos.x > collectionSelected->position.x+17) || (pos.x < collectionSelected->position.x) )
476
graphBehavior->UpdatePosObject(collectionSelected,pos.x,pos.y,40);
479
// Don't draw too much when dragging table around canvas [lower cpu use]
480
if(refresh%refreshRate==0)
489
else if(mode==pt_join)
491
if(joinSource && !joinDest)
501
void gqbView::OnKeyDown(wxKeyEvent& event)
503
if(event.GetKeyCode() == WXK_DELETE)
505
if(collectionSelected)
507
this->joinsGridTable->removeJoins(collectionSelected);
508
controller->removeTableFromModel(collectionSelected,gridTable,orderByLGridTable,orderByRGridTable);
509
collectionSelected=NULL;
515
this->joinsGridTable->removeJoin(joinSelected);
516
controller->removeJoin(joinSelected);
524
void gqbView::drawAll(wxBufferedDC& bdc)
528
// Get an iterator for the objects (tables/views) in the model.
529
iterator = this->model->createQueryIterator();
531
iterator->ResetIterator();
534
while(iterator->HasNext())
536
gqbQueryObject *tmp= (gqbQueryObject *)iterator->Next();
537
wxPoint pt = wxPoint(tmp->position); // Use a copy because I don't want to store the modified
538
// version of point after CalcScrolledPosition was called
540
// adjust coordinates
541
this->CalcScrolledPosition(pt.x,pt.y,&pt.x,&pt.y);
542
graphBehavior->drawTable(bdc,&pt,tmp); // graph table
545
// Later Draw Joins over Tables
546
iterator->ResetIterator();
547
while(iterator->HasNext())
549
gqbQueryObject *tmp= (gqbQueryObject *)iterator->Next();
551
if(tmp->getHaveJoins())
553
gqbIteratorBase *joinsIterator = tmp->createJoinsIterator();
554
while(joinsIterator->HasNext())
556
gqbQueryJoin *join = (gqbQueryJoin *) joinsIterator->Next();
557
wxPoint o = join->getSourceAnchor();
558
wxPoint d = join->getDestAnchor();
560
// adjust coordinates origin
561
this->CalcScrolledPosition(o.x,o.y,&o.x,&o.y);
563
// adjust coordinates destination
564
this->CalcScrolledPosition(d.x,d.y,&d.x,&d.y);
565
graphBehavior->drawJoin(bdc,o,d,join->getAnchorsUsed(), join->getSelected(), join->getKindofJoin());
567
delete joinsIterator;
572
// This iterator is delete at destroyer for reuse purposes
575
// Draw temporary line while creating a join
577
wxPoint destination=pos;
578
this->CalcScrolledPosition(source.x,source.y,&source.x,&source.y);
579
this->CalcScrolledPosition(destination.x,destination.y,&destination.x,&destination.y);
580
graphBehavior->drawTempJoinLine(bdc,source,destination);
585
void gqbView::setPointerMode(pointerMode pm)
589
this->SetCursor(joinCursor);
591
this->SetCursor(wxNullCursor);
595
bool gqbView::clickOnJoin (gqbQueryJoin *join, wxPoint &pt, wxPoint &origin, wxPoint &dest)
597
return graphBehavior->clickOnJoin(join,pt,origin,dest);
601
void gqbView::emptyPanelsData()
603
gridTable->emptyTableData();
604
this->joinsGridTable->emptyTableData();
608
void gqbView::newTableAdded(gqbQueryObject *item)
610
// Refresh Order By Panel's Left Grid
611
if (orderByLGridTable->GetView() )
613
wxGridTableMessage msg( orderByLGridTable,
614
wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
615
orderByLGridTable->GetNumberRows()-1,
616
item->parent->countCols() );
617
orderByLGridTable->GetView()->ProcessTableMessage( msg );
621
void gqbView::updateTable(gqbQueryObject *queryTable)
623
if (queryTable->getHaveJoins())
625
gqbIteratorBase *j=queryTable->createJoinsIterator();
628
gqbQueryJoin *tmp= (gqbQueryJoin *)j->Next();
629
graphBehavior->calcAnchorPoint(tmp);
634
// Update position of anchor points of Joins that come from others tables
635
if (queryTable->getHaveRegJoins())
637
gqbIteratorBase *r=queryTable->createRegJoinsIterator();
640
gqbQueryJoin *tmp= (gqbQueryJoin *)r->Next();
641
graphBehavior->calcAnchorPoint(tmp);