2008-02-27

Working with GtkScintilla

First thing to note, I was working with PHP-GTK2 (php-gtk-2.0.0 beta) and, naturally, was following the reference on that (PHP-GTK2 reference). However, GtkScintilla section in PHP-GTK2 reference really is not all that informative. Since the GtkScintilla API undergone very little changes from the last version (as far as I can tell), you may be better off using the older reference for GtkScintilla.

Another thing to note, some stuff is missing from the older reference as well - most notably, descriptions (and even names) of nearly all defines are missing, as well as for some of the methods - those related to search functionality provided by GtkScintilla class, for an example. For instance, this is what the reference for set_search_flags method looks like:

PHP-GTK: void set_search_flags(int flags);
PHP-GTK2: void set_search_flags(flags);

OK. Even a somewhat more informative reference didn't take me very far. So, the flags are int. Great. Now what? And how do I do the search, anyways?

Use the source

Well, as it turned out, lacking documentation for the constants wasn't such a problem, because none of search-related constants are defined anyway. So, what's a man to do, except to dive into the source code?

After some greping through the GtkScintilla sources, I've found these values in ext\scintilla\libscintilla\include\Scintilla.h:

#define SCFIND_WHOLEWORD 2
#define SCFIND_MATCHCASE 4
#define SCFIND_WORDSTART 0x00100000
#define SCFIND_REGEXP 0x00200000 

And so that's what I used for my search flags:

@define ("SCINTILLA_FIND_DOWN", 1);
@define ("SCINTILLA_FIND_WHOLE_WORDS", 2);
@define ("SCINTILLA_FIND_MATCH_CASE", 4);
@define ("SCINTILLA_FIND_WORD_START", 0x00100000);
@define ("SCINTILLA_FIND_REGEXP", 0x00200000);

The @s in front of each define are a future safe short-circuit error guard, in case a particular define actually exist. I could surround each statement with ifs, yeah, but this seems like a much nicer way of expressing the same thing - if a define exists, an error occurs and the new define statement is not executed. The error gets suppressed thanks to @, and we're on our merry way. Well, that covered the search-related defines, but I still didn't know how to actually conduct the search.

Putting it to (good) use

After some trial-and error, I've managed to poke my way through. So, in a nutshell:

// $current is a GtkScintilla instance
if (!$firstTime) {
    $pos = $current->get_selection_end();
    $current->set_selection_start($pos);
    $current->goto_pos($pos);
}
$current->search_anchor();
// Search forward
$result = $current->search_next($searchFlags, $searchTerm);
// Search backwards
// $result = $current->search_prev($searchFlags, $searchTerm);
if ($result > 0) echo("Match found at $result");

The if clause determines if we had a previous match and if so, resets the selection and sets the current cursor position at the end of it. Then we anchor the start of the search to the current cursor position by calling GtkSintillas search_anchor method. Then we can actually perform the search, by calling either search_next (search forward) or search_prev (search backwards) methods and passing the appropriate arguments (an integer bitmask $searchFlags and a string to look for, $searchTerm).

Of course, you should have something like this above the code I just introduced:

$firstTime = true; // or false
$searchTerm = "blah"; // whatever your search term is
$searchFlags = SCINTILLA_FIND_MATCH_CASE & SCINTILLA_FIND_WHOLE_WORDS; // or whatever

If successful, both methods set the selection around the search term for you and return the position in the text where the match occured. If the return value is 0, the match wasn't found.

No comments:

Post a Comment