1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // gqbView.cpp - View implementation for MVC Pattern of GQB
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 
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
15 
16 #include "pgAdmin3.h"
17 
18 // wxWindows headers
19 #include <wx/wx.h>
20 #include <wx/dcbuffer.h>
21 #include <wx/generic/gridctrl.h>
22 #include <wx/notebook.h>
23 #include <wx/choicdlg.h>
24 
25 // App headers
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"
34 
35 // Image
36 #include "images/gqbJoinCursor.pngc"
37 
BEGIN_EVENT_TABLE(gqbView,wxScrolledWindow)38 BEGIN_EVENT_TABLE(gqbView, wxScrolledWindow)
39 	EVT_SIZE(gqbView::OnSize)
40 	EVT_PAINT(gqbView::onPaint)
41 	EVT_MOTION(gqbView::onMotion)
42 	EVT_LEFT_DOWN(gqbView::onMotion)
43 	EVT_RIGHT_DOWN(gqbView::onRightClick)
44 	EVT_LEFT_UP(gqbView::onMotion)
45 	EVT_LEFT_DCLICK(gqbView::onDoubleClick)
46 	EVT_ERASE_BACKGROUND(gqbView::onEraseBackGround)  //This erase flicker create by wxStaticText when erasing background but this is not needed
47 	EVT_KEY_DOWN(gqbView::OnKeyDown)
48 	EVT_MENU(GQB_RMJ_DELETE,	gqbView::OnMenuJoinDelete)
49 	EVT_MENU(GQB_RMT_DELETE,	gqbView::OnMenuTableDelete)
50 	EVT_MENU(GQB_RMT_SETALIAS,	gqbView::OnMenuTableSetAlias)
51 	EVT_MENU(GQB_REFRESH,           gqbView::OnRefresh)
52 END_EVENT_TABLE()
53 
54 gqbView::gqbView(wxWindow *gqbParent, ctlAuiNotebook *gridParent, wxSize size, gqbController *controller, gqbModel *model)
55 	: wxScrolledWindow(gqbParent, wxID_ANY, wxPoint(201, 0), size,
56 	                   wxHSCROLL | wxVSCROLL | wxBORDER | wxRETAINED)
57 {
58 	this->controller = controller;
59 	this->model = model;
60 	pressed = -1;
61 	selected = -1;
62 	changeTOpressed = false;
63 	canvasSize = size;
64 	collectionSelected = NULL;
65 	joinSelected = NULL;
66 	joinSource = NULL;
67 	joinDest = NULL;
68 	joinSCol = NULL;
69 	joinDCol = NULL;
70 	refreshRate = 3;
71 	iterator = NULL;
72 	mode = pt_normal;
73 	joinCursorImage = *gqbJoinCursor_png_img;
74 	joinCursor = wxCursor(joinCursorImage);
75 	m_rightJoins = NULL;
76 	m_rightTables = NULL;
77 	m_gqbPopup = NULL;
78 	jTempSelected = NULL;
79 	cTempSelected = NULL;
80 
81 	// Assing kind of join Options
82 	joinTypeChoices.Add(wxString(wxT(" = ")));
83 	joinTypeChoices.Add(wxString(wxT(" > ")));
84 	joinTypeChoices.Add(wxString(wxT(" < ")));
85 	joinTypeChoices.Add(wxString(wxT(" >= ")));
86 	joinTypeChoices.Add(wxString(wxT(" <= ")));
87 
88 	// Assign default graphic behavior [skin of forms inside model]
89 	this->graphBehavior = new gqbGraphSimple();
90 
91 	// Create Projection Panel
92 	// GQB-TODO: move model to grid panel constructor
93 	this->gridTable = new gqbGridProjTable(this->model->getOrderedColumns(), this->model->getColumnsParents(), this->model->getColumnsAlias());
94 	this->projectionPanel = new gqbGridPanel(controller->getTabs(), -1, gridTable);
95 
96 	// Create Restrictions Panel
97 	this->restrictionsGridTable = new gqbGridRestTable(model->getRestrictions());
98 	this->criteriaPanel = new gqbCriteriaPanel(controller->getTabs(), model, restrictionsGridTable);
99 
100 	// Create Joins Panel
101 	this->joinsGridTable = new gqbGridJoinTable(this->controller);
102 	this->joinsPanel = new gqbJoinsPanel(controller->getTabs(), model, joinsGridTable, controller);
103 
104 	// Create Order by Panel
105 	this->orderByLGridTable = new gqbGridOrderTable(1, model->getOrdByAvailColumns(), model->getOrdByAvailParents(), NULL);
106 	this->orderByRGridTable = new gqbGridOrderTable(2, model->getOrdByColumns(), model->getOrdByParents(), model->getOrdByKind());
107 	this->orderPanel = new gqbOrderPanel(controller->getTabs(), orderByLGridTable, orderByRGridTable);
108 
109 #if !wxCHECK_VERSION(2, 9, 0)
110 	// does nothing in 2.9+
111 	SetVirtualSizeHints(size);
112 #endif
113 
114 }
115 
116 
~gqbView()117 gqbView::~gqbView()
118 {
119 	if(graphBehavior)
120 		delete graphBehavior;
121 	if(iterator)
122 		delete iterator;
123 
124 	if(m_rightTables)
125 		delete m_rightTables;
126 
127 	if(m_rightJoins)
128 		delete m_rightJoins;
129 
130 	if (m_gqbPopup)
131 		delete m_gqbPopup;
132 
133 	if(orderByRGridTable)
134 		delete orderByRGridTable;
135 
136 	if(orderByLGridTable)
137 		delete orderByLGridTable;
138 }
139 
140 
141 // Overwrite and disable onEraseBackground Event to avoid Flicker
onEraseBackGround(wxEraseEvent & event)142 void gqbView::onEraseBackGround(wxEraseEvent &event)
143 {
144 }
145 
146 
147 // Detect when should be drawn the canvas with the model information
onPaint(wxPaintEvent & event)148 void gqbView::onPaint(wxPaintEvent &event)
149 {
150 	wxPaintDC dcc(this);                          // Prepare Context for Buffered Draw
151 	wxBufferedDC dc(&dcc, canvasSize);
152 	drawAll(dc, true);                            // Call Function to draw all
153 }
154 
155 
156 // GQB-TODO: remove all possible modification to model from here to controller.
onRightClick(wxMouseEvent & event)157 void gqbView::onRightClick(wxMouseEvent &event)
158 {
159 	// GQB-TODO: Validate Alias
160 	gqbObject *anySelected = NULL;
161 	wxPoint pdc = event.GetPosition();
162 	pdc.x = event.GetPosition().x;
163 	pdc.y = event.GetPosition().y;
164 	this->CalcUnscrolledPosition(pdc.x, pdc.y, &pdc.x, &pdc.y);
165 	anySelected = controller->getModelSelected(pdc, cTempSelected, jTempSelected, false);
166 	if(anySelected)
167 	{
168 		if(anySelected->getType() == GQB_QUERYOBJ)
169 		{
170 			if(!m_rightTables)
171 			{
172 				m_rightTables = new wxMenu;
173 				m_rightTables->Append(GQB_RMT_SETALIAS, _("&Set Alias for table"));
174 				m_rightTables->Append(GQB_RMT_DELETE, _("&Delete Table"));
175 				m_rightTables->AppendSeparator();
176 				m_rightTables->Append(GQB_REFRESH, _("&Refresh"));
177 
178 			}
179 			cTempSelected = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
180 			jTempSelected = NULL;
181 			PopupMenu(m_rightTables, event.GetPosition());
182 		}
183 
184 		if(anySelected->getType() == GQB_JOIN)
185 		{
186 			if(!m_rightJoins)
187 			{
188 				m_rightJoins = new wxMenu;
189 				m_rightJoins->Append(GQB_RMJ_DELETE, _("&Delete Join"));
190 				m_rightJoins->AppendSeparator();
191 				m_rightJoins->Append(GQB_REFRESH, _("&Refresh"));
192 			}
193 			cTempSelected = NULL;
194 			jTempSelected = (gqbQueryJoin *) anySelected;;
195 			PopupMenu(m_rightJoins, event.GetPosition());
196 		}
197 	}
198 	else
199 	{
200 		if(!m_gqbPopup)
201 		{
202 			m_gqbPopup = new wxMenu;
203 			m_gqbPopup->Append(GQB_REFRESH, _("&Refresh"));
204 		}
205 		PopupMenu(m_gqbPopup, event.GetPosition());
206 	}
207 }
208 
209 
OnMenuJoinDelete(wxCommandEvent & WXUNUSED (event))210 void gqbView::OnMenuJoinDelete(wxCommandEvent &WXUNUSED(event))
211 {
212 	if(jTempSelected)
213 	{
214 		this->joinsGridTable->removeJoin(jTempSelected);
215 		controller->removeJoin(jTempSelected);
216 		jTempSelected = NULL;
217 		this->Refresh();
218 	}
219 }
220 
221 
OnMenuTableDelete(wxCommandEvent & WXUNUSED (event))222 void gqbView::OnMenuTableDelete(wxCommandEvent &WXUNUSED(event))
223 {
224 	if(cTempSelected)
225 	{
226 		joinsGridTable->removeJoins(cTempSelected);
227 		controller->removeTableFromModel(cTempSelected, gridTable, orderByLGridTable, orderByRGridTable);
228 		cTempSelected = NULL;
229 		this->Refresh();
230 	}
231 }
232 
233 
OnMenuTableSetAlias(wxCommandEvent & event)234 void gqbView::OnMenuTableSetAlias(wxCommandEvent &event)
235 {
236 	if(cTempSelected)
237 	{
238 		// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
239 		// as his parent the main container of the view, and void the bug
240 		wxTextEntryDialog dialog(controller->getDialogParent(),
241 		                         wxString::Format(_("Enter an alias for table %s"), cTempSelected->getName().c_str()),
242 		                         _("Please enter an alias for the table."),
243 		                         wxT(""),
244 		                         wxOK | wxCANCEL | wxCENTRE);
245 		dialog.SetValue(cTempSelected->getAlias());
246 		if (dialog.ShowModal() == wxID_OK)
247 		{
248 			cTempSelected->setAlias(dialog.GetValue());
249 			joinsPanel->Refresh();
250 		}
251 		cTempSelected = NULL;
252 		this->Refresh();
253 	}
254 }
255 
256 
onDoubleClick(wxMouseEvent & event)257 void gqbView::onDoubleClick(wxMouseEvent &event)
258 {
259 	// GQB-TODO: Validate Alias
260 	gqbObject *anySelected = NULL;
261 	wxPoint pdc = event.GetPosition();
262 	pdc.x = event.GetPosition().x;
263 	pdc.y = event.GetPosition().y;
264 	this->CalcUnscrolledPosition(pdc.x, pdc.y, &pdc.x, &pdc.y);
265 
266 	anySelected = controller->getModelSelected(pdc, cTempSelected, jTempSelected, false);
267 	if(anySelected)
268 	{
269 		if(anySelected->getType() == GQB_QUERYOBJ)
270 		{
271 			gqbQueryObject *t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
272 
273 			// Because a bug that scrolled automatically the panel of the view if this dialog is called, then assign
274 			// as his parent the main container of the view, and void the bug
275 			wxTextEntryDialog dialog(controller->getDialogParent(),
276 			                         wxString::Format(_("Enter an alias for table %s"), t->getName().c_str()),
277 			                         _("Please enter an alias for the table."),
278 			                         wxT(""),
279 			                         wxOK | wxCANCEL | wxCENTRE);
280 			dialog.SetValue(t->getAlias());
281 			if (dialog.ShowModal() == wxID_OK)
282 			{
283 				t->setAlias(dialog.GetValue());
284 				joinsPanel->Refresh();
285 
286 				// hack to avoid misplaced joins anchors after insert an alias that trigger a table graph resize (bigger)
287 				this->Refresh();
288 				this->Update(); //force refresh
289 				graphBehavior->UpdatePosObject(t, t->position.x, t->position.y, 0);
290 			}
291 		}
292 		else if(anySelected->getType() == GQB_JOIN)
293 		{
294 			gqbQueryJoin *j = (gqbQueryJoin *) anySelected;
295 
296 			controller->getTabs()->ChangeSelection(ti_joinsPanel);
297 			gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
298 			jPanel->selectJoin(j);
299 		}
300 	}
301 	this->Refresh();
302 }
303 
304 
305 // Manages user input [Mouse click, drag & drop] over the Canvas
onMotion(wxMouseEvent & event)306 void gqbView::onMotion(wxMouseEvent &event)
307 {
308 	static int refresh = 1;                       // refresh counter, everytime this values reaches
309 	// "refreshRate" value then Refresh while dragging
310 	// Discover area where event ocurrs
311 	pos.x = event.GetPosition().x;
312 	pos.y = event.GetPosition().y;
313 	this->CalcUnscrolledPosition(pos.x, pos.y, &pos.x, &pos.y);
314 	gqbObject *anySelected = NULL;
315 
316 	// Button Down Event is triggered
317 	if(event.ButtonDown() && !changeTOpressed)
318 	{
319 		this->SetFocus();
320 
321 		// Which kind of button down was? join creation [click on any column at the
322 		// right of checkbox and drag & drop] or table moving [click on title and drag & drop]
323 		anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
324 		if(anySelected)
325 		{
326 			// Anything before just forget about it
327 			changeTOpressed = false;
328 			joinSource = NULL;
329 			joinSCol = NULL;
330 			joinDCol = NULL;
331 			joinDest = NULL;
332 			jpos.x = 0;
333 			jpos.y = 0;
334 
335 			if(anySelected->getType() == GQB_QUERYOBJ)
336 			{
337 				gqbQueryObject *t = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
338 
339 				// If click on the title area AND don't click on the columns selection checkbox
340 				if( (pos.y - t->position.y <= graphBehavior->getTitleRowHeight()))
341 					controller->setPointerMode(pt_normal);
342 				else if(pos.x - t->position.x <= 17)
343 					controller->setPointerMode(pt_normal);
344 				else
345 					controller->setPointerMode(pt_join);
346 			}
347 		}
348 		else
349 		{
350 			anySelected = NULL;
351 			mode = pt_normal;
352 		}
353 
354 		if(mode == pt_normal)                     // pointer is used to move tables & select/unselect columns
355 		{
356 			// getSelected Item [Mark it as selected if possible]
357 			anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, true);
358 			changeTOpressed = true;
359 
360 			// Do conversion of type object if any found
361 			if(anySelected)
362 			{
363 				if(anySelected->getType() == GQB_QUERYOBJ)
364 				{
365 					collectionSelected = (gqbQueryObject *) (gqbObjectCollection *) anySelected;
366 					joinSelected = NULL;
367 				}
368 				else if(anySelected->getType() == GQB_JOIN)
369 				{
370 					joinSelected = (gqbQueryJoin *) anySelected;
371 					collectionSelected = NULL;
372 				}
373 			}
374 			else
375 			{
376 				collectionSelected = NULL;
377 				joinSelected = NULL;
378 			}
379 
380 			if(!collectionSelected)
381 			{
382 				// none selected temp unselect all items
383 				controller->unsetModelSelected(true);
384 			}
385 			else
386 			{
387 				gqbColumn *col = graphBehavior->getColumnAtPosition(&pos, collectionSelected);
388 				if(col)
389 				{
390 					// Add or remove column from model & observers (ex: Grid) (projection part of SQL sentence)
391 					controller->processColumnInModel(collectionSelected, col, gridTable);
392 				}
393 			}
394 
395 			if(!joinSelected)
396 			{
397 				controller->unsetModelSelected(false);
398 			}
399 
400 		}
401 		// Pointer is used to add joins
402 		else if(mode == pt_join)
403 		{
404 			anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
405 
406 			// Even if I get an object check that it isn't a join
407 			if( (anySelected) && anySelected->getType() == GQB_QUERYOBJ)
408 				joinSource = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
409 			else
410 				joinSource = NULL;
411 
412 			if(!joinSource)
413 			{
414 				// creation of join starts
415 				joinSCol = NULL;
416 				joinDCol = NULL;
417 				jpos.x = 0;
418 				jpos.y = 0;
419 			}
420 			else
421 			{
422 				joinSCol = graphBehavior->getColumnAtPosition(&pos, joinSource, joinSource->getWidth());
423 				jpos = pos;
424 
425 				// GQB-TODO then draw line between column & pointer
426 			}
427 		}
428 
429 		this->Refresh();
430 	}
431 
432 	// Button Up Event is triggered
433 	if(event.ButtonUp())
434 	{
435 		// Pointer is used to move tables & select/unselect columns
436 		if(mode == pt_normal)
437 		{
438 			changeTOpressed = false;
439 			anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
440 			if (anySelected && anySelected->getType() == GQB_JOIN)
441 			{
442 				gqbJoinsPanel *jPanel = wxDynamicCast( joinsPanel, gqbJoinsPanel );
443 				jPanel->selectJoin((gqbQueryJoin *)anySelected);
444 			}
445 		}
446 		// Pointer is used to add joins
447 		else if(mode == pt_join)
448 		{
449 			anySelected = controller->getModelSelected(pos, collectionSelected, joinSelected, false);
450 
451 			// Even if I get an object check that it isn't a join
452 			if( (anySelected) && anySelected->getType() == GQB_QUERYOBJ)
453 			{
454 				joinDest = (gqbQueryObject *)(gqbObjectCollection *) anySelected;
455 				// Validate not self joins [in this version tables can be duplicated to create same effect]
456 				if(joinDest == joinSource)
457 				{
458 					joinDest = NULL;
459 				}
460 			}
461 			else
462 				joinDest = NULL;
463 
464 			// Creation of join starts
465 			if(!joinDest)
466 			{
467 				joinSource = NULL;
468 				joinSCol = NULL;
469 				joinDCol = NULL;
470 				joinDest = NULL;
471 				jpos.x = 0;
472 				jpos.y = 0;
473 			}
474 			else
475 			{
476 				joinDCol = graphBehavior->getColumnAtPosition(&pos, joinDest, joinDest->getWidth());
477 				if(joinDCol)
478 				{
479 					// GQB-TODO: Allow other type of joins
480 					gqbQueryJoin *qj = controller->addJoin(joinSource, joinSCol, joinDest, joinDCol, _equally);
481 					graphBehavior->calcAnchorPoint(qj);
482 					this->joinsGridTable->AppendJoin(qj);
483 				}
484 				// 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]
485 				joinSource = NULL;
486 				joinSCol = NULL;
487 				joinDest = NULL;
488 				joinDCol = NULL;
489 				jpos.x = 0;
490 				jpos.y = 0;
491 			}
492 		}
493 
494 		controller->setPointerMode(pt_normal);    //when button is up, pointer mode should be only normal
495 		this->Refresh();
496 	}
497 
498 	// Mouse is Dragged while mouse button is down
499 	if (event.Dragging() && pressed)
500 	{
501 		if(mode == pt_normal)
502 		{
503 			if(collectionSelected)
504 			{
505 				// GQB-TODO: same as gqbGraphBehavior.h [find a way to not hard code the 17 default value]
506 				if((pos.x > collectionSelected->position.x + 17) || (pos.x < collectionSelected->position.x) )
507 				{
508 					graphBehavior->UpdatePosObject(collectionSelected, pos.x, pos.y, 40);
509 				}
510 
511 				// Don't draw too much when dragging table around canvas [lower cpu use]
512 				if(refresh % refreshRate == 0)
513 				{
514 					this->Refresh();
515 					refresh = 1;
516 				}
517 				else
518 					refresh++;
519 
520 			}
521 		}
522 		else if(mode == pt_join)
523 		{
524 			if(joinSource && !joinDest)
525 			{
526 				this->Refresh();
527 			}
528 
529 		}
530 	}
531 }
532 
533 
OnKeyDown(wxKeyEvent & event)534 void gqbView::OnKeyDown(wxKeyEvent &event)
535 {
536 	if(event.GetKeyCode() == WXK_DELETE)
537 	{
538 		if(collectionSelected)
539 		{
540 			this->joinsGridTable->removeJoins(collectionSelected);
541 			controller->removeTableFromModel(collectionSelected, gridTable, orderByLGridTable, orderByRGridTable);
542 			collectionSelected = NULL;
543 			this->Refresh();
544 		}
545 
546 		if(joinSelected)
547 		{
548 			this->joinsGridTable->removeJoin(joinSelected);
549 			controller->removeJoin(joinSelected);
550 			joinSelected = NULL;
551 			this->Refresh();
552 		}
553 	}
554 }
555 
556 
drawAll(wxMemoryDC & bdc,bool adjustScrolling)557 void gqbView::drawAll(wxMemoryDC &bdc, bool adjustScrolling)
558 {
559 	bdc.Clear();
560 	if(!iterator)
561 		// Get an iterator for the objects (tables/views) in the model.
562 		iterator = this->model->createQueryIterator();
563 	else
564 		iterator->ResetIterator();
565 
566 	// First Draw Tables
567 	while(iterator->HasNext())
568 	{
569 		gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();
570 		wxPoint pt = wxPoint(tmp->position);      // Use a copy because I don't want to store the modified
571 		// version of point after CalcScrolledPosition was called
572 
573 		if (adjustScrolling)
574 		{
575 			// adjust coordinates
576 			this->CalcScrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
577 		}
578 		graphBehavior->drawTable(bdc, &pt, tmp);  // graph table
579 	}
580 
581 	// Later Draw Joins over Tables
582 	iterator->ResetIterator();
583 	while(iterator->HasNext())
584 	{
585 		gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();
586 
587 		if(tmp->getHaveJoins())
588 		{
589 			gqbIteratorBase *joinsIterator = tmp->createJoinsIterator();
590 			while(joinsIterator->HasNext())
591 			{
592 				gqbQueryJoin *join = (gqbQueryJoin *) joinsIterator->Next();
593 				wxPoint o = join->getSourceAnchor();
594 				wxPoint d = join->getDestAnchor();
595 
596 				if (adjustScrolling)
597 				{
598 					// adjust coordinates origin
599 					this->CalcScrolledPosition(o.x, o.y, &o.x, &o.y);
600 
601 					// adjust coordinates destination
602 					this->CalcScrolledPosition(d.x, d.y, &d.x, &d.y);
603 				}
604 				graphBehavior->drawJoin(bdc, o, d, join->getAnchorsUsed(), join->getSelected(), join->getKindofJoin());
605 			}
606 			delete joinsIterator;
607 		}
608 
609 	}
610 
611 	// This iterator is delete at destroyer for reuse purposes
612 	if(joinSource)
613 	{
614 		// Draw temporary line while creating a join
615 		wxPoint source = jpos;
616 		wxPoint destination = pos;
617 		if(adjustScrolling)
618 		{
619 			this->CalcScrolledPosition(source.x, source.y, &source.x, &source.y);
620 			this->CalcScrolledPosition(destination.x, destination.y, &destination.x, &destination.y);
621 		}
622 		graphBehavior->drawTempJoinLine(bdc, source, destination);
623 	}
624 }
625 
626 
setPointerMode(pointerMode pm)627 void gqbView::setPointerMode(pointerMode pm)
628 {
629 	mode = pm;
630 	if(mode == pt_join)
631 		this->SetCursor(joinCursor);
632 	else
633 		this->SetCursor(wxNullCursor);
634 }
635 
636 
clickOnJoin(gqbQueryJoin * join,wxPoint & pt,wxPoint & origin,wxPoint & dest)637 bool gqbView::clickOnJoin (gqbQueryJoin *join, wxPoint &pt, wxPoint &origin, wxPoint &dest)
638 {
639 	return graphBehavior->clickOnJoin(join, pt, origin, dest);
640 }
641 
642 
emptyPanelsData()643 void gqbView::emptyPanelsData()
644 {
645 	gridTable->emptyTableData();
646 	this->joinsGridTable->emptyTableData();
647 }
648 
649 
newTableAdded(gqbQueryObject * item)650 void gqbView::newTableAdded(gqbQueryObject *item)
651 {
652 	// Refresh Order By Panel's Left Grid
653 	if (orderByLGridTable->GetView() )
654 	{
655 		wxGridTableMessage msg( orderByLGridTable,
656 		                        wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
657 		                        orderByLGridTable->GetNumberRows() - 1,
658 		                        item->parent->countCols() );
659 		orderByLGridTable->GetView()->ProcessTableMessage( msg );
660 	}
661 }
662 
updateTable(gqbQueryObject * queryTable)663 void gqbView::updateTable(gqbQueryObject *queryTable)
664 {
665 	if (queryTable->getHaveJoins())
666 	{
667 		gqbIteratorBase *j = queryTable->createJoinsIterator();
668 		while (j->HasNext())
669 		{
670 			gqbQueryJoin *tmp = (gqbQueryJoin *)j->Next();
671 			graphBehavior->calcAnchorPoint(tmp);
672 		}
673 		delete j;
674 	}
675 
676 	// Update position of anchor points of Joins that come from others tables
677 	if (queryTable->getHaveRegJoins())
678 	{
679 		gqbIteratorBase *r = queryTable->createRegJoinsIterator();
680 		while (r->HasNext())
681 		{
682 			gqbQueryJoin *tmp = (gqbQueryJoin *)r->Next();
683 			graphBehavior->calcAnchorPoint(tmp);
684 		}
685 		delete r;
686 	}
687 	this->Refresh();
688 }
689 
690 
OnRefresh(wxCommandEvent & ev)691 void gqbView::OnRefresh(wxCommandEvent &ev)
692 {
693 	updateModelSize(NULL, true);
694 	this->Update();
695 }
696 
697 
698 /*
699 *  updateModelSize
700 *     - Update the model size.
701 *     - Calculate the maximum width and maximum height of the model
702 *  * When removed a table/view from model, the obj parameter must be null,
703 *    and update parameter should be true, otherwise update parameter should
704 *    be false (Dragging event)
705 */
updateModelSize(gqbQueryObject * obj,bool updateAnyWay)706 void gqbView::updateModelSize(gqbQueryObject *obj, bool updateAnyWay)
707 {
708 	static int callCount = 0;
709 	callCount++;
710 	if (!obj)
711 	{
712 		// Do not update model size, everytime it gets called
713 		// Update the size once in 10 times
714 		// Update the size only if update flag is true
715 		if (callCount < 10 && !updateAnyWay)
716 			return;
717 		callCount = 0;
718 		// Figure out the actual model size.
719 		// Remove table
720 		int w = 0, h = 0, maxW = 0, maxH = 0;
721 		if(!iterator)
722 			// Get an iterator for the objects (tables/views) in the model.
723 			iterator = this->model->createQueryIterator();
724 		else
725 			iterator->ResetIterator();
726 
727 		while (iterator->HasNext())
728 		{
729 			gqbQueryObject *tmp = (gqbQueryObject *)iterator->Next();;
730 			w = tmp->position.x + tmp->getWidth();
731 			h = tmp->position.y + tmp->getHeight();
732 
733 			if (maxW < w)
734 				maxW = w;
735 			if (maxH < h)
736 				maxH = h;
737 		}
738 
739 		// Reset Model size
740 		modelSize.Set(maxW, maxH);
741 	}
742 	else
743 	{
744 		int w = 0, h = 0;
745 		w = obj->position.x + obj->getWidth();
746 		h = obj->position.y + obj->getHeight();
747 
748 		if (w > modelSize.GetWidth())
749 			modelSize.SetWidth(w);
750 		if (h > modelSize.GetHeight())
751 			modelSize.SetHeight(h);
752 	}
753 	bool updateView = false;
754 	int viewW, viewH;
755 	GetSize(&viewW, &viewH);
756 
757 	if (viewW < GQB_MIN_WIDTH)
758 		viewW = GQB_MIN_WIDTH;
759 
760 	if (viewH < GQB_MIN_HEIGHT)
761 		viewH = GQB_MIN_HEIGHT;
762 
763 	if ((modelSize.GetWidth() > viewW || canvasSize.GetWidth() > viewW) &&
764 	        modelSize.GetWidth() != canvasSize.GetWidth())
765 	{
766 		canvasSize.SetWidth((modelSize.GetWidth() > viewW ? modelSize.GetWidth() : viewW));
767 		updateView = true;
768 	}
769 	if ((modelSize.GetHeight() > viewH || canvasSize.GetHeight() > viewH ) &&
770 	        modelSize.GetHeight() != canvasSize.GetHeight())
771 	{
772 		canvasSize.SetHeight((modelSize.GetHeight() > viewH ? modelSize.GetHeight() : viewH));
773 		updateView = true;
774 	}
775 
776 	if (canvasSize.GetWidth() < viewW)
777 	{
778 		canvasSize.SetWidth(viewW);
779 		updateView = true;
780 	}
781 
782 	if (canvasSize.GetHeight() < viewH)
783 	{
784 		canvasSize.SetHeight(viewH);
785 		updateView = true;
786 	}
787 
788 	if (updateView)
789 	{
790 		SetVirtualSize(canvasSize);
791 	}
792 
793 	FitInside();
794 	Refresh();
795 }
796 
OnSize(wxSizeEvent & event)797 void gqbView::OnSize(wxSizeEvent &event)
798 {
799 	updateModelSize(NULL, true);
800 }
801 
802 
canSaveAsImage()803 bool gqbView::canSaveAsImage()
804 {
805 	updateModelSize(NULL, true);
806 	return !(modelSize.GetWidth() == 0 || modelSize.GetHeight() == 0);
807 }
808 
SaveAsImage(const wxString & fileName,wxBitmapType imgType)809 void gqbView::SaveAsImage(const wxString &fileName, wxBitmapType imgType)
810 {
811 
812 	updateModelSize(NULL, true);
813 
814 	if (modelSize.GetWidth() == 0 || modelSize.GetHeight() == 0)
815 	{
816 		wxMessageBox(_("Nothing to be saved!"), _("Save As an image"), wxOK | wxICON_INFORMATION);
817 		return;
818 	}
819 
820 	int width = 0, height = 0;
821 	GetVirtualSize(&width, &height);
822 
823 	/*
824 	* Create the bitmap from the Explain window
825 	*/
826 	wxMemoryDC memDC;
827 	wxBitmap tempBitmap(width, height);
828 
829 	memDC.SelectObject(tempBitmap);
830 	memDC.Clear();
831 
832 	// Draw the diagram on the bitmap (Memory Device Context)
833 	drawAll(memDC, false);
834 
835 	memDC.SelectObject(wxNullBitmap);
836 
837 	if (!tempBitmap.SaveFile(fileName, imgType))
838 	{
839 		wxLogError(_("Could not write the file %s: Errcode=%d."), fileName.c_str(), wxSysErrorCode());
840 	}
841 }
842 
843 
844 
845