]> git.saurik.com Git - wxWidgets.git/blobdiff - contrib/src/applet/prepifelse.cpp
Added support for toggle button on OS/2.
[wxWidgets.git] / contrib / src / applet / prepifelse.cpp
index cc209ce795f622de51d303b2bf2e71bc12bd395d..0724dae64483b61966471dd868ec95401b777262 100644 (file)
 *
 ****************************************************************************/
 
 *
 ****************************************************************************/
 
-// For compilers that support precompilation
-#include "wx/wxprec.h"
-#include "wx/html/forcelnk.h"
-
 // Include private headers
 #include "wx/applet/prepifelse.h"
 #include "wx/applet/ifelsevar.h"
 // Include private headers
 #include "wx/applet/prepifelse.h"
 #include "wx/applet/ifelsevar.h"
+#include "wx/applet/echovar.h"
+#include "wx/string.h"
 
 
-/*---------------------------- Global variables ---------------------------*/
-
+// Force link macro
+#include "wx/html/forcelnk.h"
+// wxWindows
+#include "wx/msgdlg.h"
 
 /*----------------------------- Implementation ----------------------------*/
 
 
 /*----------------------------- Implementation ----------------------------*/
 
@@ -49,21 +49,240 @@ do not correctly pass the given return value.
 ****************************************************************************/
 int ReverseFind(
     const wxString &tstr,
 ****************************************************************************/
 int ReverseFind(
     const wxString &tstr,
-    const wxString &str)
+    const wxString &str,
+    int start = -1)
 {
     wxASSERT( str.GetStringData()->IsValid() );
 
     // TODO could be made much quicker than that
     int p = tstr.Len()-str.Len()-1;
 {
     wxASSERT( str.GetStringData()->IsValid() );
 
     // TODO could be made much quicker than that
     int p = tstr.Len()-str.Len()-1;
+    int p2 = start-str.Len();
+
+    // if the user supplied a valid start point, use it
+    if (start != -1 && p > p2) p = p2;
     while ( p >= 0 ) {
         if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 )
             return p;
         p--;
         }
     while ( p >= 0 ) {
         if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 )
             return p;
         p--;
         }
-
     return -1;
 }
 
     return -1;
 }
 
+/* {SECRET} */
+/****************************************************************************
+REMARKS:
+tells if a character is a letter.
+replace this when wxWindows gets regex library. (without strange licensing
+restrictions)
+****************************************************************************/
+bool IsLetter(
+    char c, bool acceptspace = false)
+{
+    if (acceptspace && (c == ' ')) return true;
+    if (c >= '0' && c <= '9') return true;
+    return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '\"' || c == '\'' );
+}
+
+#define IsQuote(c) (c == '\'' || c == '\"')
+
+/* {SECRET} */
+/****************************************************************************
+REMARKS:
+tells if a character is a letter.
+replace this when wxWindows gets regex library. (without strange licensing
+restrictions)
+****************************************************************************/
+wxString GetEquals(
+    wxString var,
+    wxString value)
+{
+    if (!wxEchoVariable::Exists(var)) {
+        // TODO: when we implement the set variable, check for a set variable as well
+        #ifdef CHECKED         
+            wxMessageBox(wxString("wxHTML #if\\else error: Variable ") + var + wxString(" not found."),"Error",wxICON_ERROR);
+        #endif
+        return wxString("0"); // false
+        }
+
+    wxString tmp = wxEchoVariable::GetValue(var);
+
+    if (IsQuote( value.GetChar(0) ))
+        value = value.Mid(1);
+    if (IsQuote(value.GetChar(value.Length()-1)))
+        value = value.Mid(0,value.Length()-1);
+
+    if (tmp.CmpNoCase(value) == 0) return wxString("1");
+    return wxString("0");
+}
+
+/****************************************************************************
+PARAMETERS:
+str        - text of #if statement
+
+RETURNS:
+true or false depending on how it evaluated
+
+REMARKS:
+TODO: rewrite this whole thing using regular expressions when they are done.
+
+SEE ALSO:
+wxIfElseVariable
+****************************************************************************/
+bool ParseIfStatementValue(
+    wxString &str)
+{
+    // Find out if the tag has parenthesis
+    // recursive to parse the text within the parenthesis,
+    // replacing the text with 1 or 0, (hardcoded true or false)
+    int b;
+    while ((b = str.Find('(')) != -1) {
+        int e;
+        // Find the matching parenthesis
+        int nextbeg, nextend;
+        int parencount = 1, min = b+1;
+        do {
+            nextbeg = str.find('(', min);
+            nextend = str.find(')', min);
+            if (nextbeg < nextend && nextbeg != wxString::npos) {
+                parencount++;
+                min = nextbeg+1;
+                }
+            else {
+                parencount--;
+                min = nextend+1;
+                }
+
+            if (nextend == wxString::npos) {
+#ifdef CHECKED         
+                wxMessageBox("wxHTML #if\\else error: Unmatched parenthesis in #if expression.","Error",wxICON_ERROR);
+#endif
+                return true;
+                }
+            // once parencount reaches 0 again we have found our matchin )
+            } while (parencount > 0);
+
+        e = nextend;
+
+        // Extract the expression from the parenthesis block and recurse
+        // to solve it.
+        wxString tag;
+        tag = str.Mid(b+1, e-b-1);
+        bool val = ParseIfStatementValue(tag);
+        // Add extra spaces just in case of NOT(VAL)
+        if (val) str = str.Mid(0, b) + " 1" + str.Mid(e+1);
+        else str = str.Mid(0, b) + " 0" + str.Mid(e+1);
+        }
+
+    // Remove spaces from left and right
+    str.Trim(false);
+    str.Trim(true);
+
+    // Convert text method of operators "AND" and "OR" to c style
+    // this makes only one special case necessary for each later on
+    str.Replace(" AND ", "&&");
+    str.Replace(" OR ", "||");
+    str.Replace(" EQUALS ", "==");
+
+    // Check for equals statements
+    // == statements are special because they are evaluated as a single block
+    int equ;
+    equ = str.find("==");
+    while (equ != wxString::npos) {
+        int begin, end;
+        int begin2, end2; // ends of words
+        begin = equ-1;
+        end = equ+2;
+
+        // remove spaces, find extents
+        while (end < str.Length() && str.GetChar(end) == ' ')
+            end++;
+        while (begin >= 0 && str.GetChar(begin) == ' ')
+            begin--;
+        end2 = end;
+        begin2 = begin;
+        if (str.GetChar(end2) == '\'' || str.GetChar(end2) == '\"') {
+            end2++;
+            while (end2 < str.Length() && str.GetChar(end2) != '\'' && str.GetChar(end2) != '\"' )
+                end2++;
+            end2++;
+            }
+        else {
+            while (end2 < str.Length() && IsLetter(str.GetChar(end2)))
+                end2++;
+            }
+        while (begin >= 0 && IsLetter(str.GetChar(begin)))
+            begin--;
+
+        if (begin < 0) begin = 0;
+        else begin++;
+        if (end2 >= str.Length()) end2 = str.Length();
+
+        wxString tmpeq = GetEquals(str.Mid(begin, begin2-begin+1), str.Mid(end, end2-end));
+        str = str.Mid(0, begin) + wxString(" ") + tmpeq + wxString(" ") +
+            str.Mid(end2);
+        equ = str.find("==");
+
+        // Remove spaces from left and right
+        str.Trim(false);
+        str.Trim(true);
+        }
+
+    // We use ReverseFind so that the whole left expression gets evaluated agains
+    // the right single item, creating a left -> right evaluation
+    // Search for || operators, recurse to solve (so we don't have to handle special cases here)
+    int and, or;
+    and = ReverseFind(str, "&&");
+    or = ReverseFind(str, "||");
+    if ( (and != -1) || (or != -1) ) {
+        wxString tag1, tag2;
+        // handle the rightmost first to force left->right evaluation
+        if ( (and > or) ) {
+            return (
+                ParseIfStatementValue(tag2 = str.Mid(and+2)) &&
+                ParseIfStatementValue(tag1 = str.Mid(0, and)) );
+            }
+        else {
+            return (
+                ParseIfStatementValue(tag2 = str.Mid(or+2)) ||
+                ParseIfStatementValue(tag1 = str.Mid(0, or)) );
+            }
+        }
+
+    // By the time we get to this place in the function we are guarenteed to have a single
+    // variable operation, perhaps with a NOT or ! operator
+    bool notval = false;
+
+    // search for a NOT or ! operator
+    if (str.Mid(0, 1) == "!") {
+        str.Remove(0, 1);
+        str.Trim(false); // trim spaces from left
+        notval = true;
+        }
+    else if (str.Mid(0,4).CmpNoCase("NOT ") == 0) {
+        str.Remove(0, 4);
+        str.Trim(false); // trim any extra spaces from left
+        notval = true;
+        }
+
+    // now all we have left is the name of the class or a hardcoded 0 or 1
+    if (str == "") {
+#ifdef CHECKED         
+        wxMessageBox("wxHTML #if\\else error: Empty expression in #if\\#elif statement.","Error",wxICON_ERROR);
+#endif
+        return true;
+        }
+
+    // check for hardcoded 0 and 1 cases, (these are used by parenthesis catcher)
+    // this just decomplicates the recursion algorithm
+    if (str == "0") return notval;
+    if (str == "1") return !notval;
+
+    // Grab the value from the variable class identified by cname
+    bool value = wxIfElseVariable::GetValue(str);
+    if (notval) value = !value;
+    return value;
+
+}
 /****************************************************************************
 PARAMETERS:
 text        - HTML to process for if/else blocks
 /****************************************************************************
 PARAMETERS:
 text        - HTML to process for if/else blocks
@@ -72,7 +291,7 @@ RETURNS:
 The string containing the processed HTML
 
 REMARKS:
 The string containing the processed HTML
 
 REMARKS:
-This function replaces #if, #else, and #endif directives with the text
+This function replaces #if, #else, #elif, and #endif directives with the text
 contained within the blocks, dependant on the value of the given boolean
 variable. The variable is created by making a sub class of wxIfElseVariable.
 Dynamic class construction is used at run time internally to create an instance
 contained within the blocks, dependant on the value of the given boolean
 variable. The variable is created by making a sub class of wxIfElseVariable.
 Dynamic class construction is used at run time internally to create an instance
@@ -86,14 +305,79 @@ wxString wxIfElsePrep::Process(
 {
        int b;
        char ft[] = "<!--#if ";
 {
        int b;
        char ft[] = "<!--#if ";
+       char ftend[] = "<!--#endif-->";
+    char ftelse[] = "<!--#else-->";
+    char ftnot[] = "<!--#if not ";
+    char ftnot2[] = "<!--#if !";
+    char ftelif[] = "<!--#elif ";
        
        
-       // make a copy so we can replace text as we go without affecting the original
+    // make a copy so we can replace text as we go without affecting the original
        wxString output = text;
        wxString output = text;
+
+    // Avoid duplication of our parsing code by turning any #elif blocks into appropriate
+    // else/if blocks
+    while ((b = ReverseFind(output.Lower(), ftelif)) != -1) {
+        int e;
+        // Replace beginning of block
+        e = output.find("-->", b + strlen(ftelif));
+
+        if (e == wxString::npos) {
+#ifdef CHECKED         
+            wxMessageBox("wxHTML #elif error: Premature end of file while parsing #elif.","Error",wxICON_ERROR);
+#endif
+            break;
+            }
+
+        // Convert to lower case so find is easy, grab everything after #elif tag
+        wxString remains = (output.Mid(e+strlen("-->"))).Lower();
+
+        // find matching else or endif
+        int nextif, nextendif;
+        int ifcount = 1, min = 0;
+        do {
+            nextif = remains.find(ft, min);
+            nextendif = remains.find(ftend, min);
+            if (nextif < nextendif && nextif != wxString::npos) {
+                ifcount++;
+                min = nextif+1;
+                }
+            else {
+                ifcount--;
+                min = nextendif+1;
+                }
+
+            if (nextendif == wxString::npos) {
+#ifdef CHECKED         
+                wxMessageBox("wxHTML #elif error: Premature end of file before finding #endif.","Error",wxICON_ERROR);
+#endif
+                break;
+                }
+            // once ifcount reaches 0 again we have found our matchin #endif
+            } while (ifcount > 0);
+
+        // If it couldn't be found die gracefully
+        if (nextendif == wxString::npos) {
+            // We already displayed a message, just break all the way out
+            break;
+            }
+
+        int elifsize = e - (b + strlen(ftelif)) + strlen("-->");
+        // Create the #if/else block, removing the #elif code
+        output = output.Mid(0, b) +
+            wxString(wxString(ftelse)+wxString(ft)) +
+            output.Mid(b+strlen(ftelif), elifsize+nextendif) +
+            wxString(ftend) +
+            output.Mid(b+strlen(ftelif)+elifsize+nextendif);
+        }
+       
+    // Parse out the if else blocks themselves
     while ((b = ReverseFind(output.Lower(), ft)) != -1) {
     while ((b = ReverseFind(output.Lower(), ft)) != -1) {
-               // Loop until every #echo directive is found
+               // Loop until every #if directive is found
                // We search from the end of the string so that #if statements will properly recurse
                // and we avoid the hassle of matching statements with the correct <!--#endif-->
                // We search from the end of the string so that #if statements will properly recurse
                // and we avoid the hassle of matching statements with the correct <!--#endif-->
-               int end, c, n;
+        bool notval = false;
+        int off = 0;
+        int end;
         wxString usecode, code;
         wxString cname;
         wxString tag;
         wxString usecode, code;
         wxString cname;
         wxString tag;
@@ -111,29 +395,14 @@ wxString wxIfElsePrep::Process(
                        }
 
         end += 3;
                        }
 
         end += 3;
-        tag = output.Mid(b, end);
+        // remove the <!--#if and --> sections from the tag before passing it on to be parsed
+        tag = output.Mid(b+strlen(ft), end-strlen(ft)-3);
         output.Remove(b, end);
 
         output.Remove(b, end);
 
-        c = tag.Find("-->");
-        n = c;
-
-        // find the classname
-        c = (tag.Mid(8, n-8)).Find(" ");
-           if (c == -1) n -= 8;
-        else n = c;
-        cname = tag.Mid(8, n);
-
-        cname.Trim(false);
-        c = cname.Find("\"");
-        if (c != -1) cname = cname.Mid(c+1);
-       c = cname.Find("\"");
-           if (c != -1) cname = cname.Mid(0, c);
-
-        // Grab the value from the variable class identified by cname
-        value = wxIfElseVariable::FindValue(cname);
+        value = ParseIfStatementValue(tag);
 
         // Find the end of the tag (<!--#endif-->) and copy it all into the variable code
 
         // Find the end of the tag (<!--#endif-->) and copy it all into the variable code
-        end = ((output.Mid(b)).Lower()).Find("<!--#endif-->");
+        end = ((output.Mid(b)).Lower()).Find(ftend);
         if (end == -1) {
 #ifdef CHECKED         
             wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR);
         if (end == -1) {
 #ifdef CHECKED         
             wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR);
@@ -142,14 +411,14 @@ wxString wxIfElsePrep::Process(
                        }
 
         code = output.Mid(b, end);
                        }
 
         code = output.Mid(b, end);
-        output.Remove(b, end+13); // remove the entire #if block from original document
+        output.Remove(b, end+strlen(ftend)); // remove the entire #if block from original document
 
         // Find out if there is an else statement
 
         // Find out if there is an else statement
-        end = (code.Lower()).Find("<!--#else-->");
+        end = (code.Lower()).Find(ftelse);
         if (end != -1) {
             if (!value) {
                 // Use the else statement
         if (end != -1) {
             if (!value) {
                 // Use the else statement
-                usecode = code.Mid(end+12);
+                usecode = code.Mid(end+strlen(ftelse));
                 }
             else {
                 // Use statement before #else
                 }
             else {
                 // Use statement before #else
@@ -168,4 +437,5 @@ wxString wxIfElsePrep::Process(
     return output;
 }
 
     return output;
 }
 
-FORCE_LINK(ifelsevar)                          
+FORCE_LINK(ifelsevar)
+