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 // ctlSQLBox.cpp - SQL syntax highlighting textbox
9 //
10 //////////////////////////////////////////////////////////////////////////
11
12 #include "pgAdmin3.h"
13
14 // wxWindows headers
15 #include <wx/wx.h>
16 #include <wx/stc/stc.h>
17 #include <wx/sysopt.h>
18
19 // App headers
20 #include "db/pgSet.h"
21 #include "ctl/ctlSQLBox.h"
22 #include "dlg/dlgFindReplace.h"
23 #include "frm/menu.h"
24 #include "utils/sysProcess.h"
25
26 wxString ctlSQLBox::sqlKeywords;
27
28 // Additional pl/pgsql keywords we should highlight
29 wxString plpgsqlKeywords = wxT(" elsif exception exit loop raise record return text while");
30 //
31 // Additional Text Search keywords we should highlight
32 wxString ftsKeywords = wxT(" gettoken lextypes headline init lexize");
33
34 // Additional pgScript keywords we should highlight
35 wxString pgscriptKeywords = wxT(" assert break columns continue date datetime file go lines ")
36 wxT(" log print record reference regexrmline string waitfor while");
37
BEGIN_EVENT_TABLE(ctlSQLBox,wxStyledTextCtrl)38 BEGIN_EVENT_TABLE(ctlSQLBox, wxStyledTextCtrl)
39 EVT_KEY_DOWN(ctlSQLBox::OnKeyDown)
40 EVT_MENU(MNU_FIND, ctlSQLBox::OnSearchReplace)
41 EVT_MENU(MNU_AUTOCOMPLETE, ctlSQLBox::OnAutoComplete)
42 EVT_KILL_FOCUS(ctlSQLBox::OnKillFocus)
43 #ifdef __WXMAC__
44 EVT_STC_PAINTED(-1, ctlSQLBox::OnPositionStc)
45 #else
46 EVT_STC_UPDATEUI(-1, ctlSQLBox::OnPositionStc)
47 #endif
48 EVT_STC_MARGINCLICK(-1, ctlSQLBox::OnMarginClick)
49 EVT_END_PROCESS(-1, ctlSQLBox::OnEndProcess)
50 END_EVENT_TABLE()
51
52
53 IMPLEMENT_DYNAMIC_CLASS(ctlSQLBox, wxStyledTextCtrl)
54
55
56 ctlSQLBox::ctlSQLBox()
57 {
58 m_dlgFindReplace = 0;
59 m_autoIndent = false;
60 m_autocompDisabled = false;
61 process = 0;
62 processID = 0;
63 }
64
65
ctlSQLBox(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)66 ctlSQLBox::ctlSQLBox(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
67 {
68 m_dlgFindReplace = 0;
69
70 m_database = NULL;
71
72 m_autocompDisabled = false;
73 process = 0;
74 processID = 0;
75
76 Create(parent, id, pos, size, style);
77 }
78
79
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)80 void ctlSQLBox::Create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
81 {
82 wxStyledTextCtrl::Create(parent, id , pos, size, style);
83
84 // Clear all styles
85 StyleClearAll();
86
87 // Font
88 extern sysSettings *settings;
89 wxFont fntSQLBox = settings->GetSQLFont();
90
91 wxColour bgColor = settings->GetSQLBoxColourBackground();
92 if (settings->GetSQLBoxUseSystemBackground())
93 {
94 bgColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
95 }
96
97 wxColour frColor = settings->GetSQLBoxColourForeground();
98 if (settings->GetSQLBoxUseSystemForeground())
99 {
100 frColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
101 }
102 StyleSetBackground(wxSTC_STYLE_DEFAULT, bgColor);
103 StyleSetForeground(wxSTC_STYLE_DEFAULT, frColor);
104 StyleSetFont(wxSTC_STYLE_DEFAULT, fntSQLBox);
105
106 SetSelBackground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
107 SetSelForeground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
108
109 SetCaretForeground(settings->GetSQLColourCaret());
110
111 SetMarginWidth(1, 0);
112 SetTabWidth(settings->GetIndentSpaces());
113 SetUseTabs(!settings->GetSpacesForTabs());
114
115 // Setup the different highlight colurs
116 for (int i = 0; i < 34; ++ i )
117 {
118 if (i > 0 && i < 12)
119 StyleSetForeground(i, settings->GetSQLBoxColour(i));
120 else
121 StyleSetForeground(i, frColor);
122 StyleSetBackground(i, bgColor);
123 StyleSetFont(i, fntSQLBox);
124 }
125
126 // Keywords in uppercase?
127
128 if (settings->GetSQLKeywordsInUppercase())
129 StyleSetCase(5, wxSTC_CASE_UPPER);
130
131 // Margin style
132 StyleSetBackground(wxSTC_STYLE_LINENUMBER, settings->GetSQLMarginBackgroundColour());
133
134 // Brace maching styles
135 StyleSetBackground(34, wxColour(0x99, 0xF9, 0xFF));
136 StyleSetBackground(35, wxColour(0xFF, 0xCF, 0x27));
137 StyleSetFont(34, fntSQLBox);
138 StyleSetFont(35, fntSQLBox);
139
140 // SQL Lexer and keywords.
141 if (sqlKeywords.IsEmpty())
142 FillKeywords(sqlKeywords);
143 SetLexer(wxSTC_LEX_SQL);
144 SetKeyWords(0, sqlKeywords + plpgsqlKeywords + ftsKeywords + pgscriptKeywords);
145
146 // Enable folding
147 SetMarginSensitive(2, true);
148
149 SetMarginType(2, wxSTC_MARGIN_SYMBOL); // margin 2 for symbols
150 SetMarginMask(2, wxSTC_MASK_FOLDERS); // set up mask for folding symbols
151 SetMarginSensitive(2, true); // this one needs to be mouse-aware
152 SetMarginWidth(2, 16); // set margin 2 16 px wide
153
154 MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED, *wxWHITE, *wxBLACK);
155 MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED, *wxWHITE, *wxBLACK);
156 MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER, *wxWHITE, *wxBLACK);
157 MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER, *wxWHITE, *wxBLACK);
158 MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE, *wxWHITE, *wxBLACK);
159 MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS, *wxWHITE, *wxBLACK);
160 MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS, *wxWHITE, *wxBLACK);
161
162 SetProperty(wxT("fold"), wxT("1"));
163 SetFoldFlags(16);
164
165 // Setup accelerators
166 wxAcceleratorEntry entries[2];
167 entries[0].Set(wxACCEL_CTRL, (int)'F', MNU_FIND);
168 entries[1].Set(wxACCEL_CTRL, WXK_SPACE, MNU_AUTOCOMPLETE);
169 wxAcceleratorTable accel(2, entries);
170 SetAcceleratorTable(accel);
171
172 // Autocompletion configuration
173 AutoCompSetSeparator('\t');
174 AutoCompSetChooseSingle(true);
175 AutoCompSetIgnoreCase(true);
176 AutoCompSetFillUps(wxT(" \t"));
177 AutoCompSetDropRestOfWord(true);
178
179 SetEOLMode(settings->GetLineEndingType());
180 }
181
SetDatabase(pgConn * db)182 void ctlSQLBox::SetDatabase(pgConn *db)
183 {
184 m_database = db;
185 }
186
OnSearchReplace(wxCommandEvent & ev)187 void ctlSQLBox::OnSearchReplace(wxCommandEvent &ev)
188 {
189 if (!m_dlgFindReplace)
190 {
191 m_dlgFindReplace = new dlgFindReplace(this);
192 m_dlgFindReplace->Show(true);
193 }
194 else
195 {
196 m_dlgFindReplace->Show(true);
197 m_dlgFindReplace->SetFocus();
198 }
199
200 wxString selText = GetSelectedText();
201 if (!selText.IsEmpty())
202 {
203 m_dlgFindReplace->SetFindString(selText);
204 }
205
206 m_dlgFindReplace->FocusSearch();
207 }
208
Find(const wxString & find,bool wholeWord,bool matchCase,bool useRegexps,bool startAtTop,bool reverse)209 bool ctlSQLBox::Find(const wxString &find, bool wholeWord, bool matchCase, bool useRegexps, bool startAtTop, bool reverse)
210 {
211 if (!DoFind(find, wxString(wxEmptyString), false, wholeWord, matchCase, useRegexps, startAtTop, reverse))
212 {
213 wxWindow *w = wxWindow::FindFocus();
214 wxMessageBox(_("Reached the end of the document"), _("Find text"), wxICON_EXCLAMATION | wxOK, w);
215 return false;
216 }
217 return true;
218 }
219
Replace(const wxString & find,const wxString & replace,bool wholeWord,bool matchCase,bool useRegexps,bool startAtTop,bool reverse)220 bool ctlSQLBox::Replace(const wxString &find, const wxString &replace, bool wholeWord, bool matchCase, bool useRegexps, bool startAtTop, bool reverse)
221 {
222 if (!DoFind(find, replace, true, wholeWord, matchCase, useRegexps, startAtTop, reverse))
223 {
224 wxWindow *w = wxWindow::FindFocus();
225 wxMessageBox(_("Reached the end of the document"), _("Replace text"), wxICON_EXCLAMATION | wxOK, w);
226 return false;
227 }
228 return true;
229 }
230
ReplaceAll(const wxString & find,const wxString & replace,bool wholeWord,bool matchCase,bool useRegexps)231 bool ctlSQLBox::ReplaceAll(const wxString &find, const wxString &replace, bool wholeWord, bool matchCase, bool useRegexps)
232 {
233 // Use DoFind to repeatedly replace text
234 int count = 0;
235 int initialPos = GetCurrentPos();
236 GotoPos(0);
237
238 while(DoFind(find, replace, true, wholeWord, matchCase, useRegexps, false, false))
239 count++;
240
241 GotoPos(initialPos);
242
243 wxString msg;
244 msg.Printf(wxPLURAL("%d replacement made.", "%d replacements made.", count), count);
245 wxMessageBox(msg, _("Replace all"), wxOK | wxICON_INFORMATION);
246
247 if (count)
248 return true;
249 else
250 return false;
251 }
252
DoFind(const wxString & find,const wxString & replace,bool doReplace,bool wholeWord,bool matchCase,bool useRegexps,bool startAtTop,bool reverse)253 bool ctlSQLBox::DoFind(const wxString &find, const wxString &replace, bool doReplace, bool wholeWord, bool matchCase, bool useRegexps, bool startAtTop, bool reverse)
254 {
255 int flags = 0;
256 int startPos = GetSelectionStart();
257 int endPos = GetTextLength();
258
259 // Setup flags
260 if (wholeWord)
261 flags |= wxSTC_FIND_WHOLEWORD;
262
263 if (matchCase)
264 flags |= wxSTC_FIND_MATCHCASE;
265
266 // Replace the current selection, if there is one and it matches the find param.
267 wxString current = GetSelectedText();
268 if (doReplace)
269 {
270 if (useRegexps)
271 {
272 CharacterRange cr = RegexFindText(GetSelectionStart(), GetSelectionEnd(), find);
273 if (GetSelectionStart() == cr.cpMin && GetSelectionEnd() == cr.cpMax)
274 {
275 if (cr.cpMin == cr.cpMax) // Must be finding a special char, such as $ (line end)
276 {
277 InsertText(cr.cpMax, replace);
278 SetSelection(cr.cpMax, cr.cpMax + replace.Length());
279 SetCurrentPos(cr.cpMax + replace.Length());
280
281 // Stop if we've got to the end. This is important for the $
282 // case where it'll just keep finding the end of the line!!
283 if ((int)(cr.cpMin + replace.Length()) == GetLength())
284 return false;
285 }
286 else
287 {
288 ReplaceSelection(replace);
289 SetSelection(startPos, startPos + replace.Length());
290 SetCurrentPos(startPos + replace.Length());
291 }
292 }
293 }
294 else if ((matchCase && current == find) || (!matchCase && current.Upper() == find.Upper()))
295 {
296 ReplaceSelection(replace);
297 if (!reverse)
298 {
299 SetSelection(startPos, startPos + replace.Length());
300 SetCurrentPos(startPos + replace.Length());
301 }
302 else
303 {
304 SetSelection(startPos + replace.Length(), startPos);
305 SetCurrentPos(startPos);
306 }
307 }
308 }
309
310 ////////////////////////////////////////////////////////////////////////
311 // Figure out the starting position for the next search
312 ////////////////////////////////////////////////////////////////////////
313
314 if (startAtTop)
315 {
316 startPos = 0;
317 endPos = GetTextLength();
318 }
319 else
320 {
321 if (reverse)
322 {
323 endPos = 0;
324 startPos = GetCurrentPos();
325 }
326 else
327 {
328 endPos = GetTextLength();
329 startPos = GetCurrentPos();
330 }
331 }
332
333 size_t selStart = 0, selEnd = 0;
334
335 if (useRegexps)
336 {
337 CharacterRange cr = RegexFindText(startPos, endPos, find);
338 selStart = cr.cpMin;
339 selEnd = cr.cpMax;
340 }
341 else
342 {
343 selStart = FindText(startPos, endPos, find, flags);
344 selEnd = selStart + find.Length();
345 }
346
347 if (selStart != (size_t)(-1))
348 {
349 if (reverse)
350 {
351 SetCurrentPos(selStart);
352 SetSelection(selEnd, selStart);
353 }
354 else
355 {
356 SetCurrentPos(selEnd);
357 SetSelection(selStart, selEnd);
358 }
359 EnsureCaretVisible();
360 return true;
361 }
362 else
363 return false;
364 }
365
OnKeyDown(wxKeyEvent & event)366 void ctlSQLBox::OnKeyDown(wxKeyEvent &event)
367 {
368 #ifdef __WXGTK__
369 event.m_metaDown = false;
370 #endif
371
372 // Get the line ending type
373 wxString lineEnd;
374 switch (GetEOLMode())
375 {
376 case wxSTC_EOL_LF:
377 lineEnd = wxT("\n");
378 break;
379 case wxSTC_EOL_CRLF:
380 lineEnd = wxT("\r\n");
381 break;
382 case wxSTC_EOL_CR:
383 lineEnd = wxT("\r");
384 break;
385 }
386
387 // Block comment/uncomment
388 if (event.GetKeyCode() == 'K')
389 {
390 // Comment (Ctrl+k)
391 if (event.GetModifiers() == wxMOD_CONTROL)
392 {
393 if (BlockComment(false))
394 return;
395 }
396 // Uncomment (Ctrl+Shift+K)
397 else if (event.GetModifiers() == (wxMOD_CONTROL | wxMOD_SHIFT))
398 {
399 if (BlockComment(true))
400 return;
401 }
402 }
403
404 // Autoindent
405 if (m_autoIndent && event.GetKeyCode() == WXK_RETURN)
406 {
407 wxString indent, line;
408 line = GetLine(GetCurrentLine());
409
410 // Get the offset for the current line - basically, whether
411 // or not it ends with a \r\n, \n or \r, and if so, the length
412 int offset = 0;
413 if (line.EndsWith(wxT("\r\n")))
414 offset = 2;
415 else if (line.EndsWith(wxT("\n")))
416 offset = 1;
417 else if (line.EndsWith(wxT("\r")))
418 offset = 1;
419
420 // Get the indent. This is every leading space or tab on the
421 // line, up until the current cursor position.
422 int x = 0;
423 int max = line.Length() - (GetLineEndPosition(GetCurrentLine()) - GetCurrentPos()) - offset;
424 if(line != wxEmptyString)
425 {
426 while ((line[x] == '\t' || line[x] == ' ') && x < max)
427 indent += line[x++];
428 }
429
430 // Select any indent in front of the cursor to be removed. If
431 // the cursor is positioned after any non-indent characters,
432 // we don't remove anything. If there is already some selected,
433 // don't select anything new at all.
434 if (indent.Length() != 0 &&
435 (unsigned int)GetCurrentPos() <= ((GetLineEndPosition(GetCurrentLine()) - line.Length()) + indent.Length() + offset) &&
436 GetSelectedText() == wxEmptyString)
437 SetSelection(GetLineEndPosition(GetCurrentLine()) - line.Length() + offset, GetLineEndPosition(GetCurrentLine()) - line.Length() + indent.Length() + offset);
438
439 // Lose any selected text.
440 ReplaceSelection(wxEmptyString);
441
442 // Insert a replacement \n (or whatever), and the indent at the insertion point.
443 InsertText(GetCurrentPos(), lineEnd + indent);
444
445 // Now, reset the position, and clear the selection
446 SetCurrentPos(GetCurrentPos() + indent.Length() + lineEnd.Length());
447 SetSelection(GetCurrentPos(), GetCurrentPos());
448 }
449 else if (m_dlgFindReplace && event.GetKeyCode() == WXK_F3)
450 {
451 m_dlgFindReplace->FindNext();
452 }
453 else
454 event.Skip();
455 }
456
BlockComment(bool uncomment)457 bool ctlSQLBox::BlockComment(bool uncomment)
458 {
459 wxString lineEnd;
460 switch (GetEOLMode())
461 {
462 case wxSTC_EOL_LF:
463 lineEnd = wxT("\n");
464 break;
465 case wxSTC_EOL_CRLF:
466 lineEnd = wxT("\r\n");
467 break;
468 case wxSTC_EOL_CR:
469 lineEnd = wxT("\r");
470 break;
471 }
472
473 // Save the start position
474 const wxString comment = wxT("-- ");
475 int start = GetSelectionStart();
476
477 if (!GetSelectedText().IsEmpty())
478 {
479 wxString selection = GetSelectedText();
480 if (!uncomment)
481 {
482 selection.Replace(lineEnd, lineEnd + comment);
483 selection.Prepend(comment);
484 if (selection.EndsWith(comment))
485 selection = selection.Left(selection.Length() - comment.Length());
486 }
487 else
488 {
489 selection.Replace(lineEnd + comment, lineEnd);
490 if (selection.StartsWith(comment))
491 selection = selection.Right(selection.Length() - comment.Length());
492 }
493 ReplaceSelection(selection);
494 SetSelection(start, start + selection.Length());
495 }
496 else
497 {
498 // No text selection - (un)comment the current line
499 int column = GetColumn(start);
500 int curLineNum = GetCurrentLine();
501 int pos = PositionFromLine(curLineNum);
502
503 if (!uncomment)
504 {
505 InsertText(pos, comment);
506 }
507 else
508 {
509 wxString t = GetTextRange(pos, pos + comment.Length());
510 if (t == comment)
511 {
512 // The line starts with a comment, so we remove it
513 SetTargetStart(pos);
514 SetTargetEnd(pos + comment.Length());
515 ReplaceTarget(wxT(""));
516 }
517 else
518 {
519 // The line doesn't start with a comment, do nothing
520 return false;
521 }
522 }
523
524 if (GetLineCount() > curLineNum)
525 {
526 wxString nextLine = GetLine(curLineNum + 1);
527 if (nextLine.EndsWith(lineEnd))
528 nextLine = nextLine.Left(nextLine.Length() - lineEnd.Length());
529
530 int nextColumn = (nextLine.Length() < (unsigned int)column ? nextLine.Length() : column);
531 GotoPos(PositionFromLine(curLineNum + 1) + nextColumn);
532 }
533 }
534
535 return true;
536 }
537
OnKillFocus(wxFocusEvent & event)538 void ctlSQLBox::OnKillFocus(wxFocusEvent &event)
539 {
540 AutoCompCancel();
541 event.Skip();
542 }
543
UpdateLineNumber()544 void ctlSQLBox::UpdateLineNumber()
545 {
546 bool showlinenumber;
547
548 settings->Read(wxT("frmQuery/ShowLineNumber"), &showlinenumber, false);
549 if (showlinenumber)
550 {
551 long int width = TextWidth(wxSTC_STYLE_LINENUMBER,
552 wxT(" ") + NumToStr((long int)GetLineCount()) + wxT(" "));
553 if (width != GetMarginWidth(0))
554 {
555 SetMarginWidth(0, width);
556 Update();
557 }
558 }
559 else
560 {
561 SetMarginWidth(0, 0);
562 }
563 }
564
OnEndProcess(wxProcessEvent & ev)565 void ctlSQLBox::OnEndProcess(wxProcessEvent &ev)
566 {
567 if (process)
568 {
569 processErrorOutput = process->ReadErrorStream();
570 processOutput += process->ReadInputStream();
571 processExitCode = ev.GetExitCode();
572 delete process;
573 process = 0;
574 processID = 0;
575 }
576 }
577
ExternalFormat()578 wxString ctlSQLBox::ExternalFormat()
579 {
580 wxString msg;
581 processOutput = wxEmptyString;
582
583 bool isSelected = true;
584 wxString processInput = GetSelectedText();
585 if (processInput.IsEmpty())
586 {
587 processInput = GetText();
588 isSelected = false;
589 }
590 if (processInput.IsEmpty())
591 return _("Nothing to format.");
592
593 wxString formatCmd = settings->GetExtFormatCmd();
594 if (formatCmd.IsEmpty())
595 {
596 return _("You need to setup a formatting command");
597 }
598
599 if (process)
600 {
601 delete process;
602 process = NULL;
603 processID = 0;
604 }
605 processOutput = wxEmptyString;
606 processErrorOutput = wxEmptyString;
607 processExitCode = 0;
608
609 process = new sysProcess(this, wxConvUTF8);
610 processID = wxExecute(formatCmd, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process);
611 if (!processID)
612 {
613 delete process;
614 process = NULL;
615 processID = 0;
616 msg = _("Couldn't run formatting command: ") + formatCmd;
617 return msg;
618 }
619 process->WriteOutputStream(processInput);
620 process->CloseOutput();
621
622 int timeoutMs = settings->GetExtFormatTimeout();
623 int timeoutStepMs = 100;
624 int i = 0;
625 while (process && i * timeoutStepMs < timeoutMs)
626 {
627 wxSafeYield();
628 if (process)
629 processOutput += process->ReadInputStream();
630 wxSafeYield();
631 wxMilliSleep(timeoutStepMs);
632 i++;
633 }
634
635 if (process)
636 {
637 AbortProcess();
638 return wxString::Format(_("Formatting command did not respond in %d ms"), timeoutMs);
639 }
640
641 if (processExitCode != 0)
642 {
643 processErrorOutput.Replace(wxT("\n"), wxT(" "));
644 msg = wxString::Format(_("Error %d: "), processExitCode) + processErrorOutput;
645 return msg;
646 }
647 else if (processOutput.Trim().IsEmpty())
648 {
649 return _("Formatting command error: Output is empty.");
650 }
651
652 if (isSelected)
653 ReplaceSelection(processOutput);
654 else
655 SetText(processOutput);
656
657 return _("Formatting complete.");
658 }
659
AbortProcess()660 void ctlSQLBox::AbortProcess()
661 {
662 if (process && processID)
663 {
664 #ifdef __WXMSW__
665 // SIGTERM is useless for Windows console apps
666 wxKill(processID, wxSIGKILL, NULL, wxKILL_CHILDREN);
667 #else
668 wxKill(processID, wxSIGTERM, NULL, wxKILL_CHILDREN);
669 #endif
670 processID = 0;
671 }
672 }
673
OnPositionStc(wxStyledTextEvent & event)674 void ctlSQLBox::OnPositionStc(wxStyledTextEvent &event)
675 {
676 int pos = GetCurrentPos();
677 wxChar ch = GetCharAt(pos - 1);
678 wxChar nextch = GetCharAt(pos);
679 int st = GetStyleAt(pos - 1);
680 int match;
681
682
683 // Line numbers
684 // Ensure we don't recurse through any paint handlers on Mac
685 #ifdef __WXMAC__
686 Freeze();
687 #endif
688 UpdateLineNumber();
689 #ifdef __WXMAC__
690 Thaw();
691 #endif
692
693 // Clear all highlighting
694 BraceBadLight(wxSTC_INVALID_POSITION);
695
696 // Check for braces that aren't in comment styles,
697 // double quoted styles or single quoted styles
698 if ((ch == '{' || ch == '}' ||
699 ch == '[' || ch == ']' ||
700 ch == '(' || ch == ')') &&
701 st != 2 && st != 6 && st != 7)
702 {
703 match = BraceMatch(pos - 1);
704 if (match != wxSTC_INVALID_POSITION)
705 BraceHighlight(pos - 1, match);
706 }
707 else if ((nextch == '{' || nextch == '}' ||
708 nextch == '[' || nextch == ']' ||
709 nextch == '(' || nextch == ')') &&
710 st != 2 && st != 6 && st != 7)
711 {
712 match = BraceMatch(pos);
713 if (match != wxSTC_INVALID_POSITION)
714 BraceHighlight(pos, match);
715 }
716
717 // Roll back through the doc and highlight any unmatched braces
718 while ((pos--) >= 0)
719 {
720 ch = GetCharAt(pos);
721 st = GetStyleAt(pos);
722
723 if ((ch == '{' || ch == '}' ||
724 ch == '[' || ch == ']' ||
725 ch == '(' || ch == ')') &&
726 st != 2 && st != 6 && st != 7)
727 {
728 match = BraceMatch(pos);
729 if (match == wxSTC_INVALID_POSITION)
730 {
731 BraceBadLight(pos);
732 break;
733 }
734 }
735 }
736
737 event.Skip();
738 }
739
740
OnMarginClick(wxStyledTextEvent & event)741 void ctlSQLBox::OnMarginClick(wxStyledTextEvent &event)
742 {
743 if (event.GetMargin() == 2)
744 ToggleFold(LineFromPosition(event.GetPosition()));
745
746 event.Skip();
747 }
748
749
750 extern "C" char *tab_complete(const char *allstr, const int startptr, const int endptr, void *dbptr);
OnAutoComplete(wxCommandEvent & rev)751 void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev)
752 {
753 if (GetReadOnly())
754 return;
755 if (m_database == NULL)
756 return;
757 if (m_autocompDisabled)
758 return;
759
760 wxString what = GetCurLine().Left(GetCurrentPos() - PositionFromLine(GetCurrentLine()));;
761 int spaceidx = what.Find(' ', true);
762
763 char *tab_ret;
764 if (spaceidx == -1)
765 tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 1, m_database);
766 else
767 tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, what.Len() + 1, m_database);
768
769 if (tab_ret == NULL || tab_ret[0] == '\0')
770 return; /* No autocomplete available for this string */
771
772 wxString wxRet = wxString(tab_ret, wxConvUTF8);
773 free(tab_ret);
774
775 // Switch to the generic list control. Native doesn't play well with
776 // autocomplete on Mac.
777 #ifdef __WXMAC__
778 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), true);
779 #endif
780
781 if (spaceidx == -1)
782 AutoCompShow(what.Len(), wxRet);
783 else
784 AutoCompShow(what.Len() - spaceidx - 1, wxRet);
785
786 // Now switch back
787 #ifdef __WXMAC__
788 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), false);
789 #endif
790 }
791
792
~ctlSQLBox()793 ctlSQLBox::~ctlSQLBox()
794 {
795 if (m_dlgFindReplace)
796 {
797 m_dlgFindReplace->Destroy();
798 m_dlgFindReplace = 0;
799 }
800 AbortProcess();
801 }
802
803
804 /*
805 * Callback function from tab-complete.c, bridging the gap between C++ and C.
806 * Execute a query using the C++ APIs, returning it as a tab separated
807 * "char*-string"
808 * The query is expected to return only one column, and will have an ORDER BY
809 * clause for this column added automatically.
810 */
811 extern "C"
pg_query_to_single_ordered_string(char * query,void * dbptr)812 char *pg_query_to_single_ordered_string(char *query, void *dbptr)
813 {
814 pgConn *db = (pgConn *)dbptr;
815 pgSet *res = db->ExecuteSet(wxString(query, wxConvUTF8) + wxT(" ORDER BY 1"));
816 if (!res)
817 return NULL;
818
819 wxString ret = wxString();
820 wxString tmp;
821
822 while (!res->Eof())
823 {
824 tmp = res->GetVal(0);
825 if (tmp.Mid(tmp.Length() - 1) == wxT("."))
826 ret += tmp + wxT("\t");
827 else
828 ret += tmp + wxT(" \t");
829
830 res->MoveNext();
831 }
832
833 if(res)
834 {
835 delete res;
836 res = NULL;
837 }
838 ret.Trim();
839 // Trims both space and tab, but we want to keep the space!
840 if (ret.Length() > 0)
841 ret += wxT(" ");
842
843 return strdup(ret.mb_str(wxConvUTF8));
844 }
845
846
847 // Find some text in the document.
RegexFindText(int minPos,int maxPos,const wxString & text)848 CharacterRange ctlSQLBox::RegexFindText(int minPos, int maxPos, const wxString &text)
849 {
850 TextToFind ft;
851 ft.chrg.cpMin = minPos;
852 ft.chrg.cpMax = maxPos;
853 wxWX2MBbuf buf = text.mb_str(wxConvUTF8);
854 ft.lpstrText = (char *)(const char *)buf;
855
856 if (SendMsg(2150, wxSTC_FIND_REGEXP, (long)&ft) == -1)
857 {
858 ft.chrgText.cpMin = -1;
859 ft.chrgText.cpMax = -1;
860 }
861
862 return ft.chrgText;
863 }
864