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 // ddTextTableItemFigure.cpp - Draw a column inside a table
9 //
10 ////////////////////////////////////////////////////////////////////////////
11
12 #include "pgAdmin3.h"
13
14 // wxWindows headers
15 #include <wx/wx.h>
16 #include <wx/dcbuffer.h>
17 #include <wx/numdlg.h>
18
19 // App headers
20 #include "dd/dditems/figures/ddTextTableItemFigure.h"
21 #include "dd/dditems/figures/ddRelationshipItem.h"
22 #include "dd/dditems/tools/ddColumnTextTool.h"
23 #include "dd/dditems/utilities/ddDataType.h"
24 #include "hotdraw/figures/hdSimpleTextFigure.h"
25 #include "hotdraw/main/hdDrawingView.h"
26 #include "dd/ddmodel/ddDrawingEditor.h"
27 #include "dd/ddmodel/ddDatabaseDesign.h"
28 #include "dd/dditems/figures/ddTableFigure.h"
29 #include "dd/dditems/utilities/ddPrecisionScaleDialog.h"
30 #include "hotdraw/utilities/hdRemoveDeleteDialog.h"
31
ddTextTableItemFigure(wxString & columnName,ddDataType dataType,ddColumnFigure * owner)32 ddTextTableItemFigure::ddTextTableItemFigure(wxString &columnName, ddDataType dataType, ddColumnFigure *owner):
33 hdSimpleTextFigure(columnName)
34 {
35 setKindId(DDTEXTTABLEITEMFIGURE);
36 ownerTable = NULL; //table name item is the only one case of use of this variable
37 oneTimeNoAlias = false;
38 columnType = dataType;
39 this->setEditable(true);
40 enablePopUp();
41 ownerColumn = owner;
42 showDataType = true;
43 showAlias = false;
44 recalculateDisplayBox();
45 precision = -1;
46 scale = -1;
47
48 if(owner) //is Column Object
49 {
50 fontColorAttribute->fontColor = owner->getOwnerTable()->fontColorAttribute->fontColor;
51 fontSelColorAttribute->fontColor = owner->getOwnerTable()->fontSelColorAttribute->fontColor;
52 }
53 }
54
~ddTextTableItemFigure()55 ddTextTableItemFigure::~ddTextTableItemFigure()
56 {
57 }
58
recalculateDisplayBox()59 void ddTextTableItemFigure::recalculateDisplayBox()
60 {
61 if (ownerColumn && ownerColumn->getOwnerTable())
62 {
63 hdSimpleTextFigure::recalculateDisplayBox();
64 displayBox().width = ownerColumn->getOwnerTable()->getFiguresMaxWidth();
65 }
66 }
67
displayBoxUpdate()68 void ddTextTableItemFigure::displayBoxUpdate()
69 {
70 recalculateDisplayBox();
71 }
72
setOwnerTable(ddTableFigure * table)73 void ddTextTableItemFigure::setOwnerTable(ddTableFigure *table)
74 {
75 ownerTable = table;
76 if(table)//is Table Name object
77 {
78 fontColorAttribute->fontColor = ownerTable->fontColorAttribute->fontColor;
79 fontSelColorAttribute->fontColor = ownerTable->fontSelColorAttribute->fontColor;
80 }
81 }
82
getText(bool extended)83 wxString &ddTextTableItemFigure::getText(bool extended)
84 {
85 if(showDataType && extended && getOwnerColumn())
86 {
87 wxString ddType = dataTypes()[getDataType()]; //Should use getDataType() & getPrecision(), because when column is fk, type is not taken from this column, instead from original column (source of fk)
88 bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
89 if( havePrecision && getPrecision() >= 0)
90 {
91 ddType.Truncate(ddType.Find(wxT("(")));
92 if(getScale() == -1)
93 ddType += wxString::Format(wxT("(%d)"), getPrecision());
94 else
95 ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
96 }
97 //Fix to serial is integer at automatically generated foreign key
98 if(getDataType() == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
99 ddType = dataTypes()[dt_integer];
100
101 out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" : ")) + ddType );
102 return out;
103 }
104 else if( showAlias && getOwnerColumn() == NULL )
105 {
106 if(!oneTimeNoAlias)
107 out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" ( ")) + colAlias + wxString(wxT(" ) ")) );
108 else
109 {
110 out = wxString( hdSimpleTextFigure::getText() );
111 oneTimeNoAlias = false;
112 }
113 return out;
114 }
115 else
116 {
117 return hdSimpleTextFigure::getText();
118 }
119 }
120
getType(bool raw)121 wxString ddTextTableItemFigure::getType(bool raw)
122 {
123 wxString ddType = dataTypes()[columnType];
124 if(raw)
125 return ddType;
126
127 bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
128 if( havePrecision && getPrecision() >= 0)
129 {
130 ddType.Truncate(ddType.Find(wxT("(")));
131 if(getScale() == -1)
132 ddType += wxString::Format(wxT("(%d)"), getPrecision());
133 else
134 ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
135 }
136
137 //Fix to serial is integer at automatically generated foreign key
138 if(columnType == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
139 ddType = dataTypes()[dt_integer];
140
141 return ddType;
142 }
143
144 //WARNING: event ID must match enum ddDataType!!! this event was created on view
OnGenericPopupClick(wxCommandEvent & event,hdDrawingView * view)145 void ddTextTableItemFigure::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
146 {
147 wxTextEntryDialog *nameDialog = NULL;
148 ddPrecisionScaleDialog *numericDialog = NULL;
149 wxString tmpString;
150 int answer;
151 int tmpprecision;
152 long tmpvalue;
153 hdRemoveDeleteDialog *delremDialog = NULL;
154
155 switch(event.GetId())
156 {
157 case MNU_DDADDCOLUMN:
158 nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Add a column"));
159 answer = nameDialog->ShowModal();
160 if (answer == wxID_OK)
161 {
162 tmpString = nameDialog->GetValue();
163 getOwnerColumn()->getOwnerTable()->addColumn(view->getIdx(), new ddColumnFigure(tmpString, getOwnerColumn()->getOwnerTable()));
164 view->notifyChanged();
165 }
166 delete nameDialog;
167 break;
168 case MNU_DELCOLUMN:
169 answer = wxMessageBox(wxT("Are you sure you wish to delete column ") + getText(true) + wxT("?"), wxT("Delete column?"), wxYES_NO | wxNO_DEFAULT, view);
170 if (answer == wxYES)
171 {
172 getOwnerColumn()->getOwnerTable()->removeColumn(view->getIdx(), getOwnerColumn());
173 view->notifyChanged();
174 }
175 break;
176 case MNU_AUTONAMCOLUMN:
177 getOwnerColumn()->activateGenFkName();
178 getOwnerColumn()->getFkSource()->syncAutoFkName();
179 view->notifyChanged();
180 break;
181 case MNU_RENAMECOLUMN:
182 nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Rename Column"), getText());
183 nameDialog->ShowModal();
184 if(getOwnerColumn()->isGeneratedForeignKey()) //after a manual user column rename, deactivated automatic generation of fk name.
185 getOwnerColumn()->deactivateGenFkName();
186 setText(nameDialog->GetValue());
187 delete nameDialog;
188 view->notifyChanged();
189 break;
190 case MNU_NOTNULL:
191 if(getOwnerColumn()->isNotNull())
192 getOwnerColumn()->setColumnOption(null);
193 else
194 getOwnerColumn()->setColumnOption(notnull);
195 view->notifyChanged();
196 break;
197 case MNU_PKEY:
198 if(getOwnerColumn()->isPrimaryKey())
199 {
200 getOwnerColumn()->disablePrimaryKey();
201 }
202 else
203 {
204 getOwnerColumn()->enablePrimaryKey();
205 getOwnerColumn()->setColumnOption(notnull);
206 }
207 view->notifyChanged();
208 break;
209 case MNU_UKEY:
210 getOwnerColumn()->toggleColumnKind(uk, view);
211 view->notifyChanged();
212 break;
213 case MNU_TYPESERIAL:
214 setDataType(dt_serial); //Should use setDataType always to set this value to allow fk to work flawlessly
215 recalculateDisplayBox();
216 getOwnerColumn()->displayBoxUpdate();
217 getOwnerColumn()->getOwnerTable()->updateTableSize();
218 view->notifyChanged();
219 break;
220 case MNU_TYPEBOOLEAN:
221 setDataType(dt_boolean);
222 recalculateDisplayBox();
223 getOwnerColumn()->displayBoxUpdate();
224 getOwnerColumn()->getOwnerTable()->updateTableSize();
225 view->notifyChanged();
226 break;
227 case MNU_TYPEINTEGER:
228 setDataType(dt_integer);
229 recalculateDisplayBox();
230 getOwnerColumn()->displayBoxUpdate();
231 getOwnerColumn()->getOwnerTable()->updateTableSize();
232 view->notifyChanged();
233 break;
234 case MNU_TYPEMONEY:
235 setDataType(dt_money);
236 recalculateDisplayBox();
237 getOwnerColumn()->displayBoxUpdate();
238 getOwnerColumn()->getOwnerTable()->updateTableSize();
239 view->notifyChanged();
240 break;
241 case MNU_TYPEVARCHAR:
242 setDataType(dt_varchar);
243 tmpprecision = wxGetNumberFromUser(_("Varchar size"),
244 _("Size for varchar datatype"),
245 _("Varchar size"),
246 getPrecision(), 0, 255, view);
247 if (tmpprecision >= 0)
248 {
249 setPrecision(tmpprecision);
250 setScale(-1);
251 }
252 recalculateDisplayBox();
253 getOwnerColumn()->displayBoxUpdate();
254 getOwnerColumn()->getOwnerTable()->updateTableSize();
255 view->notifyChanged();
256 break;
257 case MNU_TYPEOTHER:
258 answer = wxGetSingleChoiceIndex(wxT("New column datatype"), wxT("Column Datatypes"), dataTypes(), view);
259 if(answer >= 0)
260 {
261 view->notifyChanged();
262 if(answer == dt_varchar || answer == dt_bit || answer == dt_char || answer == dt_interval || answer == dt_varbit)
263 {
264 tmpprecision = wxGetNumberFromUser(_("datatype size"),
265 _("Size for datatype"),
266 _("size"),
267 getPrecision(), 0, 255, view);
268 if (tmpprecision >= 0)
269 {
270 setPrecision(tmpprecision);
271 setScale(-1);
272 }
273 recalculateDisplayBox();
274 getOwnerColumn()->displayBoxUpdate();
275 getOwnerColumn()->getOwnerTable()->updateTableSize();
276 }
277 if(answer == dt_numeric)
278 {
279 numericDialog = new ddPrecisionScaleDialog( view,
280 NumToStr((long)getPrecision()),
281 NumToStr((long)getScale()));
282 numericDialog->ShowModal();
283 numericDialog->GetValue1().ToLong(&tmpvalue);
284 setPrecision(tmpvalue);
285 numericDialog->GetValue2().ToLong(&tmpvalue);
286 setScale(tmpvalue);
287 delete numericDialog;
288 recalculateDisplayBox();
289 getOwnerColumn()->displayBoxUpdate();
290 getOwnerColumn()->getOwnerTable()->updateTableSize();
291 }
292
293
294 setDataType( (ddDataType) answer );
295 recalculateDisplayBox();
296 getOwnerColumn()->displayBoxUpdate();
297 getOwnerColumn()->getOwnerTable()->updateTableSize();
298 }
299 break;
300 case MNU_TYPEPKEY_CONSTRAINTNAME:
301 tmpString = wxGetTextFromUser(wxT("New name of primary key:"), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), view);
302 if(tmpString.length() > 0)
303 {
304 getOwnerColumn()->getOwnerTable()->setPkConstraintName(tmpString);
305 view->notifyChanged();
306 }
307 break;
308 case MNU_TYPEUKEY_CONSTRAINTNAME:
309 answer = wxGetSingleChoiceIndex(wxT("Select Unique Key constraint to edit name"), wxT("Select Unique Constraint to edit name:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames(), view);
310 if(answer >= 0)
311 {
312 tmpString = wxGetTextFromUser(wxT("Change name of Unique Key constraint:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), view);
313 if(tmpString.length() > 0)
314 {
315 getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer) = tmpString;
316 view->notifyChanged();
317 }
318 }
319 break;
320 case MNU_DELTABLE:
321
322 delremDialog = new hdRemoveDeleteDialog(wxT("Are you sure you wish to delete table ") + getOwnerColumn()->getOwnerTable()->getTableName() + wxT("?"), wxT("Delete table?"), view);
323 answer = delremDialog->ShowModal();
324 ddTableFigure *table = getOwnerColumn()->getOwnerTable();
325 if (answer == DD_DELETE)
326 {
327 ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
328 //Unselect table at all diagrams
329 editor->removeFromAllSelections(table);
330 //Drop foreign keys with this table as origin or destination
331 table->processDeleteAlert(view->getDrawing());
332 //Drop table
333 editor->deleteModelFigure(table);
334 editor->getDesign()->refreshBrowser();
335 view->notifyChanged();
336 }
337 else if(answer == DD_REMOVE)
338 {
339 ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
340 editor->getExistingDiagram(view->getIdx())->removeFromSelection(table);
341 editor->getExistingDiagram(view->getIdx())->remove(table);
342 view->notifyChanged();
343 }
344 delete delremDialog;
345 break;
346 }
347 }
348
createMenu(wxMenu & mnu)349 void ddTextTableItemFigure::createMenu(wxMenu &mnu)
350 {
351 wxMenu *submenu;
352 wxMenuItem *item;
353
354 mnu.Append(MNU_DDADDCOLUMN, _("Add a column..."));
355 item = mnu.Append(MNU_DELCOLUMN, _("Delete the selected column..."));
356 if(getOwnerColumn()->isGeneratedForeignKey())
357 item->Enable(false);
358 mnu.Append(MNU_RENAMECOLUMN, _("Rename the selected column..."));
359 if(getOwnerColumn()->isGeneratedForeignKey() && !getOwnerColumn()->isFkNameGenerated())
360 mnu.Append(MNU_AUTONAMCOLUMN, _("Activate fk auto-naming..."));
361 mnu.AppendSeparator();
362 item = mnu.AppendCheckItem(MNU_NOTNULL, _("Not NULL constraint"));
363 if(getOwnerColumn()->isNotNull())
364 item->Check(true);
365 if(getOwnerColumn()->isGeneratedForeignKey())
366 item->Enable(false);
367 mnu.AppendSeparator();
368 item = mnu.AppendCheckItem(MNU_PKEY, _("Primary Key"));
369 if(getOwnerColumn()->isPrimaryKey())
370 item->Check(true);
371 if(getOwnerColumn()->isGeneratedForeignKey())
372 item->Enable(false);
373 item = mnu.AppendCheckItem(MNU_UKEY, _("Unique Key"));
374 if(getOwnerColumn()->isUniqueKey())
375 item->Check(true);
376 mnu.AppendSeparator();
377 submenu = new wxMenu();
378 item = mnu.AppendSubMenu(submenu, _("Column datatype"));
379 if(getOwnerColumn()->isGeneratedForeignKey())
380 item->Enable(false);
381 item = submenu->AppendCheckItem(MNU_TYPESERIAL, _("serial"));
382 item->Check(columnType == dt_bigint);
383 item = submenu->AppendCheckItem(MNU_TYPEBOOLEAN, _("boolean"));
384 item->Check(columnType == dt_boolean);
385 item = submenu->AppendCheckItem(MNU_TYPEINTEGER, _("integer"));
386 item->Check(columnType == dt_integer);
387 item = submenu->AppendCheckItem(MNU_TYPEMONEY, _("money"));
388 item->Check(columnType == dt_money);
389 item = submenu->AppendCheckItem(MNU_TYPEVARCHAR, _("varchar(n)"));
390 item->Check(columnType == dt_varchar);
391 item = submenu->Append(MNU_TYPEOTHER, _("Choose another datatype..."));
392 mnu.AppendSeparator();
393 mnu.Append(MNU_TYPEPKEY_CONSTRAINTNAME, _("Primary Key Constraint name..."));
394 mnu.Append(MNU_TYPEUKEY_CONSTRAINTNAME, _("Unique Key Constraint name..."));
395 mnu.AppendSeparator();
396 mnu.Append(MNU_DELTABLE, _("Delete table..."));
397 };
398
399
dataTypes()400 const wxArrayString ddTextTableItemFigure::dataTypes()
401 {
402 if(ddDatatypes.IsEmpty())
403 {
404 //Fast access ddDatatypes
405 ddDatatypes.Add(wxT("ANY"));
406 ddDatatypes.Add(wxT("serial"));
407 ddDatatypes.Add(wxT("boolean"));
408 ddDatatypes.Add(wxT("integer"));
409 ddDatatypes.Add(wxT("money"));
410 ddDatatypes.Add(wxT("varchar(n)"));
411 //Normal access ddDatatypes
412 ddDatatypes.Add(wxT("bigint"));
413 ddDatatypes.Add(wxT("bit(n)"));
414 ddDatatypes.Add(wxT("bytea"));
415 ddDatatypes.Add(wxT("char(n)"));
416 ddDatatypes.Add(wxT("cidr"));
417 ddDatatypes.Add(wxT("circle"));
418 ddDatatypes.Add(wxT("date"));
419 ddDatatypes.Add(wxT("double precision"));
420 ddDatatypes.Add(wxT("inet"));
421 ddDatatypes.Add(wxT("interval(n)"));
422 ddDatatypes.Add(wxT("line"));
423 ddDatatypes.Add(wxT("lseg"));
424 ddDatatypes.Add(wxT("macaddr"));
425 ddDatatypes.Add(wxT("numeric(p,s)"));
426 ddDatatypes.Add(wxT("path"));
427 ddDatatypes.Add(wxT("point"));
428 ddDatatypes.Add(wxT("polygon"));
429 ddDatatypes.Add(wxT("real"));
430 ddDatatypes.Add(wxT("smallint"));
431 ddDatatypes.Add(wxT("text"));
432 ddDatatypes.Add(wxT("time"));
433 ddDatatypes.Add(wxT("timestamp"));
434 ddDatatypes.Add(wxT("varbit(n)"));
435 }
436 return ddDatatypes;
437 }
438
setText(wxString textString)439 void ddTextTableItemFigure::setText(wxString textString)
440 {
441 hdSimpleTextFigure::setText(textString);
442
443 //Hack to allow column text to submit new size of text signal to tablefigure
444 //and then recalculate displaybox. Helps with fk autorenaming too.
445 if(ownerColumn)
446 {
447 ownerColumn->displayBoxUpdate();
448 ownerColumn->getOwnerTable()->updateTableSize();
449 ownerColumn->getOwnerTable()->updateFkObservers();
450 }
451 }
452
getAlias()453 wxString ddTextTableItemFigure::getAlias()
454 {
455 return colAlias;
456 }
457
458 //Activate use of alias or short names at ddtextTableItems like TableNames [Columns don' use it]
setAlias(wxString alias)459 void ddTextTableItemFigure::setAlias(wxString alias)
460 {
461 if(alias.length() <= 0 || alias.length() > 3 )
462 {
463 showAlias = false;
464 colAlias = wxEmptyString;
465 }
466 else
467 {
468 showAlias = true;
469 colAlias = alias;
470 }
471 recalculateDisplayBox();
472 ownerTable->updateFkObservers(); //Only triggered by a tableName item [Not a column]
473 ownerTable->updateTableSize();
474 }
475
setOneTimeNoAlias()476 void ddTextTableItemFigure::setOneTimeNoAlias()
477 {
478 oneTimeNoAlias = true;
479 }
480
getOwnerColumn()481 ddColumnFigure *ddTextTableItemFigure::getOwnerColumn()
482 {
483 return ownerColumn;
484 }
485
setOwnerColumn(ddColumnFigure * column)486 void ddTextTableItemFigure::setOwnerColumn(ddColumnFigure *column)
487 {
488 ownerColumn = column;
489 }
490
setShowDataType(bool value)491 void ddTextTableItemFigure::setShowDataType(bool value)
492 {
493 showDataType = value;
494 }
495
CreateFigureTool(hdDrawingView * view,hdITool * defaultTool)496 hdITool *ddTextTableItemFigure::CreateFigureTool(hdDrawingView *view, hdITool *defaultTool)
497 {
498 if(getOwnerColumn())
499 {
500 return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Column Name"), wxT("Rename Column")) : defaultTool;
501 }
502 else
503 {
504 setOneTimeNoAlias();
505 return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Table Name"), wxT("Rename Table")) : defaultTool;
506 }
507 }
508
getTextWidth()509 int ddTextTableItemFigure::getTextWidth()
510 {
511 int w, h;
512 getFontMetrics(w, h);
513 return w;
514 }
515
getTextHeight()516 int ddTextTableItemFigure::getTextHeight()
517 {
518 int w, h;
519 getFontMetrics(w, h);
520 return h;
521 }
522
getDataType()523 ddDataType ddTextTableItemFigure::getDataType()
524 {
525 if(!getOwnerColumn()->isGeneratedForeignKey())
526 return columnType;
527 else
528 {
529 columnType = getOwnerColumn()->getFkSource()->original->getDataType();
530 return columnType;
531 }
532 }
533
setDataType(ddDataType type)534 void ddTextTableItemFigure::setDataType(ddDataType type)
535 {
536 columnType = type;
537 ownerColumn->getOwnerTable()->updateSizeOfObservers();
538 }
539
getPrecision()540 int ddTextTableItemFigure::getPrecision()
541 {
542 if(getOwnerColumn()->isGeneratedForeignKey())
543 {
544 precision = getOwnerColumn()->getFkSource()->original->getPrecision();
545 return precision;
546 }
547 else
548 {
549 return precision;
550 }
551 }
552
setPrecision(int value)553 void ddTextTableItemFigure::setPrecision(int value)
554 {
555 if(!getOwnerColumn()->isGeneratedForeignKey())
556 {
557 precision = value;
558 ownerColumn->getOwnerTable()->updateSizeOfObservers();
559 }
560 }
561
setScale(int value)562 void ddTextTableItemFigure::setScale(int value)
563 {
564 if(!getOwnerColumn()->isGeneratedForeignKey())
565 {
566 scale = value;
567 ownerColumn->getOwnerTable()->updateSizeOfObservers();
568 }
569 }
570
getScale()571 int ddTextTableItemFigure::getScale()
572 {
573 if(getOwnerColumn()->isGeneratedForeignKey())
574 {
575 scale = getOwnerColumn()->getFkSource()->original->getScale();
576 return scale;
577 }
578 else
579 {
580 return scale;
581 }
582 }
583