Home   Archive   Permalink



I cancel and action in a script, but it happens anyway. Why?

Hi! I was playing with a script inspired on work by Nick for a "card file" and came to the following situation: I cancel and edit but the edit happens anyway.
    
The scripts creates four fields with the names: n, a, p, o; and a "save" button. It also has a block called "database" where the info is stored. When the button is clicked, the script checks to see if there is any content in n/text and then checks to see if the content of n/text is not already in the database. If it is, it offers a request to confirm the changes or it exits the action block of the button.
    
The thing is, if I hit cancel in the request window, apparently noting happens but the database block is changed anyway. I say that apparently nothing happens because I inserted print statements to confirm conditional code flow (for example inside the if/else blocks and on top of the first append. And they don't print, so the blocks were not executed) but the change happens anyway.
    
Here are relevant excerpts of the code (and they reproduce the problem):
    
R E B O L []
database: copy []
    
view center-face gui: layout [
    
    tln: text-list data sort (extract database 4) [
    if value = none [return]
    marker: index? find database value
    n/text: pick database marker
    a/text: pick database marker + 1
    p/text: pick database marker + 2
    o/text: pick database marker + 3
    show gui
         ]
text "Name"    n: field
text "Address"    a: field
text "Phone"    p: field
text "Notes"    o: field
btn "save" [
    if n/text = "" [alert "You must enter at least a name" return]
    if find database n/text [
     if/else true = request rejoin ["Are you sure you want to edit " n/text "?"] [
        remove/part find database n/text 4 ] [return]
     ]
    append database form n/text
    append database form a/text
    append database form p/text
    append database form o/text
    tln/data: sort (extract copy database 4)
    
    n/text = ""
    a/text = ""
    p/text = ""
    o/text = ""
    
    show gui
    ]
]
    
If I select a name in the text-list and change -say- the address info, for example, when I press "save" I'm asked if I want to edit the record. If I press “cancel”, load another record and then return to the record I attempted to change, the edit is there. Does anybody know why?

posted by:   brother damian     10-Oct-2017/2:53:41-7:00



Wow, that's a really old piece of code. Thank you for the heads up. One option is to add 'copy when setting the text properties of the fields to values in the 'database block (otherwise the text displayed in those field widgets is bound to the actual values in the block (that's why the cancel button doesn't stop the "saving" routine - the value in the database and the value in the text field are one and the same at that point)):
    
R E B O L []
database: copy []
view center-face gui: layout [
     tln: text-list data sort (extract database 4) [
         if value = none [return]
         marker: index? find database value
         n/text: copy pick database marker
         a/text: copy pick database marker + 1
         p/text: copy pick database marker + 2
         o/text: copy pick database marker + 3
         show gui
     ]
     text "Name"     n: field
     text "Address" a: field
     text "Phone"    p: field
     text "Notes"    o: field
     btn "save" [
         if n/text = "" [alert "You must enter at least a name" return]
         if find database n/text [
             either request rejoin ["Are you sure you want to edit " n/text "?"] [
                 remove/part find database n/text 4
             ][return]
         ]
         append database form n/text
         append database form a/text
         append database form p/text
         append database form o/text
         tln/data: sort (extract database 4)
         set-face n ""
         set-face a ""
         set-face p ""
         set-face o ""
         show gui
     ]
]

posted by:   Nick     10-Oct-2017/9:18:46-7:00



Note that 'get-face and 'set-face are now preferred (I wrote that original code long enough ago that that wasn't the case (maybe 17ish years ago...)). Those functions handle the redrawing of the GUI face, copying of data values, etc., in ways that you'd expect intuitively:
    
R E B O L []    
database: copy []
view center-face gui: layout [
     tln: text-list data sort (extract database 4) [
         if value = none [return]
         marker: index? find/skip database value 4
         cnt: 0 foreach widget [n a p o] [set-face get widget pick database marker + ++ cnt]
     ]
     text "Name"     n: field
     text "Address" a: field
     text "Phone"    p: field
     text "Notes"    o: field
     btn "save" [
         if n/text = "" [alert "You must enter at least a name" return]
         if find/skip database n/text 4 [
             either request rejoin ["Are you sure you want to edit " n/text "?"] [
                 remove/part find/skip database n/text 4 4
             ][return]
         ]
         foreach widget [n a p o] [
             append database get-face get widget
             set-face get widget ""
         ]
         tln/data: sort (extract database 4) show tln
     ]
]

posted by:   Nick     10-Oct-2017/9:48:05-7:00



Notice also the 'find/skip in the example above to ensure the search only checks the first field in each record.

posted by:   Nick     10-Oct-2017/10:01:10-7:00



I'd prefer to demonstrate this example from http://re-bol.com/short_rebol_examples.r :
    
R E B O L [title: "Generic CRUD Data Card File App - less densely coded"]
clear-form: does [foreach i [a b c] [set-face get i""] focus a]
save-form: does [
    delete-form repend d [a/text b/text c/text] save %d d clear-form
]
edit-form: does [attempt [
    r: copy/part (find/skip d (request-list"" extract d 3)3)3
    repeat i length? j: [a b c] [set-face get j/:i r/:i]
]]
delete-form: does [remove-each [i j k] d [i = a/text] save %d d]    
write/append %d ""    
d: load %d
view g: layout[
    a: field b: field c: area
    across
    btn "New" [clear-form]
    btn "Save" [save-form]
    btn "Edit" [edit-form]
    btn "Delete" [if request "Really Delete?" [delete-form clear-form]]
    btn "Raw" [editor %d]
]
    
Or the shorter version:
    
n: [foreach i[a b c][set-face get i""]focus a] s: [save %d d alert"ok"]
x: [do z repend d[a/text b/text c/text] save %d d do n]    
y: [attempt[r: copy/part(find/skip d (request-list"" extract d 3)3)3
    repeat i length? j:[a b c][set-face get j/:i r/:i]]]
z: [remove-each[i j k]d[i = a/text] do s] write/append %d "" d: load %d
view g: layout[a: field b: field c: area across btn"New"[do n]
    btn"Save"[do x] btn"Edit"[do y] btn"Delete"[do z do n]btn"Raw"[editor %d]
]

posted by:   Nick     10-Oct-2017/9:21:45-7:00



Take a look at this grid example, slightly adjusted from http://re-bol.com/shorter_examples.r :
    
R E B O L []
s: 0 asc: 1 x: [[1 "John" "1 Street Road" "555-9876" "Likes cake"]]
edit: func [f] [f/text: request-text/default f/text show l]
srt: func [c] [sort/compare x func [a b] [(at a c)< at b c]
    if asc: not asc [reverse x] show l]
view g: layout [
    style t text 91x24 edge [size: 2x2][edit face] across space 0x0
    h4 "ID" 55 [srt 1] h4 "Name" 91 [srt 2] h4 "Address" 91 [srt 3]    
    h4 "Phone" 91 [srt 4] h4 "Notes" 117 [srt 5] return
    l: list 450x412 [across space 0x0 info 55 tan t t t t 117] supply [
     face/text: either e: pick x count: count + s [e/:index] [none]
    ] sl: slider 20x412 [s: value * length? x show l] return
    btn "Add"[y: maximum-of x repend/only x[1 + y/1/1 cp"" cp"" cp"" cp""]show l]
    btn "Delete" [r: do request-text/title"#" remove-each i x[i/1 = r]show l]
    btn "Find"[u: cp[] h: request-text foreach i x[if find v: form i h[append
     u v]] if not f: request-list""u[exit]s:(do first parse f"")- 1 show l]
    btn "Save" [attempt [save request-file/only/save/file %x.txt x]]
    btn "Load" [attempt [x: load request-file/only/file %x.txt show l]]
]
    
That's often a preferable solution which demonstrates even more capabilities, and provides a generic interface which can be easily adjusted for lots of use cases (take a look a the original example at the link above, and the longer variation at http://re-bol.com/data-management-apps-with-rebol.html#section-8 )

posted by:   Nick     10-Oct-2017/12:11:51-7:00



Wow Nick, you just blew my mind! First, thanks for providing an explanation. It makes perfect sense now. Second, thanks for the alternative code. Is this still Rebel 2? I have to admit I've never seen something like this and it shows that I have a lot of studying to do. So thanks for the references also.
    
Can you show me how does this work?
cnt: 0 foreach widget [n a p o] [set-face get widget pick database marker + ++ cnt]
     ]
    
And this?
    
repeat i length? j: [a b c] [set-face get j/:i r/:i]
    
Sorry to pick your brain like this, I'm just mesmerized.

posted by:   Brother Damian     11-Oct-2017/2:46:16-7:00



During the 'foreach loop, the iterator refers to word values ('n 'a 'p and 'o, in the first example). Those word values are simply that - just unbound words - the 'get function gets the value associated with those words (face objects created by the GUI dialect, in this context). So in the first example, 'set-face is used to set the text of each of the faces represented by the words 'n 'a 'p and 'o, to values picked from the 'database block. The ++ function increments the value of the word, AFTER returning the current value of the word.
    
>> x: 4
== 4
>> 3 + ++ x
== 7
>> x
== 5
>>
    
The j/:i notation in the second example is the same as "pick j i"
    
This is about as convoluted as "normal" Rebol syntax gets - not hard once you get familiar with it, still very concise, and useful in a wide variety of cases. Once you get used to some syntax and the idea of ubiquitous series structures, how to use data types, etc., Rebol's approach becomes consistent across many problem domains (especially when combined with the ability to define dialects, which is baked into the core feature set of the language).

posted by:   Nick     11-Oct-2017/5:12:30-7:00



Got it, thanks. I even seem to be able to read this code now, even the denser one.
    
One last thing Nick: in some lines there are empty strings that are not separated from other arguments, like
    
Set-face get i""
    
Or
    
Request-list"" extract d 3
    
How does Rebol know that the quotes are a parameter and not a word that ends with the double quotes, as in 'i""?
    
A million thanks

posted by:   Brother Damian     12-Oct-2017/0:37:37-7:00



Double quotes are one of several characters which don't need to be separated by white space (parentheses are another).
    
>> length? [i""]
== 2

posted by:   Nick     12-Oct-2017/4:27:23-7:00



That's one of the things I've done in the super short examples to keep from having to add many extra lines (along with one letter words, etc.). In production code I use more readable style, descriptive words, etc.

posted by:   Nick     12-Oct-2017/4:34:37-7:00