mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-16 14:15:50 -06:00
844 lines
22 KiB
C++
844 lines
22 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// gqbView.cpp - View implementation for MVC Pattern of GQB
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// 1. READ MODEL STATE FROM gqbModel TO CREATE THE GRAPHIC REPRESENTATION OF THE QUERY
|
|
// 2. USE THE CONTROLLER TO CHANGE THE MODEL WITH THE USER INPUT
|
|
|
|
#include "pgAdmin3.h"
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
#include <wx/dcbuffer.h>
|
|
#include <wx/generic/gridctrl.h>
|
|
#include <wx/notebook.h>
|
|
#include <wx/choicdlg.h>
|
|
|
|
// App headers
|
|
#include "gqb/gqbModel.h"
|
|
#include "gqb/gqbEvents.h"
|
|
#include "gqb/gqbViewController.h"
|
|
#include "gqb/gqbQueryObjs.h"
|
|
#include "gqb/gqbGraphSimple.h"
|
|
#include "gqb/gqbViewPanels.h"
|
|
#include "gqb/gqbObject.h"
|
|
#include "gqb/gqbObjectCollection.h"
|
|
|
|
// Image
|
|
#include "images/gqbJoinCursor.pngc"
|
|
|
|
BEGIN_EVENT_TABLE(gqbView, wxScrolledWindow)
|
|
EVT_SIZE(gqbView::OnSize)
|
|
EVT_PAINT(gqbView::onPaint)
|
|
EVT_MOTION(gqbView::onMotion)
|
|
EVT_LEFT_DOWN(gqbView::onMotion)
|
|
EVT_RIGHT_DOWN(gqbView::onRightClick)
|
|
EVT_LEFT_UP(gqbView::onMotion)
|
|
EVT_LEFT_DCLICK(gqbView::onDoubleClick)
|
|
EVT_ERASE_BACKGROUND(gqbView::onEraseBackGround) //This erase flicker create by wxStaticText when erasing background but this is not needed
|
|
EVT_KEY_DOWN(gqbView::OnKeyDown)
|
|
EVT_MENU(GQB_RMJ_DELETE, gqbView::OnMenuJoinDelete)
|
|
EVT_MENU(GQB_RMT_DELETE, gqbView::OnMenuTableDelete)
|
|
EVT_MENU(GQB_RMT_SETALIAS, gqbView::OnMenuTableSetAlias)
|
|
EVT_MENU(GQB_REFRESH, gqbView::OnRefresh)
|
|
END_EVENT_TABLE()
|
|
|
|
gqbView::gqbView(wxWindow *gqbParent, ctlAuiNotebook *gridParent, wxSize size, gqbController *controller, gqbModel *model)
|
|
: wxScrolledWindow(gqbParent, wxID_ANY, wxPoint(201, 0), size,
|
|
wxHSCROLL | wxVSCROLL | wxBORDER | wxRETAINED)
|
|
{
|
|
this->controller = controller;
|
|
this->model = model;
|
|
pressed = -1;
|
|
selected = -1;
|
|
changeTOpressed = false;
|
|
canvasSize = size;
|
|
collectionSelected = NULL;
|
|
joinSelected = NULL;
|
|
joinSource = NULL;
|
|
joinDest = NULL;
|
|
joinSCol = NULL;
|
|
joinDCol = NULL;
|
|
refreshRate = 3;
|
|
iterator = NULL;
|
|
mode = pt_normal;
|
|
joinCursorImage = *gqbJoinCursor_png_img;
|
|
joinCursor = wxCursor(joinCursorImage);
|
|
m_rightJoins = NULL;
|
|
m_rightTables = NULL;
|
|
m_gqbPopup = NULL;
|
|
jTempSelected = NULL;
|
|
cTempSelected = NULL;
|
|
|
|
// Assing kind of join Options
|
|
joinTypeChoices.Add(wxString(wxT(" = ")));
|
|
joinTypeChoices.Add(wxString(wxT(" > ")));
|
|
joinTypeChoices.Add(wxString(wxT(" < ")));
|
|
joinTypeChoices.Add(wxString(wxT(" >= ")));
|
|
joinTypeChoices.Add(wxString(wxT(" <= ")));
|
|
|
|
// Assign default graphic behavior [skin of forms inside model]
|
|
this->graphBehavior = new gqbGraphSimple();
|
|
|
|
// Create Projection Panel
|
|
// GQB-TODO: move model to grid panel constructor
|
|
this->gridTable = new gqbGridProjTable(this->model->getOrderedColumns(), this->model->getColumnsParents(), this->model->getColumnsAlias());
|
|
this->projectionPanel = new gqbGridPanel(controller->getTabs(), -1, gridTable);
|
|
|
|
// Create Restrictions Panel
|
|
this->restrictionsGridTable = new gqbGridRestTable(model->getRestrictions());
|
|
this->criteriaPanel = new gqbCriteriaPanel(controller->getTabs(), model, restrictionsGridTable);
|
|
|
|
// Create Joins Panel
|
|
this->joinsGridTable = new gqbGridJoinTable(this->controller);
|
|
this->joinsPanel = new gqbJoinsPanel(controller->getTabs(), model, joinsGridTable, controller);
|
|
|
|
// Create Order by Panel
|
|
this->orderByLGridTable = new gqbGridOrderTable(1, model->getOrdByAvailColumns(), model->getOrdByAvailParents(), NULL);
|
|
this->orderByRGridTable = new gqbGridOrderTable(2, model->getOrdByColumns(), model->getOrdByParents(), model->getOrdByKind());
|
|
this->orderPanel = new gqbOrderPanel(controller->getTabs(), orderByLGridTable, orderByRGridTable);
|
|
|
|
#if !wxCHECK_VERSION(2, 9, 0)
|
|
// does nothing in 2.9+
|
|
SetVirtualSizeHints(size);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
gqbView::~gqbView()
|
|
{
|
|
if(graphBehavior)
|
|
delete graphBehavior;
|
|
if(iterator)
|
|
delete iterator;
|
|
|
|
if(m_rightTables)
|
|
delete m_rightTables;
|
|
|
|
if(m_rightJoins)
|
|
delete m_rightJoins;
|
|
|
|
if (m_gqbPopup)
|
|
delete m_gqbPopup;
|
|
|
|
if(orderByRGridTable)
|
|
delete orderByRGridTable;
|
|
|
|
if(orderByLGridTable)
|
|
delete orderByLGridTable;
|
|
}
|
|
|
|
|
|
// Overwrite and disable onEraseBackground Event to avoid Flicker
|
|
void gqbView::onEraseBackGround(wxEraseEvent &event)
|
|
{
|
|
}
|
|
|
|
|
|
// Detect when should be drawn the canvas with the model information
|
|
void gqbView::onPaint(wxPaintEvent &event)
|
|
{
|
|
wxPaintDC dcc(this); // Prepare Context for Buffered Draw
|
|
wxBufferedDC dc(&dcc, canvasSize);
|
|
drawAll(dc, true); // Call Function to draw all
|
|
}
|
|
|
|
|
|
// GQB-TODO: remove all possible modification to model from here to controller.
|
|
void gqbView::onRightClick(wxMouseEvent &event)
|
|
{
|
|
// GQB-TODO: Validate Alias
|
|
gqbObject *anySelected = NULL;
|
|
wxPoint pdc = event.GetPosition();
|
|
pdc.x = event.GetPosition().x;
|
|
pdc.y = event.GetPosition().y;
|
|
this->CalcUnscrolledPosition(pdc.x, pdc.y, &pdc.x, &pdc.y);
|
|
anySelected = controller->getModelSelected(pdc, cTempSelected, jTempSelected, false);
|
|
if(anySelected)
|
|
{
|
|
if(anySelected->getType() == GQB_QUERYOBJ)
|
|
{
|
|
if(!m_rightTables)
|
|
{
|
|
m_rightTables = new wxMenu;
|
|
m_rightTables->Append(GQB_RMT_SETALIAS, _("&Set Alias for table"));
|
|
m_rightTables->Append(GQB_RMT_DELETE, _("&Delete Table"));
|
|
m_rightTables->AppendSeparator();
|
|
m_rightTables->Append(GQB_REFRESH, _("&Refresh"));
|
|
|
|
}
|
|
cTempSelected = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
|
|
jTempSelected = NULL;
|
|
PopupMenu(m_rightTables, event.GetPosition());
|
|
}
|
|
|
|
if(anySelected->getType() == GQB_JOIN)
|
|
{
|
|
if(!m_rightJoins)
|
|
{
|
|
m_rightJoins = new wxMenu;
|
|
m_rightJoins->Append(GQB_RMJ_DELETE, _("&Delete Join"));
|
|
m_rightJoins->AppendSeparator();
|
|
m_rightJoins->Append(GQB_REFRESH, _("&Refresh"));
|
|
}
|
|
cTempSelected = NULL;
|
|
jTempSelected = (gqbQueryJoin *) anySelected;;
|
|
PopupMenu(m_rightJoins, event.GetPosition());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!m_gqbPopup)
|
|
{
|
|
m_gqbPopup = new wxMenu;
|
|
m_gqbPopup->Append(GQB_REFRESH, _("&Refresh"));
|
|
}
|
|
PopupMenu(m_gqbPopup, event.GetPosition());
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::OnMenuJoinDelete(wxCommandEvent &WXUNUSED(event))
|
|
{
|
|
if(jTempSelected)
|
|
{
|
|
this->joinsGridTable->removeJoin(jTempSelected);
|
|
controller->removeJoin(jTempSelected);
|
|
jTempSelected = NULL;
|
|
this->Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::OnMenuTableDelete(wxCommandEvent &WXUNUSED(event))
|
|
{
|
|
if(cTempSelected)
|
|
{
|
|
joinsGridTable->removeJoins(cTempSelected);
|
|
controller->removeTableFromModel(cTempSelected, gridTable, orderByLGridTable, orderByRGridTable);
|
|
cTempSelected = NULL;
|
|
this->Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::OnMenuTableSetAlias(wxCommandEvent &event)
|
|
{
|
|
if(cTempSelected)
|
|
{
|
|
// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
|
|
// as his parent the main container of the view, and void the bug
|
|
wxTextEntryDialog dialog(controller->getDialogParent(),
|
|
wxString::Format(_("Enter an alias for table %s"), cTempSelected->getName().c_str()),
|
|
_("Please enter an alias for the table."),
|
|
wxT(""),
|
|
wxOK | wxCANCEL | wxCENTRE);
|
|
dialog.SetValue(cTempSelected->getAlias());
|
|
if (dialog.ShowModal() == wxID_OK)
|
|
{
|
|
cTempSelected->setAlias(dialog.GetValue());
|
|
joinsPanel->Refresh();
|
|
}
|
|
cTempSelected = NULL;
|
|
this->Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::onDoubleClick(wxMouseEvent &event)
|
|
{
|
|
// GQB-TODO: Validate Alias
|
|
gqbObject *anySelected = NULL;
|
|
wxPoint pdc = event.GetPosition();
|
|
pdc.x = event.GetPosition().x;
|
|
pdc.y = event.GetPosition().y;
|
|
this->CalcUnscrolledPosition(pdc.x, pdc.y, &pdc.x, &pdc.y);
|
|
|
|
anySelected = controller->getModelSelected(pdc, cTempSelected, jTempSelected, false);
|
|
if(anySelected)
|
|
{
|
|
if(anySelected->getType() == GQB_QUERYOBJ)
|
|
{
|
|
gqbQueryObject *t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
|
|
|
|
// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
|
|
// as his parent the main container of the view, and void the bug
|
|
wxTextEntryDialog dialog(controller->getDialogParent(),
|
|
wxString::Format(_("Enter an alias for table %s"), t->getName().c_str()),
|
|
_("Please enter an alias for the table."),
|
|
wxT(""),
|
|
wxOK | wxCANCEL | wxCENTRE);
|
|
dialog.SetValue(t->getAlias());
|
|
if (dialog.ShowModal() == wxID_OK)
|
|
{
|
|
t->setAlias(dialog.GetValue());
|
|
joinsPanel->Refresh();
|
|
|
|
// hack to avoid misplaced joins anchors after insert an alias that trigger a table graph resize (bigger)
|
|
this->Refresh();
|
|
this->Update(); //force refresh
|
|
graphBehavior->UpdatePosObject(t, t->position.x, t->position.y, 0);
|
|
}
|
|
}
|
|
else if(anySelected->getType() == GQB_JOIN)
|
|
{
|
|
gqbQueryJoin *j = (gqbQueryJoin *) anySelected;
|
|
|
|
controller->getTabs()->ChangeSelection(ti_joinsPanel);
|
|
gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
|
|
jPanel->selectJoin(j);
|
|
}
|
|
}
|
|
this->Refresh();
|
|
}
|
|
|
|
|
|
// Manages user input [Mouse click, drag & drop] over the Canvas
|
|
void gqbView::onMotion(wxMouseEvent &event)
|
|
{
|
|
static int refresh = 1; // refresh counter, everytime this values reaches
|
|
// "refreshRate" value then Refresh while dragging
|
|
// Discover area where event ocurrs
|
|
pos.x = event.GetPosition().x;
|
|
pos.y = event.GetPosition().y;
|
|
this->CalcUnscrolledPosition(pos.x, pos.y, &pos.x, &pos.y);
|
|
gqbObject *anySelected = NULL;
|
|
|
|
// Button Down Event is triggered
|
|
if(event.ButtonDown() && !changeTOpressed)
|
|
{
|
|
this->SetFocus();
|
|
|
|
// Which kind of button down was? join creation [click on any column at the
|
|
// right of checkbox and drag & drop] or table moving [click on title and drag & drop]
|
|
anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
|
|
if(anySelected)
|
|
{
|
|
// Anything before just forget about it
|
|
changeTOpressed = false;
|
|
joinSource = NULL;
|
|
joinSCol = NULL;
|
|
joinDCol = NULL;
|
|
joinDest = NULL;
|
|
jpos.x = 0;
|
|
jpos.y = 0;
|
|
|
|
if(anySelected->getType() == GQB_QUERYOBJ)
|
|
{
|
|
gqbQueryObject *t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
|
|
|
|
// If click on the title area AND don't click on the columns selection checkbox
|
|
if( (pos.y - t->position.y <= graphBehavior->getTitleRowHeight()))
|
|
controller->setPointerMode(pt_normal);
|
|
else if(pos.x - t->position.x <= 17)
|
|
controller->setPointerMode(pt_normal);
|
|
else
|
|
controller->setPointerMode(pt_join);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
anySelected = NULL;
|
|
mode = pt_normal;
|
|
}
|
|
|
|
if(mode == pt_normal) // pointer is used to move tables & select/unselect columns
|
|
{
|
|
// getSelected Item [Mark it as selected if possible]
|
|
anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, true);
|
|
changeTOpressed = true;
|
|
|
|
// Do conversion of type object if any found
|
|
if(anySelected)
|
|
{
|
|
if(anySelected->getType() == GQB_QUERYOBJ)
|
|
{
|
|
collectionSelected = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
|
|
joinSelected = NULL;
|
|
}
|
|
else if(anySelected->getType() == GQB_JOIN)
|
|
{
|
|
joinSelected = (gqbQueryJoin *) anySelected;
|
|
collectionSelected = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collectionSelected = NULL;
|
|
joinSelected = NULL;
|
|
}
|
|
|
|
if(!collectionSelected)
|
|
{
|
|
// none selected temp unselect all items
|
|
controller->unsetModelSelected(true);
|
|
}
|
|
else
|
|
{
|
|
gqbColumn *col = graphBehavior->getColumnAtPosition(&pos, collectionSelected);
|
|
if(col)
|
|
{
|
|
// Add or remove column from model & observers (ex: Grid) (projection part of SQL sentence)
|
|
controller->processColumnInModel(collectionSelected, col, gridTable);
|
|
}
|
|
}
|
|
|
|
if(!joinSelected)
|
|
{
|
|
controller->unsetModelSelected(false);
|
|
}
|
|
|
|
}
|
|
// Pointer is used to add joins
|
|
else if(mode == pt_join)
|
|
{
|
|
anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
|
|
|
|
// Even if I get an object check that it isn't a join
|
|
if( (anySelected) && anySelected->getType() == GQB_QUERYOBJ)
|
|
joinSource = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
|
|
else
|
|
joinSource = NULL;
|
|
|
|
if(!joinSource)
|
|
{
|
|
// creation of join starts
|
|
joinSCol = NULL;
|
|
joinDCol = NULL;
|
|
jpos.x = 0;
|
|
jpos.y = 0;
|
|
}
|
|
else
|
|
{
|
|
joinSCol = graphBehavior->getColumnAtPosition(&pos, joinSource, joinSource->getWidth());
|
|
jpos = pos;
|
|
|
|
// GQB-TODO then draw line between column & pointer
|
|
}
|
|
}
|
|
|
|
this->Refresh();
|
|
}
|
|
|
|
// Button Up Event is triggered
|
|
if(event.ButtonUp())
|
|
{
|
|
// Pointer is used to move tables & select/unselect columns
|
|
if(mode == pt_normal)
|
|
{
|
|
changeTOpressed = false;
|
|
anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
|
|
if (anySelected && anySelected->getType() == GQB_JOIN)
|
|
{
|
|
gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
|
|
jPanel->selectJoin((gqbQueryJoin *)anySelected);
|
|
}
|
|
}
|
|
// Pointer is used to add joins
|
|
else if(mode == pt_join)
|
|
{
|
|
anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
|
|
|
|
// Even if I get an object check that it isn't a join
|
|
if( (anySelected) && anySelected->getType() == GQB_QUERYOBJ)
|
|
{
|
|
joinDest = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
|
|
// Validate not self joins [in this version tables can be duplicated to create same effect]
|
|
if(joinDest == joinSource)
|
|
{
|
|
joinDest = NULL;
|
|
}
|
|
}
|
|
else
|
|
joinDest = NULL;
|
|
|
|
// Creation of join starts
|
|
if(!joinDest)
|
|
{
|
|
joinSource = NULL;
|
|
joinSCol = NULL;
|
|
joinDCol = NULL;
|
|
joinDest = NULL;
|
|
jpos.x = 0;
|
|
jpos.y = 0;
|
|
}
|
|
else
|
|
{
|
|
joinDCol = graphBehavior->getColumnAtPosition(&pos, joinDest, joinDest->getWidth());
|
|
if(joinDCol)
|
|
{
|
|
// GQB-TODO: Allow other type of joins
|
|
gqbQueryJoin *qj = controller->addJoin(joinSource, joinSCol, joinDest, joinDCol, _equally);
|
|
graphBehavior->calcAnchorPoint(qj);
|
|
this->joinsGridTable->AppendJoin(qj);
|
|
}
|
|
// 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]
|
|
joinSource = NULL;
|
|
joinSCol = NULL;
|
|
joinDest = NULL;
|
|
joinDCol = NULL;
|
|
jpos.x = 0;
|
|
jpos.y = 0;
|
|
}
|
|
}
|
|
|
|
controller->setPointerMode(pt_normal); //when button is up, pointer mode should be only normal
|
|
this->Refresh();
|
|
}
|
|
|
|
// Mouse is Dragged while mouse button is down
|
|
if (event.Dragging() && pressed)
|
|
{
|
|
if(mode == pt_normal)
|
|
{
|
|
if(collectionSelected)
|
|
{
|
|
// GQB-TODO: same as gqbGraphBehavior.h [find a way to not hard code the 17 default value]
|
|
if((pos.x > collectionSelected->position.x + 17) || (pos.x < collectionSelected->position.x) )
|
|
{
|
|
graphBehavior->UpdatePosObject(collectionSelected, pos.x, pos.y, 40);
|
|
}
|
|
|
|
// Don't draw too much when dragging table around canvas [lower cpu use]
|
|
if(refresh % refreshRate == 0)
|
|
{
|
|
this->Refresh();
|
|
refresh = 1;
|
|
}
|
|
else
|
|
refresh++;
|
|
|
|
}
|
|
}
|
|
else if(mode == pt_join)
|
|
{
|
|
if(joinSource && !joinDest)
|
|
{
|
|
this->Refresh();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::OnKeyDown(wxKeyEvent &event)
|
|
{
|
|
if(event.GetKeyCode() == WXK_DELETE)
|
|
{
|
|
if(collectionSelected)
|
|
{
|
|
this->joinsGridTable->removeJoins(collectionSelected);
|
|
controller->removeTableFromModel(collectionSelected, gridTable, orderByLGridTable, orderByRGridTable);
|
|
collectionSelected = NULL;
|
|
this->Refresh();
|
|
}
|
|
|
|
if(joinSelected)
|
|
{
|
|
this->joinsGridTable->removeJoin(joinSelected);
|
|
controller->removeJoin(joinSelected);
|
|
joinSelected = NULL;
|
|
this->Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::drawAll(wxMemoryDC &bdc, bool adjustScrolling)
|
|
{
|
|
bdc.Clear();
|
|
if(!iterator)
|
|
// Get an iterator for the objects (tables/views) in the model.
|
|
iterator = this->model->createQueryIterator();
|
|
else
|
|
iterator->ResetIterator();
|
|
|
|
// First Draw Tables
|
|
while(iterator->HasNext())
|
|
{
|
|
gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();
|
|
wxPoint pt = wxPoint(tmp->position); // Use a copy because I don't want to store the modified
|
|
// version of point after CalcScrolledPosition was called
|
|
|
|
if (adjustScrolling)
|
|
{
|
|
// adjust coordinates
|
|
this->CalcScrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
|
|
}
|
|
graphBehavior->drawTable(bdc, &pt, tmp); // graph table
|
|
}
|
|
|
|
// Later Draw Joins over Tables
|
|
iterator->ResetIterator();
|
|
while(iterator->HasNext())
|
|
{
|
|
gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();
|
|
|
|
if(tmp->getHaveJoins())
|
|
{
|
|
gqbIteratorBase *joinsIterator = tmp->createJoinsIterator();
|
|
while(joinsIterator->HasNext())
|
|
{
|
|
gqbQueryJoin *join = (gqbQueryJoin *) joinsIterator->Next();
|
|
wxPoint o = join->getSourceAnchor();
|
|
wxPoint d = join->getDestAnchor();
|
|
|
|
if (adjustScrolling)
|
|
{
|
|
// adjust coordinates origin
|
|
this->CalcScrolledPosition(o.x, o.y, &o.x, &o.y);
|
|
|
|
// adjust coordinates destination
|
|
this->CalcScrolledPosition(d.x, d.y, &d.x, &d.y);
|
|
}
|
|
graphBehavior->drawJoin(bdc, o, d, join->getAnchorsUsed(), join->getSelected(), join->getKindofJoin());
|
|
}
|
|
delete joinsIterator;
|
|
}
|
|
|
|
}
|
|
|
|
// This iterator is delete at destroyer for reuse purposes
|
|
if(joinSource)
|
|
{
|
|
// Draw temporary line while creating a join
|
|
wxPoint source = jpos;
|
|
wxPoint destination = pos;
|
|
if(adjustScrolling)
|
|
{
|
|
this->CalcScrolledPosition(source.x, source.y, &source.x, &source.y);
|
|
this->CalcScrolledPosition(destination.x, destination.y, &destination.x, &destination.y);
|
|
}
|
|
graphBehavior->drawTempJoinLine(bdc, source, destination);
|
|
}
|
|
}
|
|
|
|
|
|
void gqbView::setPointerMode(pointerMode pm)
|
|
{
|
|
mode = pm;
|
|
if(mode == pt_join)
|
|
this->SetCursor(joinCursor);
|
|
else
|
|
this->SetCursor(wxNullCursor);
|
|
}
|
|
|
|
|
|
bool gqbView::clickOnJoin (gqbQueryJoin *join, wxPoint &pt, wxPoint &origin, wxPoint &dest)
|
|
{
|
|
return graphBehavior->clickOnJoin(join, pt, origin, dest);
|
|
}
|
|
|
|
|
|
void gqbView::emptyPanelsData()
|
|
{
|
|
gridTable->emptyTableData();
|
|
this->joinsGridTable->emptyTableData();
|
|
}
|
|
|
|
|
|
void gqbView::newTableAdded(gqbQueryObject *item)
|
|
{
|
|
// Refresh Order By Panel's Left Grid
|
|
if (orderByLGridTable->GetView() )
|
|
{
|
|
wxGridTableMessage msg( orderByLGridTable,
|
|
wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
|
|
orderByLGridTable->GetNumberRows() - 1,
|
|
item->parent->countCols() );
|
|
orderByLGridTable->GetView()->ProcessTableMessage( msg );
|
|
}
|
|
}
|
|
|
|
void gqbView::updateTable(gqbQueryObject *queryTable)
|
|
{
|
|
if (queryTable->getHaveJoins())
|
|
{
|
|
gqbIteratorBase *j = queryTable->createJoinsIterator();
|
|
while (j->HasNext())
|
|
{
|
|
gqbQueryJoin *tmp = (gqbQueryJoin *)j->Next();
|
|
graphBehavior->calcAnchorPoint(tmp);
|
|
}
|
|
delete j;
|
|
}
|
|
|
|
// Update position of anchor points of Joins that come from others tables
|
|
if (queryTable->getHaveRegJoins())
|
|
{
|
|
gqbIteratorBase *r = queryTable->createRegJoinsIterator();
|
|
while (r->HasNext())
|
|
{
|
|
gqbQueryJoin *tmp = (gqbQueryJoin *)r->Next();
|
|
graphBehavior->calcAnchorPoint(tmp);
|
|
}
|
|
delete r;
|
|
}
|
|
this->Refresh();
|
|
}
|
|
|
|
|
|
void gqbView::OnRefresh(wxCommandEvent &ev)
|
|
{
|
|
updateModelSize(NULL, true);
|
|
this->Update();
|
|
}
|
|
|
|
|
|
/*
|
|
* updateModelSize
|
|
* - Update the model size.
|
|
* - Calculate the maximum width and maximum height of the model
|
|
* * When removed a table/view from model, the obj parameter must be null,
|
|
* and update parameter should be true, otherwise update parameter should
|
|
* be false (Dragging event)
|
|
*/
|
|
void gqbView::updateModelSize(gqbQueryObject *obj, bool updateAnyWay)
|
|
{
|
|
static int callCount = 0;
|
|
callCount++;
|
|
if (!obj)
|
|
{
|
|
// Do not update model size, everytime it gets called
|
|
// Update the size once in 10 times
|
|
// Update the size only if update flag is true
|
|
if (callCount < 10 && !updateAnyWay)
|
|
return;
|
|
callCount = 0;
|
|
// Figure out the actual model size.
|
|
// Remove table
|
|
int w = 0, h = 0, maxW = 0, maxH = 0;
|
|
if(!iterator)
|
|
// Get an iterator for the objects (tables/views) in the model.
|
|
iterator = this->model->createQueryIterator();
|
|
else
|
|
iterator->ResetIterator();
|
|
|
|
while (iterator->HasNext())
|
|
{
|
|
gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();;
|
|
w = tmp->position.x + tmp->getWidth();
|
|
h = tmp->position.y + tmp->getHeight();
|
|
|
|
if (maxW < w)
|
|
maxW = w;
|
|
if (maxH < h)
|
|
maxH = h;
|
|
}
|
|
|
|
// Reset Model size
|
|
modelSize.Set(maxW, maxH);
|
|
}
|
|
else
|
|
{
|
|
int w = 0, h = 0;
|
|
w = obj->position.x + obj->getWidth();
|
|
h = obj->position.y + obj->getHeight();
|
|
|
|
if (w > modelSize.GetWidth())
|
|
modelSize.SetWidth(w);
|
|
if (h > modelSize.GetHeight())
|
|
modelSize.SetHeight(h);
|
|
}
|
|
bool updateView = false;
|
|
int viewW, viewH;
|
|
GetSize(&viewW, &viewH);
|
|
|
|
if (viewW < GQB_MIN_WIDTH)
|
|
viewW = GQB_MIN_WIDTH;
|
|
|
|
if (viewH < GQB_MIN_HEIGHT)
|
|
viewH = GQB_MIN_HEIGHT;
|
|
|
|
if ((modelSize.GetWidth() > viewW || canvasSize.GetWidth() > viewW) &&
|
|
modelSize.GetWidth() != canvasSize.GetWidth())
|
|
{
|
|
canvasSize.SetWidth((modelSize.GetWidth() > viewW ? modelSize.GetWidth() : viewW));
|
|
updateView = true;
|
|
}
|
|
if ((modelSize.GetHeight() > viewH || canvasSize.GetHeight() > viewH ) &&
|
|
modelSize.GetHeight() != canvasSize.GetHeight())
|
|
{
|
|
canvasSize.SetHeight((modelSize.GetHeight() > viewH ? modelSize.GetHeight() : viewH));
|
|
updateView = true;
|
|
}
|
|
|
|
if (canvasSize.GetWidth() < viewW)
|
|
{
|
|
canvasSize.SetWidth(viewW);
|
|
updateView = true;
|
|
}
|
|
|
|
if (canvasSize.GetHeight() < viewH)
|
|
{
|
|
canvasSize.SetHeight(viewH);
|
|
updateView = true;
|
|
}
|
|
|
|
if (updateView)
|
|
{
|
|
SetVirtualSize(canvasSize);
|
|
}
|
|
|
|
FitInside();
|
|
Refresh();
|
|
}
|
|
|
|
void gqbView::OnSize(wxSizeEvent &event)
|
|
{
|
|
updateModelSize(NULL, true);
|
|
}
|
|
|
|
|
|
bool gqbView::canSaveAsImage()
|
|
{
|
|
updateModelSize(NULL, true);
|
|
return !(modelSize.GetWidth() == 0 || modelSize.GetHeight() == 0);
|
|
}
|
|
|
|
void gqbView::SaveAsImage(const wxString &fileName, wxBitmapType imgType)
|
|
{
|
|
|
|
updateModelSize(NULL, true);
|
|
|
|
if (modelSize.GetWidth() == 0 || modelSize.GetHeight() == 0)
|
|
{
|
|
wxMessageBox(_("Nothing to be saved!"), _("Save As an image"), wxOK | wxICON_INFORMATION);
|
|
return;
|
|
}
|
|
|
|
int width = 0, height = 0;
|
|
GetVirtualSize(&width, &height);
|
|
|
|
/*
|
|
* Create the bitmap from the Explain window
|
|
*/
|
|
wxMemoryDC memDC;
|
|
wxBitmap tempBitmap(width, height);
|
|
|
|
memDC.SelectObject(tempBitmap);
|
|
memDC.Clear();
|
|
|
|
// Draw the diagram on the bitmap (Memory Device Context)
|
|
drawAll(memDC, false);
|
|
|
|
memDC.SelectObject(wxNullBitmap);
|
|
|
|
if (!tempBitmap.SaveFile(fileName, imgType))
|
|
{
|
|
wxLogError(_("Could not write the file %s: Errcode=%d."), fileName.c_str(), wxSysErrorCode());
|
|
}
|
|
}
|
|
|
|
|
|
|