REBOL [
    Title: "Simple Electronic List of Every Name and Address"
]

;; [---------------------------------------------------------------------------]
;; [ This is a simple name and address list with the addition of the           ]
;; [ ability to store and show a picture.                                      ]
;; [---------------------------------------------------------------------------]

;; [---------------------------------------------------------------------------]
;; [ These items are coded here at the front so you can find and change them   ]
;; [ for your own installation.                                                ]
;; [---------------------------------------------------------------------------]

DATA-FILE-ID: %SELENA-DATA.txt
DATA-PHOTO-FOLDER: %SELENA-PICTURES/
DATA-CSV-ID: %SELENA-DATA.csv

;; [---------------------------------------------------------------------------]
;; [ This is a hard-coded photo of "nobody" that we will put on the screen     ]
;; [ when no data is showing, or when no photo exists for a person.            ]
;; [---------------------------------------------------------------------------]

NOBODY-PHOTO:     
64#{
/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkS
Ew8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJ
CQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy
MjIyMjIyMjIyMjIyMjL/wAARCAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEA
AAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh
MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6
Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ
mqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx
8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA
AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV
YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp
anN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPE
xcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3
GiiigYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFA
BRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRUcsqQxtJI4RVG
STwKAJKrXN9bWa5nmVT2Xua5/UfEckhaOz+ROnmHqawmZnYs7FmPJJOSaBHSz+KI
1JFvbs/oznANUX8S3zH5BGg9Auax6KANT/hINS/57J/37FSJ4lvlPziNx6FcVj0U
AdNB4ojYgXFuyerIcgVsW19bXi5gmVj3XuK4GlVmRgyMVYcgg4IoA9GorltO8RyR
lY7z506eYOorpYpUmjWSNw6sMgjkUDJKKKKACiiigAooooAKKKKACiiigAooooAK
KKKACiiigAooooAKKKKAI5547eFppWCooySa4vU9Ul1GY5ysIPyJU+u6mby4MMbf
uIzgY7msmgQUUUUgCiiigAooooAKKKKACr+mapLp0wxloSfnSqFFAHocE8dxCs0T
BkYZBFSVx2hambO4EMjfuJDg57GuxpjCiiigAooooAKKKKACiiigAooooAKKKKAC
iiigAooooAKy9dvTZ2DBDiSX5V9R61qVx3iK5M+pmMHKxKFA7ZoEZNFFFIAooooA
KKKKACiiigAooooAKKKKACuz0K9N5YKHOZIvlb1PpXGVreHbkwamIycLKpUjtmmB
2NFFFAwooooAKKKKACiiigAooooAKKKKACiiigAooooAQnaCT2Ga8+uZDLdSyE53
OTXfynEMh9Eb+RrzvOefXmgQUUUUgCiiigAooooAKKKKACiiigAooooAKltpDFdR
SA42uDUVGcc+nNAHowO4AjuM0tMiOYYz6ov8hT6YwooooAKKKKACiiigAooooAKK
KKACiiigAooooAZKMwyD/Yb+RrzvGOPSvRWG5WHqpFeeSjbNIvoxFAhtFFFIAooo
oAKKKKACiiigAooooAKKKKACjGePWinRDdNGvqwFAHoUQxDGP9hf5Cn01RtVR6KB
TqYwooooAKKKKACiiigAooooAKKKKACiiigAooooAQdRXB6jH5WpXCdMOT+dd7XH
eI4vL1Vn7SKGzQIyaKKKQBRRRQAUUUUAFFFFABRRRQAUUUUAFWtOj83UrdOuXB/K
qta3hyLzNVV+0als0AdgeppaKKYwooooAKKKKACiiigAooooAKKKKACiiigAoooo
AK5/xRBughnA+4xUn610FVNRthdafND3Kkr+FAHB0Ucg4IwRwRRSEFFFFABRRRQA
UUUUAFFFFABRRRQAV0/heDbBNOR99goP0rmOScAZJ4ArvNOtha6fDD3Cgt+NMC3R
RRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDiNbtPsmpOAMJJ8y1n
12Ov2X2uxMiDMkXzD1I71x1AgooopAFFFFABRRRQAUUUUAFFFFAGholp9r1JARlI
/mau3rJ0Cy+yWIkcYkl+Y+oHatamAUUUUDCiiigAooooAKKKKACiiigAooooAKKK
KACiiigAooooAQgEYPOeoritZsDY3p2j91JlkPp7V21U9RsU1C0aFuGHKN6GgDhK
KfLE8EzRSKQynBBplIQUUUUAFFFFABRRRQAVo6NYG+vRuH7qPDOfX2qjFE88yxRq
SzHAAruNOsU0+0WFeWPLt6mmBbAAGBxjoKWiigYUUUUAFFFFABRRRQAUUUUAFFFF
ABRRRQAUUUUAFFFFABRRRQAUUUUAYPiSyia1+1jiVSFJH8VctXTeJ7kCKK2B+Ync
w9K5mgQUUUUgCiiigAooooA6nw3ZRLa/azzKxKgn+Gt6ue8MXIMUtsT8wO5R610N
MAooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU13VFLOwVR1L
HAoAdVS/v4dPgMkhyx+4g6ms2/8AEcMIaO0Hmv03n7ormZ7iW5laSZy7HqTQIW5u
ZLu4eaU5Zjk+gqKiikAUUUUAFFFFABRRRQBLbXMlpcJNEcMpyPQ129hfw6hAJIzh
h99D1FcHUkFxLbSrJC5Rh0IoA9DorCsPEcMwWO7HlP03j7prbR1dQyMGU9CpyKYx
1FFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFISACTwB1NZl5rtnaZUP5sg/hTkfnQB
qVXub22tATNMqexOTXK3fiC9uMiNhCh7L1/OstmZmJZiSepJyaBHR3ficDK2kWfR
5On5Vh3N9c3jZnlZh2XoBVeikAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVYtr65
s2zBKyjuvUGq9FAHSWnicHC3cWPV4+n5Vt217bXYBhmV/YHBrgKVWZWBViCOhBwa
YHo1FcbaeIL23wJGEyDs3X863rPXbO7wpfypD/C/A/OgDUopAQQCOQehpaBhRRRQ
AUUUUAFUNQ1W305f3jbpD0QdarazrAsV8mEg3DDr1C1yTu0js8jFmJySeSaBF2+1
e6viQz7I+yLwKoUUUgCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKK
KACiiigAooooAv2Or3ViQFffH3RuRXU6fqtvqK/u22yDqh61w9OR2jdXjYqwOQRw
RTA9ForH0bWBfL5MxAuFHXoGrYoGFVr26Wzs5J2/hHyj1NWa53xTORHBADwSWYet
AHOyyvPK0sjZZiSaZRRSEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQA
UUUUAFFFFABRRRQAUUUUAFFFFAD4pXglWWNsMpBFd3ZXS3lnHOv8Q+Yehrga6Xwt
OTHPATwCGUelMDoq5TxST9vgHbys/qa6uuU8U/8AIQh/65f1NAGHRRRSAKKKKACi
iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACt
zwsT9vnHbys/qKw63PC3/IQm/wCuX9RTA//Z
}

;; [---------------------------------------------------------------------------]
;; [ Note the folder we are running from in case we have to "cd" out and       ]
;; [ get back.                                                                 ]
;; [---------------------------------------------------------------------------]

HOME-DIR: what-dir

;; [---------------------------------------------------------------------------]
;; [ The data file will be a block that we load into memory.                   ]
;; [ We will need data when we build the window, so that the "extract"         ]
;; [ function that builds the moniker list will have something to extract.     ]
;; [ So, to accomplish that and to provide a little documentation,             ]
;; [ we will start the data block with one dummy item.  This dummy item        ]
;; [ can be viewed to understand how the program works, and then can be        ]
;; [ deleted when more data is present.                                        ]
;; [ One item in the name-and-address list will be two items in the data       ]
;; [ block.  This first will be a "moniker" which will be a string to          ]
;; [ use to refer to the person.  These items will be in a list on the         ]
;; [ main window, in alphabetical order, so normally one would use a first     ]
;; [ or last name.  These items should be unique because there will be         ]
;; [ some selecting done on them, and if you have duplicates, the second       ]
;; [ never will get selected.  The second item will be a block which will      ]
;; [ contain the data about the person, as shown below.                        ]
;; [---------------------------------------------------------------------------]

DATA-BLOCK: [
    "A-MODEL" [                              ;; code name/nickname/sort code
        "Mister"                             ;; first name
        "John"                               ;; middle name
        "Smith"                              ;; last name
        "100 Main Street"                    ;; address
        "Duluth"                             ;; city
        "MN"                                 ;; state
        "55803"                              ;; postal code
        "111-111-1111"                       ;; home phone
        "222-222-2222"                       ;; work phone
        "333-333-3333"                       ;; cell phone number
        "444-444-4444"                       ;; other phone number
        "This a demo person you may delete"  ;; comment
    ]
]
DATA-RECORD: []  ;; attribute block of address book entry 
DATA-LOAD: does [
    DATA-BLOCK: copy []
    DATA-BLOCK: load DATA-FILE-ID
]
DATA-SAVE: does [
    sort/skip DATA-BLOCK 2
    save DATA-FILE-ID DATA-BLOCK
]
;; Not necessary, but if we copy data items to these words, it will be
;; easier to probe around for debugging.
;; These items will be loaded when we select a record from the data block,
;; they will be loaded with screen data when we click any of the update
;; buttons, they will be blanked out when the screen is cleared. 
;; Note that KEY-MONIKER is not part of the attribute block and that is
;; why the name is KEY-MONITOR and not REC-MONIKER. 
;; The items REC-* are the ones that go into the attribute block of an 
;; item in the DATA-BLOCK
KEY-MONIKER: ""
REC-FIRST-NAME: ""
REC-MIDDLE-NAME: ""
REC-LAST-NAME: ""
REC-ADDRESS: ""
REC-CITY: ""
REC-STATE: ""
REC-POSTAL-CODE: ""
REC-HOME-PHONE: ""
REC-WORK-PHONE: ""
REC-CELL-PHONE: ""
REC-OTHER-PHONE: ""
REC-COMMENT: ""

;; Build the attribute block of an item in DATA-BLOCK.
REC-LOAD: does [
    DATA-RECORD: copy []
    append DATA-RECORD REC-FIRST-NAME
    append DATA-RECORD REC-MIDDLE-NAME
    append DATA-RECORD REC-LAST-NAME
    append DATA-RECORD REC-ADDRESS
    append DATA-RECORD REC-CITY
    append DATA-RECORD REC-STATE
    append DATA-RECORD REC-POSTAL-CODE
    append DATA-RECORD REC-HOME-PHONE
    append DATA-RECORD REC-WORK-PHONE
    append DATA-RECORD REC-CELL-PHONE
    append DATA-RECORD REC-OTHER-PHONE
    append DATA-RECORD REC-COMMENT
]

;; Unload the attribute block of an item in DATA-BLOCK
REC-UNLOAD: does [
    REC-FIRST-NAME: copy ""
    REC-MIDDLE-NAME: copy ""
    REC-LAST-NAME: copy ""
    REC-ADDRESS: copy ""
    REC-CITY: copy ""
    REC-STATE: copy ""
    REC-POSTAL-CODE: copy ""
    REC-HOME-PHONE: copy ""
    REC-WORK-PHONE: copy ""
    REC-CELL-PHONE: copy ""
    REC-OTHER-PHONE: copy ""
    REC-COMMENT: copy ""
    REC-FIRST-NAME: copy DATA-RECORD/1
    REC-MIDDLE-NAME: copy DATA-RECORD/2
    REC-LAST-NAME: copy DATA-RECORD/3
    REC-ADDRESS: copy DATA-RECORD/4
    REC-CITY: copy DATA-RECORD/5
    REC-STATE: copy DATA-RECORD/6
    REC-POSTAL-CODE: copy DATA-RECORD/7
    REC-HOME-PHONE: copy DATA-RECORD/8
    REC-WORK-PHONE: copy DATA-RECORD/9
    REC-CELL-PHONE: copy DATA-RECORD/10
    REC-OTHER-PHONE: copy DATA-RECORD/11
    REC-COMMENT: copy DATA-RECORD/12
]

REC-CLEAR: does [
    REC-FIRST-NAME: copy ""
    REC-MIDDLE-NAME: copy ""
    REC-LAST-NAME: copy ""
    REC-ADDRESS: copy ""
    REC-CITY: copy ""
    REC-STATE: copy ""
    REC-POSTAL-CODE: copy ""
    REC-HOME-PHONE: copy ""
    REC-WORK-PHONE: copy ""
    REC-CELL-PHONE: copy ""
    REC-OTHER-PHONE: copy ""
    REC-COMMENT: copy ""
] 

SET-SCREEN: does [
    set-face DATA-MONIKER KEY-MONIKER
    set-face DATA-FIRST-NAME REC-FIRST-NAME
    set-face DATA-MIDDLE-NAME REC-MIDDLE-NAME
    set-face DATA-LAST-NAME REC-LAST-NAME
    set-face DATA-ADDRESS REC-ADDRESS
    set-face DATA-CITY REC-CITY
    set-face DATA-STATE REC-STATE
    set-face DATA-POSTAL-CODE REC-POSTAL-CODE
    set-face DATA-HOME-PHONE REC-HOME-PHONE
    set-face DATA-WORK-PHONE REC-WORK-PHONE
    set-face DATA-CELL-PHONE REC-CELL-PHONE
    set-face DATA-OTHER-PHONE REC-OTHER-PHONE
    set-face DATA-COMMENT REC-COMMENT
]

GET-SCREEN: does [
    KEY-MONIKER: trim  get-face DATA-MONIKER
    REC-FIRST-NAME: trim  get-face DATA-FIRST-NAME
    REC-MIDDLE-NAME: trim  get-face DATA-MIDDLE-NAME
    REC-LAST-NAME: trim  get-face DATA-LAST-NAME
    REC-ADDRESS: trim  get-face DATA-ADDRESS
    REC-CITY: trim  get-face DATA-CITY
    REC-STATE: trim  get-face DATA-STATE
    REC-POSTAL-CODE: trim  get-face DATA-POSTAL-CODE
    REC-HOME-PHONE: trim  get-face DATA-HOME-PHONE
    REC-WORK-PHONE: trim  get-face DATA-WORK-PHONE
    REC-CELL-PHONE: trim  get-face DATA-CELL-PHONE
    REC-OTHER-PHONE: trim  get-face DATA-OTHER-PHONE
    REC-COMMENT: trim  get-face DATA-COMMENT
]

;; Create a new item in the list, only after we have determined that
;; an item with this moniker is not present.
ADD-NEW-ITEM: does [
    append DATA-BLOCK KEY-MONIKER
    REC-LOAD
    append/only DATA-BLOCK DATA-RECORD
]

;; [---------------------------------------------------------------------------]
;; [ Since a photo is optional, we will have this separate procedure for       ]
;; [ putting a photo on the screen, or clearing the photo on the screen.       ]
;; [ A photo will be in a special folder, and the name will be:                ]
;; [     .jpg                                                         ]
;; [ This allows for just one photo.  That is a feature not a bug.             ]
;; [ If you want more you could modify the program to suite yourself.          ]
;; [ We will show the photo for whatever value is in KEY-MONIKER,              ]
;; [ and since we have a hard-coded blank photo, this procedure will work      ]
;; [ if KEY-MONIKER has a valid value or has nothing.                          ]
;; [---------------------------------------------------------------------------]

SHOW-PHOTO: does [
    PHOTO-FILE-ID: to-file rejoin [
       DATA-PHOTO-FOLDER
       KEY-MONIKER
       ".jpg"
    ]
    either exists? PHOTO-FILE-ID [
        DATA-PHOTO-IMAGE/image: load PHOTO-FILE-ID
    ] [
        DATA-PHOTO-IMAGE/image: load NOBODY-PHOTO
    ]
    show DATA-PHOTO-IMAGE
]

;; [---------------------------------------------------------------------------]
;; [ This function is performed when a name is selected from the list.         ]
;; [ MONIKER-LIST/picked will be the selected name.  Use it with the           ]
;; [ "select" function to find the data items.                                 ]
;; [---------------------------------------------------------------------------]

SHOW-SELECTED-PERSON: does [
    KEY-MONIKER: MONIKER-LIST/picked
    either DATA-RECORD: select DATA-BLOCK KEY-MONIKER [
        REC-UNLOAD
        SET-SCREEN 
        SHOW-PHOTO
    ] [
        alert rejoin [
            "Error: '"
            KEY-MONIKER
            "' is not in the data"
        ]
        exit
    ]
]

;; [---------------------------------------------------------------------------]
;; [ "Add/Update" button.                                                      ]
;; [ An item in the file is identified by the moniker.                         ]
;; [ We will take advantage of that to simplify the programming.               ]
;; [ If the moniker is present in the file, update that item.                  ]
;; [ If the moniker is not present, add the item.                              ]
;; [ This way, we need just one button, not separate buttons to add            ]
;; [ something new or change something that exists.                            ]
;; [ Remember that every address book entry is identified uniquely by a        ]
;; [ user-supplied key, which we refer to as the "moniker."                    ]
;; [ When we are updating existing data, we could examine each field from      ]
;; [ the screen and update only the data items that have been changed,         ]
;; [ but why suffer.  We will just assume that the operator wants the          ]
;; [ address book entry to be what is visible on the screen, and we will       ]
;; [ replace the attribute block of the address book item with a new block     ]
;; [ assembled from what is on the screen.  So one must be careful.            ]
;; [ If you blank out a field on the screen, you will blank out the data       ]
;; [ in the file.                                                              ]
;; [ What should we do for confirmation?  Personal preference.  For now,       ]
;; [ we will blank the data entry fields and refresh the moniker list.         ]
;; [---------------------------------------------------------------------------]

ADD-OR-UPDATE-BUTTON: does [
    GET-SCREEN
    if equal? KEY-MONIKER "" [
        alert "Moniker field must be filled in"
        exit
    ]
    either UPDATE-POSITION: find DATA-BLOCK KEY-MONIKER [
        UPDATE-POSITION: next UPDATE-POSITION ;; the attribute block
        REC-LOAD  ;; build a new attribute block
        change/only UPDATE-POSITION DATA-RECORD
        DATA-SAVE
        REC-CLEAR
        KEY-MONIKER: copy ""
        SET-SCREEN
        MONIKER-LIST/data: extract DATA-BLOCK 2
        show MONIKER-LIST
        SHOW-PHOTO
    ] [
        ADD-NEW-ITEM
        DATA-SAVE 
        REC-CLEAR
        KEY-MONIKER: copy ""
        SET-SCREEN
        MONIKER-LIST/data: extract DATA-BLOCK 2
        show MONIKER-LIST
        SHOW-PHOTO
    ]
]

;; [---------------------------------------------------------------------------]
;; [ "Delete" button.                                                          ]
;; [ Delete the item represented by the moniker on the screen, if it is        ]
;; [ present.  Remember, the operator could have changed the moniker in        ]
;; [ which case we would not find it in DATA-BLOCK.                            ]
;; [ What the "DELETE-POSITION: find DATA-BLOCK KEY-MONIKER" does is find      ]
;; [ the moniker and create a reference to it in DELETE-POSITION, which may    ]
;; [ be thought of as a sub-series of DATA-BLOCK starting at the position      ]
;; [ where we found the moniker.  Therefore, we can use the "remove"           ]
;; [ function to remove the first item of DELETE-POSITION and then remove      ]
;; [ again to remove the new first item, which will be the attribute block     ]
;; [ after the moniker, and we will have deleted and item from DATA-BLOCK.     ]
;; [ We will have removed the item from DATA-BLOCK because DELETE-POSITION     ]
;; [ was a reference to a spot in DATA-BLOCK and not any sort of copy of       ]
;; [ the data we found in DATA-BLOCK.                                          ]
;; [---------------------------------------------------------------------------]

DELETE-BUTTON: does [
    GET-SCREEN
    if equal? KEY-MONIKER "" [
        alert "Nothing selected to delete"
        exit
    ]
    either DELETE-POSITION: find DATA-BLOCK KEY-MONIKER [
        remove DELETE-POSITION  ;; moniker
        remove DELETE-POSITION  ;; attriubte block
        DATA-SAVE
        REC-CLEAR
        KEY-MONIKER: copy ""
        SET-SCREEN
        MONIKER-LIST/data: extract DATA-BLOCK 2
        show MONIKER-LIST
        SHOW-PHOTO
    ] [
        alert rejoin [
            "Moniker '"
            KEY-MONIKER
            "' not on file"
        ]
        exit
    ]
]

;; [---------------------------------------------------------------------------]
;; [ "Clear" button.                                                           ]
;; [ Clear the window by clearing the REC-* data and then refreshing the       ]
;; [ window from it.                                                           ]
;; [---------------------------------------------------------------------------]

CLEAR-BUTTON: does [
    KEY-MONIKER: copy ""
    REC-CLEAR 
    SET-SCREEN 
    SHOW-PHOTO
]

;; [---------------------------------------------------------------------------]
;; [ "Quit" button.                                                            ]
;; [ Make sure everything is saved and tidied and so on.                       ]
;; [---------------------------------------------------------------------------]

QUIT-BUTTON: does [
    quit
]

;; [---------------------------------------------------------------------------]
;; [ "Export CSV" button.                                                      ]
;; [ Copy the DATA-BLOCK to a csv file so the operator can use the data        ]
;; [ elsewhere, perhaps in a spreadsheet.                                      ]
;; [---------------------------------------------------------------------------]

EXPORT-CSV-BUTTON: does [
    CSV-FILE: copy ""
    append CSV-FILE rejoin [
        "MONIKER,"
        "FIRSTNAME,"
        "MIDDLENAME,"
        "LASTNAME,"
        "ADDRESS,"
        "CITY,"
        "STATE,"
        "POSTALCODE,"
        "HOMEPHONE,"
        "WORKPHONE,"
        "CELLPHONE,"
        "OTHERPHONE," 
        "COMMENT"
        newline
    ]
    foreach [MONIKER ATTRIBUTE-BLOCK] DATA-BLOCK [
        append CSV-FILE MONIKER
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/1
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/2
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/3
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/4
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/5
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/6
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/7
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/8
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/9
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/10
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/11
        append CSV-FILE ","
        append CSV-FILE ATTRIBUTE-BLOCK/12
        append CSV-FILE ","
        append CSV-FILE newline
    ]
    write DATA-CSV-ID CSV-FILE
    alert rejoin [
        "Data exported to "
        DATA-CSV-ID
    ]
]

;; [---------------------------------------------------------------------------]
;; [ Main window and its component panes.                                      ]
;; [---------------------------------------------------------------------------]

MONIKER-PANE: [
    MONIKER-LIST: text-list 150x700 data (extract DATA-BLOCK 2)
        [SHOW-SELECTED-PERSON]
]

DATA-PANE: [
    size 300x700
    across
    lab 100 "Moniker" font [color: red]
    DATA-MONIKER: field 300
    return 
    lab 100 "First name"
    DATA-FIRST-NAME: field 300
    return
    lab 100 "Middle name"
    DATA-MIDDLE-NAME: field 300
    return
    lab 100 "Last name"
    DATA-LAST-NAME: field 300
    return
    lab 100 "Address"
    DATA-ADDRESS: field 300
    return
    lab 100 "City"
    DATA-CITY: field 300
    return
    lab 100 "State"
    DATA-STATE: field 300
    return
    lab 100 "Postal code"
    DATA-POSTAL-CODE: field 300
    return 
    lab 100 "Home phone"
    DATA-HOME-PHONE: field 300
    return 
    lab 100 "Work phone"
    DATA-WORK-PHONE: field 300
    return 
    lab 100 "Cell phone"
    DATA-CELL-PHONE: field 300
    return 
    lab 100 "Other phone"
    DATA-OTHER-PHONE: field 300
    return
    lab 100 "Comment"
    DATA-COMMENT: area 300x50
    return
    lab 100 "Photo"
    DATA-PHOTO-IMAGE: image 300x300 (load NOBODY-PHOTO) effect [aspect] 
]

MAIN-WINDOW: layout [
    across
    banner "Simple Electronic List of Every Name and Address"
    return
    panel 170x720 MONIKER-PANE
    panel 320x729 DATA-PANE
    return
    box 555x4 red
    return
    button 180 "Add or update shown" [ADD-OR-UPDATE-BUTTON]
    button 180 "Delete item shown"  [DELETE-BUTTON]
    button 180 "Clear input fields" [CLEAR-BUTTON]
    return
    button "Quit" [QUIT-BUTTON]
    button "Debug" [halt]
    button "Export to CSV" [EXPORT-CSV-BUTTON]
]

if exists? DATA-FILE-ID [
    DATA-LOAD
    MONIKER-LIST/data: extract DATA-BLOCK 2
]
if not exists? DATA-PHOTO-FOLDER [
    make-dir DATA-PHOTO-FOLDER
]

view center-face MAIN-WINDOW