2002/07/19 04:41:23
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:49:07 +0000 (06:49 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:49:07 +0000 (06:49 +0000)
darcs-hash:20040130064907-2ba56-75bbe992c8a9d06894eb0db5a43f4ad2ce0e30f3.gz

src/org/xwt/builtin/bevel.xwt [new file with mode: 0644]
src/org/xwt/builtin/bevel_2_down.png [new file with mode: 0644]
src/org/xwt/builtin/bevel_2_flat.png [new file with mode: 0644]
src/org/xwt/builtin/bevel_2_up.png [new file with mode: 0644]
src/org/xwt/builtin/button.xwt [new file with mode: 0644]
src/org/xwt/builtin/edit.xwt [new file with mode: 0644]

diff --git a/src/org/xwt/builtin/bevel.xwt b/src/org/xwt/builtin/bevel.xwt
new file mode 100644 (file)
index 0000000..240697f
--- /dev/null
@@ -0,0 +1,26 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL] -->
+<xwt>
+
+    <redirect target="self"/>
+    <template thickness="2" depth="up" hpad="2" vpad="2">
+
+        var bar = 5;
+
+        _depth = function(d) {
+            arguments.cascade(d);
+            sync();
+        }
+
+        var sync = function() {
+            if (thickness > hpad) hpad = thickness;
+            if (thickness > vpad) vpad = thickness;
+
+            if (depth == "up") border = "org.xwt.builtin.bevel_2_up";
+            else if (depth == "down") border = "org.xwt.builtin.bevel_2_down";
+            else if (depth == "flat") border = "org.xwt.builtin.bevel_2_flat";
+            else border = null;
+        }
+
+    </template>
+
+</xwt>
diff --git a/src/org/xwt/builtin/bevel_2_down.png b/src/org/xwt/builtin/bevel_2_down.png
new file mode 100644 (file)
index 0000000..0dd1608
--- /dev/null
@@ -0,0 +1,2 @@
+\89PNG\r
+\1a
diff --git a/src/org/xwt/builtin/bevel_2_flat.png b/src/org/xwt/builtin/bevel_2_flat.png
new file mode 100644 (file)
index 0000000..0dd1608
--- /dev/null
@@ -0,0 +1,2 @@
+\89PNG\r
+\1a
diff --git a/src/org/xwt/builtin/bevel_2_up.png b/src/org/xwt/builtin/bevel_2_up.png
new file mode 100644 (file)
index 0000000..0dd1608
--- /dev/null
@@ -0,0 +1,2 @@
+\89PNG\r
+\1a
diff --git a/src/org/xwt/builtin/button.xwt b/src/org/xwt/builtin/button.xwt
new file mode 100644 (file)
index 0000000..168d313
--- /dev/null
@@ -0,0 +1,52 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL] -->
+<xwt>
+
+    <redirect target="self"/>
+    <preapply name="org.xwt.builtin.bevel"/>
+    <template hot="false">
+
+        var owned = false;
+
+        _holdfrequency = function() { }
+
+        // FIXME: I don't know why I can't use an attribute for this; causes infinite loops
+        holdfrequency = 250;
+
+        __Press1 = function(message) {
+            owned = true;
+            press = true;
+
+            xwt.thread = function() {
+                xwt.sleep(holdfrequency);
+                while (owned) {
+                    if (xwt.button == 1 and mouseinside) hold = true;
+                    xwt.sleep(holdfrequency);
+                }
+            }
+            root._Release1 = function() {
+                if (mouseinside and owned) click = true;
+                owned = false;
+                root.__Release1 = null;
+                release = true;
+            }
+        }
+
+        var owned = false;
+
+        _style = function(s) { arguments.cascade(s); sync_(); }
+
+        _Enter = function() { sync_(); }
+        _Leave = function() { sync_(); }
+        _press = function() { owned = true; sync_(); }
+        _release = function() { owned = false; sync_(); }
+
+        var sync_ = function() {
+            if (mouseinside and owned) depth = "flat";
+            else if (mouseinside) depth = "up";
+            else depth = "up";
+        }
+
+        sync_();
+
+    </template>
+
diff --git a/src/org/xwt/builtin/edit.xwt b/src/org/xwt/builtin/edit.xwt
new file mode 100644 (file)
index 0000000..9e1af61
--- /dev/null
@@ -0,0 +1,479 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL] -->
+<xwt>
+
+    A text edit box.
+
+        multiline : boolean       -- if true, lines will be broken at newline characters
+        editable  : boolean       -- if false, the text cannot be changed, although it can be copied
+
+    <redirect target="self"/>
+
+    <static>
+        var boxen = [];
+        var cr_regexp = /[\n\r]/g;
+        var nonwhitespace = /\S/;
+    </static>
+
+    <template textcolor="black" orient="vertical" vpad="2" editable="true" multiline="false" text="" cursor="text">
+
+        // Structural Stuff //////////////////////////////////////////////////////////////////////
+
+        // Cursor1 is the cursor -- when there is no selection, its width is
+        // 1, and its height is the height of one line. When the user selects
+        // a one-line region, Cursor1 is expanded to cover that region (but
+        // placed behind the text on the z-axis). When the user selects more
+        // than one line, Cursor1 covers the background of the first line of
+        // the selection, Cursor3 covers the last line, and the intermediate
+        // lines get their color set to "blue".
+
+        var curs = $curs;                      // the main cursor
+        var curs2 = $curs2;                    // the "backup" cursor for multiline selections
+        var numcursors = 2;                    // the number of non-textline children of this box
+        var master = 0;
+
+        <box text="" align="topleft"/>
+        <box id="curs" absolute="true" textcolor="white" color="blue" width="1" y="0" x="1" invisible="true"/>
+        <box id="curs2" absolute="true" textcolor="white" color="blue" width="1" y="0" x="1" invisible="true"/>
+
+        // Cursor Manipulation //////////////////////////////////////////////////////////////////////
+
+        // cx1, cy1 is the coordinates (in characters, not pixels) of the start of the selection
+        // cx2, cy2 is the coordinates (in characters, not pixels) of the end   of the selection
+        cx2 = cx1 = cy2 = cy1 = 0;
+
+        // the x-coordinate of the beginning of the selection, in characters
+        _cx1 = function(a) {
+            a = xwt.math.floor(a);
+            if (0 > a) { if (cy1 > 0) cy1--; a = thisbox[cy1].text.length; }
+            if (numchildren - numcursors > cy1 and a > thisbox[cy1].text.length) { cy1++; a = 0; }
+            curs.x = xwt.textwidth(font, thisbox[cy1].text.substring(0, a));
+            arguments.cascade(a);
+            sync();
+        }
+
+        // the x-coordinate of the end of the selection, in characters
+        _cx2 = function(a) {
+            a = xwt.math.floor(a);
+            if (0 > a) { cy2--; a = thisbox[cy2].text.length; }
+            if (numchildren - numcursors > cy2 and a > thisbox[cy2].text.length) { cy2++; a = 0; }
+            arguments.cascade(a);
+            sync();
+        }
+
+        // the y-coordinate of the beginning of the selection, in characters
+        _cy1 = function(a) {
+            a = xwt.math.floor(a);
+            if (0 > a) { a = 0; cx1 = 0; }
+            arguments.cascade(a);
+            if (cx1 > thisbox[a].text.length) cx1 = thisbox[a].text.length;
+            sync();
+        }
+
+        // the y-coordinate of the end of the selection, in characters
+        _cy2 = function(a) {
+            a = xwt.math.floor(a);
+            arguments.cascade(a);
+            while (a >= numchildren - numcursors) {
+                var b = getbox();
+                b.text = "";
+                thisbox[numchildren - numcursors] = b;
+            }
+            if (cx2 > thisbox[a].text.length) cx2 = thisbox[a].text.length;
+            sync();
+        }
+
+        // the x-coordinate of the beginning of the selection, measured in _pixels_
+        __px1 = function() {
+            if (cy1 > numchildren || thisbox[cy1].text == null) return 0;
+            return xwt.textwidth(font, thisbox[cy1].text.substring(0, cx1));
+        }
+
+        // the x-coordinate of the end of the selection, measured in _pixels_
+        __px2 = function() {
+            if (cy2 > numchildren || thisbox[cy2].text == null) return 0;
+            return xwt.textwidth(font, thisbox[cy2].text.substring(0, cx2));
+        }
+
+        // on the r-th row, this will determine how many characters are to the left of a
+        // point b pixels from the left edge of the edit  Algorithm is a binary search,
+        // making educated guesses based on the average width of a character on that line.
+        getboundary = function(r, b) {
+
+            if (o >= b) return 0;
+            var s = thisbox[r].text;
+
+            var left = 0;                                 // the left boundary of the search region, in characters
+            var right = s.length;                         // the right boundary of the search region, in characters
+            var start = 0;                                // the left boundary of the search region, in pixels
+            // the right boundary of the search region, in pixels
+            var end = xwt.textwidth(font, s);
+            if (b >= end) return right;                            // short circuit if we're off the end
+
+            var avgwidth = end / right;                            // average width of one character
+
+            while(true) {
+                var middle = left + (b - start) / avgwidth;        // make a guess at where we should look
+
+                // extra safety guard against infinite loops
+                if (left == right) return left;
+                if (left >= middle) middle = left + 1;
+                if (middle >= right) middle = left - 1;
+
+                start = xwt.textwidth(font, s.substring(0, middle));
+                if (start > b) {
+                    right = middle;
+                } else if (b >= start + xwt.textwidth(font, s.charAt(middle))) {
+                    left = middle;
+                } else {
+                    return middle;
+                }
+            }
+        }
+
+        // returns the row containing the point yp pixels from the top of the widget
+        getrow = function(yp) { return xwt.math.min(numchildren - numcursors - 1, xwt.math.floor(yp / lineheight)); }
+
+        sync = function() {
+            curs.x = px1;
+            curs.y = thisbox[cy1].y; //lineheight * cy1 + thisbox[0].y;
+            curs.width = px2 - px1;
+            if (1 > curs.width) { curs.width = 1; curs.text = ""; }
+            curs2.invisible = true;
+
+            if (cy1 != cy2) {
+                curs.width = width - curs.x;
+                curs2.invisible = false;
+                curs2.x = 0;
+                curs2.y = thisbox[cy2].y; //lineheight * cy2;
+                curs2.height = lineheight;
+                curs2.width = px2;
+            }
+
+            for(var i=0; numchildren - numcursors>i; i++) {
+                if (i>cy1 and cy2>i) { 
+                    thisbox[i].textcolor = "white";
+                    thisbox[i].color = "blue";
+                } else {
+                    thisbox[i].textcolor = "black";
+                    thisbox[i].color = null;
+                }
+            }
+
+            curs.text = (thisbox[cy1] == null || thisbox[cy1].text == null) ? null :
+                            thisbox[cy1].text.substring(getboundary(cy1, curs.x),
+                                                     getboundary(cy1, curs.x + curs.width));
+
+            curs2.text = (thisbox[cy2] == null || thisbox[cy2].text == null) ? null :
+                            thisbox[cy2].text.substring(getboundary(cy2, curs2.x),
+                                                     getboundary(cy2, curs2.x + curs2.width));
+        }
+
+
+
+        // Externally Visible Traps //////////////////////////////////////////////////////////
+
+        _focused = function(f) {
+            if (!f) {
+                if (curs.width == 1) curs.invisible = true;
+            } else {
+                curs.invisible = false;
+            }
+        }
+
+        // returns the currently-selected text
+        __selection = function() {
+            var ret = "";
+            if (cy1 == cy2) {
+                ret += thisbox[cy1].text.substring(cx1, cx2);
+            } else {
+                ret += thisbox[cy1].text.substring(cx1) + "\n";
+                for(var i=cy1 + 1; cy2 > i; i++) ret += thisbox[i].text + "\n";
+                ret += thisbox[cy2].text.substring(0, cx2);
+            }
+            return ret;
+        }
+
+        _font = function(f) {
+            curs.height = curs2.height = lineheight = f == null ? xwt.textheight() : xwt.textheight(f);
+            for(var i=0; numchildren>i; i++) {
+                thisbox[i].font = f;
+                if (numchildren - numcursors > i) thisbox[i].minheight = lineheight;
+            }
+        }
+        font = null;
+
+        _editable = function(e) {
+            if (e) {
+                auto_focus = true;
+                color = "white";
+            } else {
+                auto_focus = false;
+                focused = false;
+                curs.invisible = true;
+                curs2.invisible = true;
+            }
+        }
+
+        _multiline = function(m) {
+            if (m and m != "false") {
+                while(numchildren > numcursors + 1) thisbox[1].thisbox = null;
+                arguments.cascade(true);
+            } else {
+                arguments.cascade(false);
+            }
+        }
+
+        __text = function(t) {
+            if (arguments.length == 0) {
+                var ret = "";
+                for(var i=0; numchildren - numcursors>i; i++) ret = ret + thisbox[i].text + "\n";
+                return ret.substring(0, ret.length - 1); // chop off trailing CR
+            }
+            var me = this;
+            for(var i = numchildren - numcursors - 1; i >= 0; i--) {
+                static.boxen[static.boxen.length] = me[i];
+                me[i].thisbox = null;
+            }
+            cy1 = 0; cy2 = 0; cx1 = 0; cx2 = 0;
+            xwt.thread = function() {
+                try { insert_text(t); } catch (e) { xwt.println(e); }
+            }
+        }
+
+        last_was_a_kill = false;               // indicates if the last "action" was a kill -- lets us know if we
+                                               // we should append newly killed text to the kill buffer or clear it
+
+
+
+        // Text Manipulation /////////////////////////////////////////////////////////////
+
+        // deletes the region between the start and end of the selection
+        var nuke_selection = function() {
+            if (cy1 != cy2 and thisbox[cy1].text != null and thisbox[cy2].text != null) {
+                thisbox[cy1].text = thisbox[cy1].text.substring(0, cx1) + thisbox[cy2].text.substring(cx2);
+                for(var i=cy1 + 1; cy2 >= i; i++) thisbox[cy1 + 1].thisbox = null;
+                cy2 = cy1;
+                cx2 = cx1;
+            } else {
+                thisbox[cy1].text = thisbox[cy1].text.substring(0, cx1) +  thisbox[cy1].text.substring(cx2);
+                cx2 = cx1;
+            }
+        }
+
+        var getbox = function() {
+            if (static.boxen.length == 0) static.boxen[0] = xwt.newBox("box");
+            var ret = static.boxen[static.boxen.length - 1];
+            static.boxen.length--;
+            ret.minheight = lineheight;
+            ret.vshrink = true;
+            ret.textcolor = "black";
+            ret.font = font;
+            ret.align = "topleft";
+            ret.invisible = false;
+            ret.absolute = false;
+            return ret;
+        }
+
+        // nukes the selected region and inserts arg in its place
+        var insert_text = function(arg) {
+            var mine = master = xwt.math.random();
+            if (!multiline) arg = (arg + "").replace(static.cr_regexp, "");
+            if (curs.width > 1) nuke_selection();
+            while(!(arg == null || arg == "" || arg.replace == null)) {
+                cy2 = cy1; cx2 = cx1;
+                // insert a new line if necessary
+                if (arg.charAt(0) == '\n') {
+                    var n = getbox();
+                    thisbox[cy1 + 1] = n;
+                    n.text = thisbox[cy1].text.substring(cx1); 
+                    thisbox[cy1].text = thisbox[cy1].text.substring(0, cx1); 
+                    cy1++; cy2 = cy1; cx1 = 0; cx2 = 0;
+                    arg = arg.substring(1);
+                    xwt.yield();
+                    if (master != mine) return;
+                }
+                var upto = arg.indexOf('\n') == -1 ? arg.length : arg.indexOf('\n');
+                var itext = arg.substring(0, upto);
+                thisbox[cy1].text = thisbox[cy1].text.substring(0, cx1) + itext + thisbox[cy1].text.substring(cx1);
+                cx1 += itext.length;
+                arg = arg.substring(upto);
+            }   
+            cy2 = cy1;
+            cx2 = cx1;
+            master = 0;
+        }
+
+            
+        // XWT Event Handlers //////////////////////////////////////////////////////////////
+
+_Press2 = function() {
+xwt.println("curs.x = " + curs.x);
+xwt.println("curs.y = " + curs.y);
+xwt.println("curs.w = " + curs.width);
+xwt.println("curs.h = " + curs.height);
+xwt.println("curs.i = " + curs.invisible);
+xwt.println("curs[] = " + indexof(curs));
+xwt.println("numchi = " + numchildren);
+}
+
+        _Press1 = function() {
+            focused = true;
+            if (master != 0) return;
+            last_was_a_kill = false;
+
+            root._Move = function() {
+                curs.invisible = false;
+                var mousey_row = getrow(mousey);
+                var pressy_row = getrow(pressy);
+
+                if (pressy_row > mousey_row || (mousey_row == pressy_row and pressx > mousex)) {
+                    cy1 = mousey_row;
+                    cy2 = pressy_row;
+                    cx1 = getboundary(cy1, mousex);
+                    cx2 = getboundary(cy2, pressx);
+
+                } else {
+                    var newcy2 = mousey_row;
+                    if (newcy2 > numchildren - numcursors - 1) newcy2 = numchildren - numcursors - 1;
+                    cy2 = newcy2;
+                    cy1 = pressy_row;
+                    cx2 = xwt.math.min(getboundary(cy2, mousex), thisbox[cy2].text.length);
+                    cx1 = getboundary(cy1, pressx);
+                }
+            }
+
+            root.__Release1 = function() {
+                root._Move = null;
+                root._Release1 = null;
+                if (cx1 != cx2 || cy1 != cy2) xwt.clipboard = selection;
+            }
+
+            pressx = mousex;
+            pressy = mousey;
+            cy1 = getrow(mousey);
+            cy2 = getrow(mousey);
+            cx2 = xwt.math.min(getboundary(cy1, mousex), thisbox[cy1].text.length);
+            cx1 = xwt.math.min(getboundary(cy1, mousex), thisbox[cy1].text.length);
+        }
+
+        __Press3 = function() {
+            last_was_a_kill = false;
+            cy1 = cy2 = getrow(mousey);
+            cx2 = cx1 = xwt.math.min(getboundary(cy1, mousex), thisbox[cy1].text.length);
+            if (xwt.clipboard != null and editable) insert_text(xwt.clipboard);
+        }
+
+        _DoubleClick1 = function() {
+            cy1 = cy2 = getrow(mousey);
+            cx1 = 0; cx2 = thisbox[cy1].text.length;
+            xwt.clipboard = selection;
+        }
+
+        var key_C_Q = function() {
+
+            // C-q => fill
+            var start = cy1;
+            var stop = cy2;
+
+            // if a region wasn't selected, fill this paragraph alone
+            if (cy1 == cy2) {
+                while (start > 0 and thisbox[start].text.match(static.nonwhitespace)) start--;
+                while (numchildren - numcursors > stop and thisbox[stop].text.match(static.nonwhitespace)) stop++;
+            }
+
+            // pull out the text-to-be filled
+            var filltext = "";
+            for(var i=start; stop >= i; i++) {
+                filltext += thisbox[start].text + "\n";
+                if (numchildren > numcursors) {
+                    static.boxen[static.boxen.length] = thisbox[start];
+                    thisbox[start].thisbox = null;
+                } else {
+                    thisbox[start].text = "";
+                }
+            }
+
+            xwt.println("prefill text:\n" + filltext);            
+
+            filltext = filltext.replace(/^\\s+/);
+
+            var quoted = false;
+            if (filltext.substring(0, 2) == "> ") {
+                filltext = filltext.substring(2).replace(/\n> /g, "\n");
+                quoted = true;
+            }
+
+            filltext = filltext.replace(/\s+/g, " ");
+
+            var newfilltext = "";
+
+            while (filltext.length > 0) {
+                var tmp = filltext.substring(0, 78);
+                var tmp2;
+                if (tmp.lastIndexOf(' ') != -1) {
+                    filltext = tmp.substring(tmp.lastIndexOf(' ') + 1) + filltext.substring(78);
+                    tmp2 = tmp.substring(0, tmp.lastIndexOf(' '));
+                } else {
+                    tmp2 = filltext;
+                    filltext = "";
+                }
+                newfilltext += (quoted ? "> " : "") + tmp2 + "\n";  
+            }
+
+            cy1 = start; cy2 = start;
+            cx1 = 0; cx2 = 0;
+
+            xwt.println("newfilltext:\n" + newfilltext);
+
+            insert_text("\n" + newfilltext + "\n");
+         }
+
+        var key_C_W = function() { if (xwt.clipboard != null) xwt.clipboard = selection;
+                                   if (editable) nuke_selection();
+                                 }
+        var key_C_K = function() { if (!editable) return;
+                                   if (1 >= curs.width and cx1 == thisbox[cy1].text.length) cx2++;
+                                   else if (1 >= curs.width) cx2 = thisbox[cy2].text.length;
+                                   if (!last_was_a_kill and xwt.clipboard != null) xwt.clipboard = "";
+                                   if (xwt.clipboard != null) xwt.clipboard += selection;
+                                   else xwt.clipboard = selection;
+                                   nuke_selection();
+                                 }
+
+        _KeyPressed = function(key) {
+            if (master != 0) return;
+            if (!focused) return;
+            var kill = false;
+            if (key == "C-a") { cx1 = 0; cx2 = 0; }
+            else if (key == "C-e") { cx1 = thisbox[cy2].text.length; cx2 = cx1; cy2 = cy1; }
+            else if (key == "C-d" || key == "delete") { if (!editable) return; if (1 >= curs.width) cx2++; nuke_selection(); }
+            else if (key == "C-y") { if (xwt.clipboard != null and editable) insert_text(xwt.clipboard); }
+            else if (key == "C-k") key_C_K();
+            else if (key == "C-w") key_C_W();
+            else if (key == "C-h" || key == "back_space") {
+                if (!editable) return;
+                if (cx1 == 0 and cx2 == 0 and cy1 == 0 and cy2 == 0) return;
+                if (1 >= curs.width) cx1--;
+                nuke_selection();
+            }
+            else if (key == "C-b" || key == "left") { cx1--; cy2 = cy1; cx2 = cx1; }
+            else if (key == "C-p" || key == "up") { cy1--; cy2 = cy1; cx2 = cx1; }
+            else if (key == "C-f" || key == "right") { cx2++; cy1 = cy2; cx1 = cx2; }
+            else if (key == "C-m" || key == "C-o" || key == "enter" || key == "C-j") { if (editable) insert_text("\n"); }
+            else if (key == "C-n" || key == "down") { cy2++; cy1 = cy2; cx1 = cx2; }
+            else if (key == "C-q") key_C_Q();
+            else if (key.length == 1) {
+                if (!editable) return;
+                if (curs.width > 1) nuke_selection();
+                thisbox[cy1].text = thisbox[cy1].text.substring(0, cx1) +
+                                 key +
+                                 thisbox[cy1].text.substring(cx1);
+                cx2 = cx1 + 1; cx1 = cx2;
+            }
+        
+            last_was_a_kill = key == "C-k";
+        }
+    
+    </template>
+</xwt>
+