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 // frmHint.cpp - PostgreSQL Guru hints
9 //
10 //////////////////////////////////////////////////////////////////////////
11
12
13 #include "pgAdmin3.h"
14
15 // wxWindows headers
16 #include <wx/wx.h>
17 #include <wx/image.h>
18 #include <wx/file.h>
19 #include "wx/wxhtml.h"
20 #include <wx/tipdlg.h>
21
22 // App headers
23 #include "copyright.h"
24 #include "ctl/ctlMenuToolbar.h"
25 #include "frm/frmHint.h"
26 #include "frm/frmMain.h"
27 #include "schema/pgObject.h"
28 #include "utils/misc.h"
29 #include "utils/utffile.h"
30
31 // Icons
32 #include "images/hint.pngc"
33 #include "images/hint2.pngc"
34
35
36
37 #define HINT_CANSUPPRESS 1
38 #define HINT_CANABORT 2
39 #define HINT_CANFIX 4
40 #define HINT_YESNO 8
41
42
43 struct make_dumb_compilers_happy // unnamed structs don't make all compilers happy...
44 {
45 const wxChar *hintPage;
46 const wxChar *hintCaption;
47 const wxChar *fixText;
48 const wxChar *helpItem;
49 int flags;
50 }
51 hintArray[] =
52 {
53 {
54 HINT_CONNECTSERVER,
55 __("Server not listening"),
56 0,
57 wxT("pg/runtime-config#runtime-config-connection"),
58 HINT_CANSUPPRESS
59 },
60 {
61 HINT_MISSINGHBA,
62 __("Server denies access"),
63 0,
64 wxT("pg/client-authentication#auth-pg-hba-conf"),
65 HINT_CANSUPPRESS
66 },
67 {
68 HINT_MISSINGIDENT,
69 __("Ident authentication failed"),
70 0,
71 wxT("pg/auth-methods#auth-ident"),
72 HINT_CANSUPPRESS
73 },
74 {
75 HINT_PRIMARYKEY,
76 __("Creation of primary key suggested"),
77 0,
78 wxT("pg/ddl-constraints"),
79 HINT_CANSUPPRESS | HINT_CANABORT
80 },
81 {
82 HINT_FKINDEX,
83 __("Creation of index in referencing table suggested"),
84 0,
85 wxT("pg/ddl-constraints#ddl-constraints-fk"),
86 HINT_CANSUPPRESS | HINT_CANABORT
87 },
88 {
89 HINT_VACUUM,
90 __("Running VACUUM recommended"),
91 __("VACUUM"),
92 wxT("pg/maintenance#routine-vacuuming"),
93 HINT_CANSUPPRESS | HINT_CANFIX
94 },
95 {
96 HINT_VACUUM_FULL,
97 __("Running VACUUM FULL not recommended"),
98 __("VACUUM FULL"),
99 wxT("pg/sql-vacuum"),
100 HINT_CANSUPPRESS | HINT_CANABORT
101 },
102 {
103 HINT_QUERYRUNTIME,
104 __("Query took a long time to complete"),
105 0,
106 wxT("query"),
107 HINT_CANSUPPRESS | HINT_CANABORT | HINT_YESNO
108 },
109 {
110 HINT_INSTRUMENTATION,
111 __("Server instrumentation not installed"),
112 0,
113 wxEmptyString,
114 HINT_CANSUPPRESS
115 },
116 {
117 HINT_INSTRUMENTATION_91_WITH,
118 __("Server instrumentation not installed"),
119 0,
120 wxEmptyString,
121 HINT_CANSUPPRESS | HINT_CANFIX
122 },
123 {
124 HINT_INSTRUMENTATION_91_WITHOUT,
125 __("Server instrumentation not installed"),
126 0,
127 wxEmptyString,
128 HINT_CANSUPPRESS
129 },
130 {
131 HINT_ENCODING_UNICODE,
132 __("Database encoding is Unicode"),
133 0,
134 wxT("pg/multibyte"),
135 HINT_CANSUPPRESS
136 },
137 {
138 HINT_ENCODING_ASCII,
139 __("Database encoding is SQL_ASCII"),
140 0,
141 wxT("pg/multibyte"),
142 HINT_CANSUPPRESS
143 },
144 {
145 HINT_READONLY_NOPK,
146 __("Can't edit tables without primary key"),
147 0,
148 wxT("editgrid"),
149 HINT_CANSUPPRESS
150 },
151 {
152 HINT_AUTOVACUUM,
153 _("Enabling autovacuum recommended"),
154 0,
155 wxT("pg/maintenance#autovacuum"),
156 HINT_CANSUPPRESS
157 },
158 {
159 HINT_OBJECT_EDITING,
160 _("Editing views, stored procedures or functions"),
161 0,
162 wxT("query"),
163 HINT_CANSUPPRESS | HINT_CANABORT
164 },
165 {
166 HINT_SAVING_PASSWORDS,
167 _("Saving passwords"),
168 0,
169 wxT("pg/libpq-pgpass"),
170 HINT_CANSUPPRESS | HINT_CANABORT
171 },
172 { 0, 0, 0, 0 }
173 };
174
175
176 BEGIN_EVENT_TABLE(frmHint, DialogWithHelp)
177 EVT_BUTTON(XRCID("btnFix"), frmHint::OnFix)
178 END_EVENT_TABLE();
179
180
181 #define chkSuppress CTRL_CHECKBOX("chkSuppress")
182 #define htmlHint (XRCCTRL(*this, "htmlHint", wxHtmlWindow))
183 #define btnFix CTRL_BUTTON("btnFix")
184 #define btnYes CTRL_BUTTON("wxID_YES")
185 #define btnNo CTRL_BUTTON("wxID_NO")
186 #define btnHelp CTRL_BUTTON("wxID_HELP")
187
frmHint(wxWindow * fr,bool _force)188 frmHint::frmHint(wxWindow *fr, bool _force) : DialogWithHelp(0)
189 {
190 force = _force;
191 SetFont(settings->GetSystemFont());
192 LoadResource(fr, wxT("frmHint"));
193 RestorePosition();
194
195 if (force)
196 btnCancel->Disable();
197
198 SetIcon(*hint_png_ico);
199 }
200
201
202
GetPage(const wxChar * hintPage)203 wxString frmHint::GetPage(const wxChar *hintPage)
204 {
205 wxString page;
206
207 wxString cn = settings->GetCanonicalLanguageName();
208 if (cn.IsEmpty())
209 cn = wxT("en_US");
210
211 wxString filename = docPath + wxT("/") + cn + wxT("/hints/") + hintPage + wxT(".html");
212
213 if (!wxFile::Exists(filename))
214 filename = docPath + wxT("/en_US/hints/") + hintPage + wxT(".html");
215 if (wxFile::Exists(filename))
216 {
217 wxUtfFile file(filename, wxFile::read, wxFONTENCODING_UTF8);
218 file.Read(page);
219 }
220
221 return page;
222 }
223
224
SetHint(int hintno,const wxString & info)225 void frmHint::SetHint(int hintno, const wxString &info)
226 {
227 hintnos.Add(hintno);
228 SetHint(info);
229 };
230
231
SetHint(const wxArrayInt & _hintnos,const wxString & info)232 void frmHint::SetHint(const wxArrayInt &_hintnos, const wxString &info)
233 {
234 hintnos = _hintnos;
235 SetHint(info);
236 }
237
238
SetHint(const wxString & info)239 void frmHint::SetHint(const wxString &info)
240 {
241 currentHint = hintnos.Item(0);
242
243 bool canSuppress = false;
244 if (hintnos.GetCount() == 1)
245 {
246 if (hintArray[currentHint].flags & HINT_CANSUPPRESS)
247 canSuppress = true;
248 SetTitle(_("Guru Hint") + wxString(wxT(" - ")) + wxGetTranslation(hintArray[currentHint].hintCaption));
249
250 wxString page = GetPage(hintArray[currentHint].hintPage);
251 page.Replace(wxT("<INFO>"), HtmlEntities(info));
252
253 htmlHint->SetPage(page);
254 }
255 else
256 {
257 SetTitle(_("Guru Hints"));
258
259 wxString header = GetPage(wxT("multiple"));
260 wxString pages;
261
262 if (header.IsEmpty())
263 {
264 header =
265 wxT("<html><head>\n")
266 wxT("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">\n")
267 wxT("<link rel=\"STYLESHEET\" type=\"text/css\" href=\"../pgadmin3.css\">\n")
268 wxT("<title>Guru Hints</title></head>\n")
269 wxT("<body><p>\n");
270 }
271 else
272 {
273 int o = header.Find(wxT("</body>"));
274 if (o > 0)
275 header = header.Left(o) + wxT("<p>");
276 }
277
278 size_t i;
279 for (i = 0 ; i < hintnos.GetCount() ; i++)
280 {
281 int hintno = hintnos.Item(i);
282 if (hintArray[hintno].flags & HINT_CANSUPPRESS)
283 canSuppress = true;
284 wxString page = GetPage(hintArray[hintno].hintPage);
285 int a = page.Find(wxT("<body>"));
286 int o = page.Find(wxT("</body>"));
287 if (a < 0)
288 a = 0;
289 if (o < 0)
290 o = wxString::npos;
291
292
293 int ha = page.Find(wxT("<H3>"));
294 int ho = page.Find(wxT("</H3>"));
295 if (ha < 0)
296 ha = page.Find(wxT("<h3>"));
297 if (ho < 0)
298 ho = page.Find(wxT("</h3>"));
299
300 if (ha > a && ho > ha)
301 {
302 wxString hintTitle = page.Mid(ha + 4, ho - ha - 4);
303
304 pages += page.Mid(a, ha - a)
305 + wxT("<H3><A Name=\"") + hintArray[hintno].hintPage + wxT("\">")
306 + hintTitle + wxT("</A>")
307 + page.Mid(ho, o - ho);
308 header += wxString(wxT("<A HREF=\"#")) + hintArray[hintno].hintPage + wxT("\">")
309 + hintTitle + wxT("</A><BR>");
310 }
311 else
312 pages += page.Mid(a, o - a);
313 }
314
315 pages.Replace(wxT("<INFO>"), info);
316 pages.Replace(wxT("<INFO/>"), info);
317
318 htmlHint->SetPage(header + wxT("</p>") + pages + wxT("</body></html>\n"));
319 }
320
321 chkSuppress->SetValue(false);
322 chkSuppress->Enable(!force && canSuppress);
323
324 if (force || !(hintArray[currentHint].flags & HINT_CANABORT))
325 btnCancel->Disable();
326 if (!(hintArray[currentHint].flags & HINT_CANFIX))
327 btnFix->Hide();
328
329 if (!(hintArray[currentHint].flags & HINT_CANFIX))
330 btnFix->Hide();
331 else if (hintArray[currentHint].fixText)
332 btnFix->SetLabel(wxGetTranslation(hintArray[currentHint].fixText));
333
334 if (hintArray[currentHint].flags & HINT_YESNO)
335 btnOK->Hide();
336 else
337 {
338 btnYes->Hide();
339 btnNo->Hide();
340 }
341
342 if (hintArray[currentHint].helpItem == wxEmptyString)
343 btnHelp->Disable();
344 else
345 btnHelp->Enable();
346 };
347
348
~frmHint()349 frmHint::~frmHint()
350 {
351 if (!force && chkSuppress->GetValue())
352 {
353 size_t i;
354 for (i = 0 ; i < hintnos.GetCount() ; i++)
355 {
356 int hintno = hintnos.Item(i);
357 if (hintArray[hintno].flags & HINT_CANSUPPRESS)
358 settings->Write(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxT("Suppress"));
359 }
360 }
361 SavePosition();
362 }
363
364
GetHelpPage() const365 wxString frmHint::GetHelpPage() const
366 {
367 const wxChar *helpItem = hintArray[currentHint].helpItem;
368 if (helpItem)
369 return helpItem;
370
371 return wxT("guruhints");
372 }
373
374
ResetHints()375 void frmHint::ResetHints()
376 {
377 int hintno = 0;
378 while (hintArray[hintno].hintPage)
379 {
380 settings->Write(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxString(wxEmptyString));
381 hintno++;
382 }
383 }
384
385
GetHintNo(const wxString & hint)386 int frmHint::GetHintNo(const wxString &hint)
387 {
388 int hintno = 0;
389 while (hintArray[hintno].hintPage)
390 {
391 if (hintArray[hintno].hintPage == hint)
392 return hintno;
393 hintno++;
394 }
395 return -1;
396 }
397
398
WantHint(const wxString & hint)399 bool frmHint::WantHint(const wxString &hint)
400 {
401 int hintno = GetHintNo(hint);
402 if (hintno < 0)
403 return false;
404
405 return WantHint(hintno);
406 }
407
408
WantHint(int hintno)409 bool frmHint::WantHint(int hintno)
410 {
411 if (hintno < 0)
412 return false;
413
414 if ((hintArray[hintno].flags & HINT_CANSUPPRESS) && settings->GetSuppressGuruHints())
415 return false;
416
417 if (settings->Read(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxEmptyString) != wxT("Suppress"))
418 return true;
419
420 return false;
421 }
422
423
424
ShowHint(wxWindow * fr,const wxArrayString & hints,const wxString & info,bool force)425 int frmHint::ShowHint(wxWindow *fr, const wxArrayString &hints, const wxString &info, bool force)
426 {
427 wxArrayInt hintnos;
428 size_t i;
429
430 if(!hints.GetCount())
431 return wxID_OK;
432 for (i = 0 ; i < hints.GetCount() ; i++)
433 {
434 int hintNo = GetHintNo(hints.Item(i));
435 if (hintNo >= 0 && (force || WantHint(hintNo)))
436 hintnos.Add(hintNo);
437 }
438
439 if (!hintnos.GetCount())
440 return wxID_OK;
441
442 frmHint *frm = new frmHint(fr, force);
443 frm->SetHint(hintnos, info);
444
445 frm->CenterOnParent();
446 int rc = frm->ShowModal();
447 delete frm;
448
449 if ((rc == wxID_CANCEL || rc == -1))
450 rc = wxID_OK;
451
452 return rc;
453 }
454
455
ShowHint(wxWindow * fr,const wxString & hint,const wxString & info,bool force)456 int frmHint::ShowHint(wxWindow *fr, const wxString &hint, const wxString &info, bool force)
457 {
458 int rc = wxID_OK;
459 int hintno = GetHintNo(hint);
460
461 if (WantHint(hintno))
462 {
463 frmHint *frm = new frmHint(fr, force);
464 frm->SetHint(hintno, info);
465
466 frm->CenterOnParent();
467 rc = frm->ShowModal();
468 delete frm;
469
470 if ((rc == wxID_CANCEL || rc == -1) && !(hintArray[hintno].flags & HINT_CANABORT))
471 rc = wxID_OK;
472 }
473 return rc;
474 }
475
476
OnFix(wxCommandEvent & ev)477 void frmHint::OnFix(wxCommandEvent &ev)
478 {
479 EndModal(HINT_RC_FIX);
480 }
481
482
483
hintFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar,bool bigTool)484 hintFactory::hintFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar, bool bigTool) : actionFactory(list)
485 {
486 mnu->Append(id, _("Hints"), _("Display helpful hints on current object."));
487 if (toolbar)
488 {
489 const wxBitmap &bmp = (bigTool ? *hint_png_bmp : *hint2_png_bmp);
490 toolbar->AddTool(id, wxEmptyString, bmp, _("Display helpful hints on current object."));
491 }
492 }
493
494
StartDialog(frmMain * form,pgObject * obj)495 wxWindow *hintFactory::StartDialog(frmMain *form, pgObject *obj)
496 {
497 if (obj)
498 obj->ShowHint(form, true);
499 else
500 wxLogMessage(_("There are no hints available for the current object."));
501
502 return 0;
503 }
504
505
CheckEnable(pgObject * obj)506 bool hintFactory::CheckEnable(pgObject *obj)
507 {
508 return obj && obj->GetCanHint();
509 }
510
511