2 // SqlEditor.cs - writen in C# using GTK#
5 // Daniel Morgan <danmorg@sc.rr.com>
6 // Rodrigo Moya <rodrigo@gnome-db.org>
8 // (c)copyright 2002 Daniel Morgan
9 // (c)copyright 2002 Rodrigo Moya
11 // SqlEditorSharp is based on the gnome-db-sql-editor.c in libgnomedb.
12 // SqlEditorSharp falls under the GPL license and is included
13 // in SQL# For GTK#. SQL# For GTK# is a database query tool for Mono.
16 namespace SqlEditorSharp
22 using System
.Collections
;
25 using System
.Runtime
.InteropServices
;
26 using System
.Diagnostics
;
27 using Mono
.Data
.SqlSharp
.Gui
.GtkSharp
;
29 /// <summary> SqlEditor Class</summary>
32 public class SqlEditorSharp
: Gtk
.VBox
36 // text tags for TextTagTable in TextBuffer
37 private TextTag freecomment_tag
;
38 private TextTag linecomment_tag
;
39 private TextTag singlequotedconstant_tag
;
40 private TextTag sql_tag
;
41 private TextTag normaltext_tag
;
43 // determine if something has changed beyond a line
44 // updating one line is faster than the whole buffer
45 //private int line_last_changed;
46 //private int last_freecomment_count;
49 private bool use_hi_lighting
;
50 private string family
;
53 private ScrolledWindow scroll
;
54 private TextView sqlTextView
;
55 private TextBuffer sqlTextBuffer
;
56 private EditorTab tab
= null;
60 public SqlEditorSharp() : base(false, 4) {
61 scroll
= new ScrolledWindow (
62 new Adjustment (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
63 new Adjustment (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
64 scroll
.HscrollbarPolicy
= Gtk
.PolicyType
.Automatic
;
65 scroll
.VscrollbarPolicy
= Gtk
.PolicyType
.Automatic
;
66 scroll
.ShadowType
= Gtk
.ShadowType
.In
;
67 this.PackStart (scroll
, true, true, 0);
69 // default font famly for SQL editor
72 // other default settings
73 use_hi_lighting
= false;
75 // create text tag table
76 TextTagTable textTagTable
= new TextTagTable ();
78 // anything else is normaltext
79 normaltext_tag
= new TextTag ("normaltext");
80 normaltext_tag
.Family
= family
;
81 normaltext_tag
.Foreground
= "black";
82 normaltext_tag
.Style
= Pango
.Style
.Normal
;
83 textTagTable
.Add (normaltext_tag
);
85 // SQL Keywords - SELECT FROM WHERE, etc
86 sql_tag
= new TextTag ("sql");
87 sql_tag
.Family
= family
;
88 sql_tag
.Foreground
= "blue";
89 sql_tag
.Style
= Pango
.Style
.Normal
;
90 textTagTable
.Add (sql_tag
);
92 // c like free comment - used within a SQL statement
93 freecomment_tag
= new TextTag ("freecomment");
94 freecomment_tag
.Family
= family
;
95 freecomment_tag
.Foreground
= "darkgreen";
96 freecomment_tag
.Style
= Pango
.Style
.Italic
;
97 textTagTable
.Add (freecomment_tag
);
99 // c++ like line comment, but using two hyphens
100 linecomment_tag
= new TextTag ("linecomment");
101 linecomment_tag
.Family
= family
;
102 linecomment_tag
.Foreground
= "darkgreen";
103 linecomment_tag
.Style
= Pango
.Style
.Italic
;
104 textTagTable
.Add (linecomment_tag
);
106 /* single quoted constant - WHERE COL1 = 'ABC' */
107 singlequotedconstant_tag
= new TextTag ("singlequotedconstant");
108 singlequotedconstant_tag
.Family
= family
;
109 singlequotedconstant_tag
.Foreground
= "red";
110 singlequotedconstant_tag
.Style
= Pango
.Style
.Normal
;
111 textTagTable
.Add (singlequotedconstant_tag
);
113 // create TextBuffer and TextView
114 sqlTextBuffer
= new TextBuffer (textTagTable
);
115 sqlTextView
= new TextView (sqlTextBuffer
);
117 // allow it to be edited
118 sqlTextView
.Editable
= true;
120 //line_last_changed = -1;
121 //last_freecomment_count = -1;
123 // attach OnTextChanged callback function
124 // to "changed" signal so we can do something
125 // when the text has changed in the buffer
126 sqlTextBuffer
.Changed
+= new EventHandler (OnTextChanged
);
128 // add the TextView to the ScrolledWindow
129 scroll
.Add (sqlTextView
);
134 public TextBuffer Buffer
{
136 return sqlTextBuffer
;
140 public TextView View
{
146 public EditorTab Tab
{
156 public bool UseSyntaxHiLighting
{
158 return use_hi_lighting
;
162 use_hi_lighting
= value;
168 void OnTextChanged (object o
, EventArgs args
)
171 tab
.label
.Text
= tab
.basefilename
+ " *";
173 SqlSharpGtk
.DebugWriteLine ("[[[[[ Syntax Hi-Light Text BEGIN ]]]]]");
175 if (use_hi_lighting
== true) {
176 SyntaxHiLightText ();
179 SqlSharpGtk
.DebugWriteLine ("[[[[[ Syntax Hi-Light Text END ]]]]]\n");
182 void SyntaxHiLightText ()
184 TextIter start_iter
, end_iter
,
186 TextIter match_start1
, match_end1
,
187 match_start2
, match_end2
;
189 int hyphen
= 0, single_quotes
= 0;
190 string text
= String
.Empty
;
191 int i
= 0, start_con
= 0, end_con
= 0;
193 //int freecomment_count = 0;
195 TextMark insert_mark
;
198 insert_mark
= sqlTextBuffer
.InsertMark
;
199 sqlTextBuffer
.GetIterAtMark (out insert_iter
, insert_mark
);
200 //line = insert_iter.Line;
202 /* get the starting and ending text iterators */
203 sqlTextBuffer
.GetIterAtOffset (out start_iter
, 0);
204 char_count
= sqlTextBuffer
.CharCount
;
205 sqlTextBuffer
.GetIterAtOffset (out end_iter
, char_count
);
207 SqlSharpGtk
.DebugWriteLine ("char_count: " + char_count
);
209 /* since line is not same - redo all */
210 //if (line != line_last_changed) {
211 /* remove all previously applied tags */
212 sqlTextBuffer
.RemoveAllTags (start_iter
, end_iter
);
214 /* apply the entire buffer to the normaltext tag */
215 sqlTextBuffer
.ApplyTag (normaltext_tag
, start_iter
, end_iter
);
217 //else { /* just worry about current insertion line */
218 // /* get start iter */
219 // if (insert_iter.StartsLine () == true) {
220 // start_iter = insert_iter;
223 // start_iter = insert_iter;
224 // start_iter.LineOffset = 0;
226 // /* get end iter */
227 // end_iter.ForwardToLineEnd ();
228 // char_count = start_iter.CharsInLine;
230 // /* remove all previously applied tags */
231 // sqlTextBuffer.RemoveAllTags (start_iter, end_iter);
233 // /* apply the entire buffer to the normaltext tag */
234 // sqlTextBuffer.ApplyTag (normaltext_tag,
235 // start_iter, end_iter);
237 // /* get the starting and ending text iterators */
238 // sqlTextBuffer.GetIterAtOffset (out start_iter, 0);
239 // char_count = sqlTextBuffer.CharCount;
240 // sqlTextBuffer.GetIterAtOffset (out end_iter, char_count);
243 /* ------------------------------------
244 * Free Comments (sort of like c style)
245 * ------------------------------------
246 * except in SQL, a c like comment occurs within
249 match_start1
= start_iter
; // dummy
250 match_end1
= end_iter
; // dummy
251 match_start2
= start_iter
; // dummy
252 match_end2
= end_iter
; // dummy
254 while (start_iter
.IsEnd
== false) {
255 // FIXME: match_start1, match_end1, end_iter
256 // need to be set to have ref in front
257 // Problem with TextIter's ForwardSearch()
258 // in GTK# (not GTK+)
259 if (start_iter
.ForwardSearch (
261 TextSearchFlags
.TextOnly
,
266 /* beginning of free comment found */
267 //freecomment_count++;
268 // FIXME: fix match_start2, match_end2, end_iter
270 if (match_end1
.ForwardSearch (
272 TextSearchFlags
.TextOnly
,
277 // ending of free comment found,
278 // now hi-light comment
279 sqlTextBuffer
.ApplyTag (
283 match_end2
.ForwardChars (1);
284 start_iter
= match_end2
;
288 // hi-light to the end,
289 // to let the user know
290 // the ending asterisk slash is missing
303 /* if free comments is different than last time,
304 * invalidate line_last_changed - causes
305 * a complete redo (instead hi-lighting just the current line -
306 * do the whole buffer)
307 * THIS IS JUST AN ATTEMPT FOR SPEED
309 //if (freecomment_count != last_freecomment_count) {
310 // line_last_changed = -1;
313 /*********************************************************************
314 * See if the following needs hi-lighting:
315 * - Line Comments (sort of like C++ slash slash comments
316 * but uses hypen hyphen and it is based at the beginning of a line)
317 * - Single-Quoted Constants ( WHERE COL1 = 'ABC' )
318 * - SQL keywords (SELECT, FROM, WHERE, UPDATE, etc)
319 *********************************************************************/
320 //if (line != line_last_changed) {
321 sqlTextBuffer
.GetIterAtOffset (out start_iter
, 0);
324 // if (insert_iter.StartsLine () == true) {
325 // start_iter = insert_iter;
328 // start_iter = insert_iter;
329 // start_iter.LineOffset = 0;
333 // get starting and ending iters
334 // and character count of line
335 char_count
= sqlTextBuffer
.CharCount
;
336 sqlTextBuffer
.GetIterAtOffset (out end_iter
, char_count
);
338 // for each line, look for:
339 // line comments, constants, and keywoards
342 iter
.ForwardToLineEnd ();
343 text
= sqlTextBuffer
.GetText (
344 start_iter
, iter
, false);
346 // look for line comment
347 char_count
= start_iter
.CharsInLine
;
349 for (i
= 0; i
< char_count
- 1; i
++) {
354 // line comment found
371 // this line is not line commented
372 i
= char_count
; // break out of for loop
376 // if not line commented,
377 // look for singled quoted constants
380 if (start_iter
.IsEnd
== true)
381 break; // break out of for loop
386 LookForSingleQuotesAndWords (
395 } while (start_iter
.ForwardLine () == true);
398 // POOR ATTEMPTS AT SPEED - last_freecomment_count
399 // and line_last_changed
401 //last_freecomment_count = freecomment_count;
402 //line_last_changed = line;
405 void LookForSingleQuotesAndWords (ref TextIter start_iter
,
406 string text
, int char_count
,
407 ref int start_word
, ref int single_quotes
,
408 ref int start_con
, ref int end_con
)
410 TextIter match_start1
, match_end1
;
414 for (i
= 0; i
< char_count
; i
++) {
415 match_start1
= start_iter
;
416 match_end1
= start_iter
;
418 if (match_end1
.IsEnd
== true)
421 if (CharHasTag (start_iter
,
425 if (single_quotes
== 0 &&
433 else if (Char
.IsLetter (ch
)) {
440 else if (single_quotes
== 1) {
444 // single quoted constant
448 // ending of constant
453 singlequotedconstant_tag
,
462 else if (start_word
!= -1) {
464 // is character alphabetic, numeric, or '_'
465 if (Char
.IsLetterOrDigit (ch
) ||
474 if (IsTextSQL (text
, start_word
, i
)) {
475 // word is a SQL keyword,
496 if( start_word
!= -1) {
497 if (IsTextSQL (text
, start_word
, i
)) {
498 // word is a SQL keyword,
509 void ApplyTag ( TextTag apply_tag
, TextTag remove_tag
,
510 TextIter start_iter
, TextIter end_iter
)
513 DebugText(start_iter
, end_iter
, "ApplyTag() " +
514 "remove: " + remove_tag
.Name
+
515 " apply: " + apply_tag
.Name
);
518 sqlTextBuffer
.RemoveTag (
520 start_iter
, end_iter
);
522 sqlTextBuffer
.ApplyTag (
524 start_iter
, end_iter
);
527 void ApplyTagOffsets (TextIter start_iter
,
528 int start_offset
, int end_offset
,
532 TextIter begin_iter
, end_iter
;
534 begin_iter
= start_iter
;
535 end_iter
= start_iter
;
537 begin_iter
.LineOffset
= start_offset
;
538 end_iter
.LineOffset
= end_offset
;
541 DebugText(start_iter
, end_iter
, "ApplyTagOffsets() " +
542 "remove: " + remove_tag
.Name
+
543 " apply: " + apply_tag
.Name
+
544 " start: " + start_offset
.ToString() +
545 " end: " + end_offset
.ToString());
548 sqlTextBuffer
.RemoveTag (remove_tag
,
549 begin_iter
, end_iter
);
551 sqlTextBuffer
.ApplyTag (apply_tag
,
552 begin_iter
, end_iter
);
555 /* is word a SQL keyword? */
556 bool IsTextSQL (string text
, int begin
, int end
)
562 if(text
.Equals(String
.Empty
))
574 text_len
= end
- begin
;
579 SqlSharpGtk
.DebugWriteLine("IsTextSQL - " +
580 "begin: " + begin
.ToString() +
581 " end: " + end
.ToString() +
582 " text_len: " + text_len
);
583 SqlSharpGtk
.DebugWriteLine("[TEXT BEGIN]");
584 SqlSharpGtk
.DebugWriteLine(text
);
585 SqlSharpGtk
.DebugWriteLine("[TEXT END ]");
588 for (i
= 0; sql_keywords
[i
] != String
.Empty
; i
++) {
589 if(text_len
== sql_keywords
[i
].Length
) {
591 SqlSharpGtk
.DebugWriteLine(
592 "Test length: " + text_len
+
593 " keyword: " + keyword
);
596 keyword
= text
.Substring (begin
, text_len
);
598 catch(ArgumentOutOfRangeException a
) {
599 Console
.WriteLine ("Internal Error: SqlSharpGtk: text.Substring() ArgumentOutOfRange");
602 keyword
= keyword
.ToUpper();
604 if(keyword
.Equals (sql_keywords
[i
]))
612 // does the character at offset in the GtkTextIter has
613 // this text tag applied?
614 bool CharHasTag(TextIter iter
,
615 TextTag tag
, int char_offset_in_line
)
618 TextIter offset_iter
;
621 offset_iter
.LineOffset
= char_offset_in_line
;
623 return offset_iter
.HasTag (tag
);
629 start
= sqlTextBuffer
.StartIter
;
630 end
= sqlTextBuffer
.EndIter
;
631 sqlTextBuffer
.Delete(start
,end
);
634 public void LoadFromFile(string inFilename
)
636 StreamReader sr
= new StreamReader(inFilename
);
641 while((NextLine
= sr
.ReadLine()) != null) {
642 line
= NextLine
+ "\n";
643 sqlTextBuffer
.Insert (sqlTextBuffer
.EndIter
, line
);
648 public void SaveToFile(string outFilename
)
650 TextIter start_iter
, iter
;
652 StreamWriter sw
= null;
654 sw
= new StreamWriter(outFilename
);
655 sqlTextBuffer
.GetIterAtOffset (out iter
, 0);
657 while (iter
.ForwardLine()) {
658 text
= sqlTextBuffer
.GetText(start_iter
, iter
, false);
662 text
= sqlTextBuffer
.GetText(start_iter
, iter
, false);
668 void DebugText (TextIter iter_start
, TextIter iter_end
,
673 string text
= sqlTextBuffer
.GetText (
674 iter_start
, iter_end
, false);
681 SqlSharpGtk
.DebugWriteLine(msg
);
685 static readonly string[] sql_keywords
=