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