REBOL [
    Title: "NORA: NOte Recording Assistant"
]

;; [---------------------------------------------------------------------------]
;; [ This program was created for a customer base of one person, SWW.          ]
;; [ It is a replacement for all the quarter-sheets of paper scattered         ]
;; [ around his desk.  It also is a learning tool for REBOL, and a demo        ]
;; [ for the office of what a REBOL program looks like.                        ]
;; [---------------------------------------------------------------------------]

;; [---------------------------------------------------------------------------]
;; [ The code below was at one time a separate module created for the          ]
;; [ recording of little notes.  It has been pasted in here to make this       ]
;; [ program one file instead of several.  The code below could be extracted   ]
;; [ and put back into a separate module if one had a use for it elsewhere.    ]
;; [---------------------------------------------------------------------------]

;; [---------------------------------------------------------------------------]
;; [ Note management module.                                                   ]
;; [---------------------------------------------------------------------------]

NOTE-FILE-ID: %lsop.txt

;; [---------------------------------------------------------------------------]
;; [ When we create an empty file, we will write it out as text, instead of    ]
;; [ saving it as a block.  The reason for this is so that we can control      ]
;; [ the look of the file, so it can be examined visually with greater ease.   ]
;; [---------------------------------------------------------------------------]

NOTE-EMPTY-FILE: "[]"
NOTE-HIGHEST-ID: 0
NOTE-NOTES: []
NOTE-CREATE-FILE: does [
    write NOTE-FILE-ID NOTE-EMPTY-FILE
]

;; [---------------------------------------------------------------------------]
;; [ The file, on disk, is going to look like this:                            ]
;; [                                                                           ]
;; [  [    ]       ]
;; [  [    ]       ]
;; [ ...                                                                       ] 
;; [  [    ]       ]
;; [ ]                                                                         ]
;; [                                                                           ]
;; [ Notice how the ID numbers are outside the brackets that hold the          ]
;; [ data for each scrap.  This is so the scrap numbers are items in the       ]
;; [ NOTE-NOTES block and we can search for them.                              ]
;; [                                                                           ]
;; [ Below are holding areas for the data for one scrap, either one            ]
;; [ we have read or one we are about to create.  This is like the             ]
;; [ "record area" for the file.                                               ]
;; [ The item called REC-ID will do double duty.                               ]
;; [ It will be the scrap number of an item we have read, or, if it is         ]
;; [ zero, it will indicate that this is a new record.                         ]
;; [---------------------------------------------------------------------------]

NOTE-REC-ID: 0
NOTE-REC-DATE: none
NOTE-REC-TITLE: ""
NOTE-REC-TEXT: ""

;; [---------------------------------------------------------------------------]
;; [ This is where we will build the block that goes after a NOTE-ID.          ]
;; [---------------------------------------------------------------------------]

NOTE-REC-BLOCK: []

;; [---------------------------------------------------------------------------]
;; [ This procedure "opens" the file by bringing it into memory.               ]
;; [ Because the data is stored in a REBOL-recognizable format,                ]
;; [ we use the "load" command.                                                ]
;; [ When we store a note, we give it a number ID so we can identify it.       ]
;; [ We store notes so that the most recent is first.                          ]
;; [ Therefore, we can grab the number of the first one and store it as        ]
;; [ the highest-assigned number.  If we happen to delete the top note,        ]
;; [ we will end up re-using the number, which is OK.                          ]
;; [---------------------------------------------------------------------------]

NOTE-OPEN: does [
    NOTE-NOTES: copy []
    NOTE-NOTES: load NOTE-FILE-ID
    either (0 = length? NOTE-NOTES) [
        NOTE-HIGHEST-ID: 0
    ] [
        NOTE-HIGHEST-ID: first NOTE-NOTES
    ]
]

;; [---------------------------------------------------------------------------]
;; [ This procedure "closes" the file by formatting the data into a            ]
;; [ somewhat-nice-looking form and writing it to disk.                        ]
;; [---------------------------------------------------------------------------]

NOTE-FILE: ""
NOTE-CLOSE: does [
    NOTE-FILE: copy ""
    NOTE-NOTES: head NOTE-NOTES
    forskip NOTE-NOTES 2 [
        append NOTE-FILE first NOTE-NOTES
        append NOTE-FILE " "
        append/only NOTE-FILE mold second NOTE-NOTES
        append NOTE-FILE newline
    ]
    write NOTE-FILE-ID NOTE-FILE
]

;; [---------------------------------------------------------------------------]
;; [ This is a common procedure to format the block that contains the          ]
;; [ note data.                                                                ]
;; [ The note data is a block.                                                 ]
;; [---------------------------------------------------------------------------]

NOTE-FORMAT-BLOCK: does [
    NOTE-REC-DATE: now/date
    NOTE-REC-BLOCK: copy []
    append NOTE-REC-BLOCK NOTE-REC-DATE
    append NOTE-REC-BLOCK NOTE-REC-TITLE
    append NOTE-REC-BLOCK NOTE-REC-TEXT
]   

;; [---------------------------------------------------------------------------]
;; [ This is the procedure to add a new record.                                ]
;; [ The caller should fill in the items in the "record area" noted above.     ]
;; [ Do not bother with the record ID because a new one will be generated,     ]
;; [ one greater than the highest used.                                        ]
;; [ Do not bother either with the date, since that is generated also.         ]
;; [ Note that we add data to the front of the NOTE-NOTES block, so that       ]
;; [ when we show a list of existing notes, the most recent will be on the     ]
;; [ top.                                                                      ] 
;; [---------------------------------------------------------------------------]

NOTE-ADD-RECORD: does [
    NOTE-HIGHEST-ID: NOTE-HIGHEST-ID + 1
    NOTE-REC-ID: NOTE-HIGHEST-ID
    NOTE-FORMAT-BLOCK
    NOTE-NOTES: head NOTE-NOTES
    insert/only NOTE-NOTES NOTE-REC-BLOCK
    NOTE-NOTES: head NOTE-NOTES
    insert NOTE-NOTES NOTE-REC-ID
]

;; [---------------------------------------------------------------------------]
;; [ This is the procedure for updating an existing record.                    ]
;; [ The caller should fill in the record area as for ADD-RECORD, but          ]
;; [ this time DO fill in a record ID.  Normally the record ID will be         ]
;; [ filled in from a "read" operation.                                        ]
;; [ This procedure will find the record with the given ID and update the      ]
;; [ data block for that ID.                                                   ]
;; [---------------------------------------------------------------------------]

NOTE-STORE-OK: false
NOTE-STORE-RECORD: does [
    NOTE-NOTES: head NOTE-NOTES
    NOTE-TEMP: select NOTE-NOTES NOTE-REC-ID
    either NOTE-TEMP [
        NOTE-STORE-OK: true
        NOTE-FORMAT-BLOCK
        change NOTE-TEMP NOTE-REC-BLOCK
    ] [
        NOTE-STORE-OK: false
    ]
]
    
;; [---------------------------------------------------------------------------]
;; [ This procedure deletes a record identified by the contents of             ]
;; [ NOTE-REC-ID.                                                              ]
;; [---------------------------------------------------------------------------]

NOTE-DELETE-OK: false
NOTE-DELETE-RECORD: does [
    NOTE-NOTES: head NOTE-NOTES
    NOTE-TEMP: find NOTE-NOTES NOTE-REC-ID
    either NOTE-TEMP [
        NOTE-DELETE-OK: true
        remove NOTE-TEMP   ; Delete the record number     
        remove NOTE-TEMP   ; Delete the data block
    ] [
        NOTE-DELETE-OK: false
    ]
]

;; [---------------------------------------------------------------------------]
;; [ This procedure reads a record identified by the NOTE-REC-ID.              ]
;; [---------------------------------------------------------------------------]

NOTE-READ-OK: false
NOTE-READ-RECORD: does [
    NOTE-NOTES: head NOTE-NOTES
    NOTE-FOUND: find NOTE-NOTES NOTE-REC-ID
    either NOTE-FOUND [
        NOTE-READ-OK: true
        NOTE-UNLOAD-RECORD
    ] [ 
        NOTE-READ-OK: false
    ]
]

;; [---------------------------------------------------------------------------]
;; [ This procedure unloads the data from NOTE-NOTES, pointed to by the        ]
;; [ word NOTES-FOUND, into the "record area."                                 ]
;; [ In normal use, after you read a record, you would NOT ALTER the           ]
;; [ NOTE-REC-ID field, because you might later want to update the             ]
;; [ record identified by that field.                                          ]
;; [---------------------------------------------------------------------------]

NOTE-UNLOAD-RECORD: does [
    NOTE-REC-ID: 0
    NOTE-REC-DATE: none
    NOTE-REC-TITLE: copy ""
    NOTE-REC-TEXT: copy ""
    NOTE-REC-ID: first NOTE-FOUND
    NOTE-REC-DATE: first second NOTE-FOUND
    NOTE-REC-TITLE: second second NOTE-FOUND
    NOTE-REC-TEXT: third second NOTE-FOUND
]

;; [---------------------------------------------------------------------------]
;; [ This procedure builds a list of note titles, each in a string             ]
;; [ consisting of the ID, date, and title, seperated by colons.               ]
;; [ The purpose of this list is to provide data for a screen, in some other   ]
;; [ program or module.                                                        ]
;; [---------------------------------------------------------------------------]

NOTE-ID-ENTRY: ""
NOTE-ID-LIST: []
NOTE-BUILD-INDEX: does [
    NOTE-NOTES: head NOTE-NOTES
    NOTE-ID-LIST: copy []
    forskip NOTE-NOTES 2 [
        NOTE-ID-ENTRY: copy ""
        append NOTE-ID-ENTRY first NOTE-NOTES
        append NOTE-ID-ENTRY ":"
        append NOTE-ID-ENTRY first second NOTE-NOTES
        append NOTE-ID-ENTRY ":"
        append NOTE-ID-ENTRY second second NOTE-NOTES
        append NOTE-ID-LIST NOTE-ID-ENTRY
    ]   
]       
    
;; [---------------------------------------------------------------------------]
;; [ End of note management module.                                            ]
;; [---------------------------------------------------------------------------]

;; [---------------------------------------------------------------------------]
;; [ Beginning of main program.                                                ]
;; [---------------------------------------------------------------------------]

;; [---------------------------------------------------------------------------]
;; [ These are values that an operator might want to change to                 ]
;; [ tailor the program for personal use.  These items are kept here so that   ]
;; [ there is only one place to find such items.                               ]
;; [---------------------------------------------------------------------------]

LITTLENOTE-DEFAULT-DIR: %.                 
LITTLENOTE-FILE-NAME: "NORA.txt"

if not dir? LITTLENOTE-DEFAULT-DIR [
    make-dir LITTLENOTE-DEFAULT-DIR
]
change-dir LITTLENOTE-DEFAULT-DIR

LITTLENOTE-FILE-ID: rejoin [
    LITTLENOTE-DEFAULT-DIR 
    "/"
    LITTLENOTE-FILE-NAME
]
NOTE-FILE-ID: LITTLENOTE-FILE-ID

;; [---------------------------------------------------------------------------]
;; [ We set a flag when we update something, so we can skip writing the file   ]
;; [ if we do NOT update something.                                            ]
;; [---------------------------------------------------------------------------]

FILE-WAS-UPDATED: false

;; [---------------------------------------------------------------------------]
;; [ This procedure is executed when we click on an item in the text-list      ]
;; [ that holds the list of note titles.                                       ]
;; [ It reads the note, fills in the screen, and displays the screen,          ]
;; [ so the note can be read, updated, or deleted.                             ]
;; [---------------------------------------------------------------------------]

SELECTED-ID: ""
SELECTED-LINE: ""
SELECT-NOTE: does [
    SELECTED-ID: copy ""
    SELECTED-LINE: copy ""
    SELECTED-LINE: first MAIN-LIST/picked
    SELECTED-ID: first parse/all SELECTED-LINE ":"
    either (SELECTED-ID = "") [
        alert "No note was selected from the list"  ;; should be impossible
    ] [
        NOTE-REC-ID: to-integer SELECTED-ID
        NOTE-READ-RECORD
        either NOTE-READ-OK [
            SHOW-NOTE-FIELDS
        ] [
            alert rejoin ["Note " NOTE-REC-ID " not in file"]  ;; also impossible
        ]
    ]
]

SHOW-NOTE-FIELDS: does [
    MAIN-ID/text: to-string NOTE-REC-ID
    MAIN-TITLE/text: NOTE-REC-TITLE
    MAIN-DATE/text: to-string NOTE-REC-DATE
    MAIN-TEXT/text: NOTE-REC-TEXT
    show [
        MAIN-TITLE
        MAIN-ID
        MAIN-DATE
        MAIN-TEXT
    ]
]    

;; [---------------------------------------------------------------------------]
;; [ This procedure is executed by the "Save" button.                          ]
;; [ It either adds a new note or updates an existing.                         ]
;; [ If a note has been read, NOTE-REC-ID will be non-zero and we will         ]
;; [ update the current note.  If NOTE-REC-ID is zero, then either no          ]
;; [ record has been read or we have used the "New button to clear the         ]
;; [ screen, which also zeros NOTE-REC-ID.                                     ]
;; [---------------------------------------------------------------------------]

SAVE-BUTTON: does [
    either (0 = NOTE-REC-ID) [
        ADD-NEW-RECORD
    ] [
        UPDATE-CURRENT-RECORD
    ]
]

ADD-NEW-RECORD: does [
    either (MAIN-TITLE/text = "") [
        alert "You can't blank out the title area"
    ] [
        NOTE-REC-TITLE: copy ""
        NOTE-REC-TITLE: MAIN-TITLE/text
        NOTE-REC-TEXT: copy ""
        NOTE-REC-TEXT: MAIN-TEXT/text
        NOTE-ADD-RECORD
        FILE-WAS-UPDATED: true
        SHOW-FRESH-SCREEN
    ]
]

UPDATE-CURRENT-RECORD: does [
    either (MAIN-TITLE/text = "") [
        alert "You can't blank out the title area"
    ] [
        NOTE-REC-TITLE: copy ""
        NOTE-REC-TITLE: MAIN-TITLE/text
        NOTE-REC-TEXT: copy ""
        NOTE-REC-TEXT: MAIN-TEXT/text
        NOTE-STORE-RECORD
        FILE-WAS-UPDATED: true
        SHOW-FRESH-SCREEN
    ]
]

;; [---------------------------------------------------------------------------]
;; [ This procedure is run by the "Delete" button.                             ]
;; [---------------------------------------------------------------------------]

DELETE-BUTTON: does [
    either (0 = NOTE-REC-ID) [
        alert "You must select a note before you can delete it"
    ] [
        NOTE-DELETE-RECORD
        FILE-WAS-UPDATED: true
        SHOW-FRESH-SCREEN
    ]
]

;; [---------------------------------------------------------------------------]
;; [ This procedure is executed by the "New" button.                           ]
;; [ It shows a blank screen, but it also clears the note record area          ]
;; [ so that if we click the "Save" button we will add a new record, and       ]
;; [ not accidentally use any old data.                                        ]
;; [---------------------------------------------------------------------------]

NEW-BUTTON: does [
    SHOW-FRESH-SCREEN
]

;; [---------------------------------------------------------------------------]
;; [ This procedure is executed by the "Quit" button.                          ]
;; [ It will save the file if the file was updated.                            ]
;; [---------------------------------------------------------------------------]

QUIT-BUTTON: does [
    if FILE-WAS-UPDATED [
        NOTE-CLOSE
    ]
    quit
]

;; [---------------------------------------------------------------------------]
;; [ The response to any update is going to be a blank screen, ready for       ]
;; [ adding a new note.                                                        ]
;; [ That "work flow" is going to be the most common, so we will optimize      ]
;; [ for it.                                                                   ]
;; [---------------------------------------------------------------------------]

SHOW-FRESH-SCREEN: does [
    NOTE-REC-ID: 0
    NOTE-REC-DATE: none
    NOTE-REC-TITLE: copy ""
    NOTE-REC-TEXT: copy ""
    NOTE-BUILD-INDEX
    MAIN-LIST/data: NOTE-ID-LIST
    MAIN-TITLE/text: copy ""
    MAIN-ID/text: "Note ID number"
    MAIN-DATE/text: "Last update date"
    MAIN-TEXT/text: copy ""
    show [
        MAIN-LIST
        MAIN-TITLE
        MAIN-ID
        MAIN-DATE
        MAIN-TEXT
    ]
]

;; [---------------------------------------------------------------------------]
;; [ Begin.                                                                    ]
;; [                                                                           ]
;; [ I think we have to build the note index before we run the VID code        ]
;; [ through the "layout" function, so there is data available for the         ]
;; [ text list.                                                                ]
;; [---------------------------------------------------------------------------]

either exists? NOTE-FILE-ID [
    NOTE-OPEN
] [
    GO?: alert [
        rejoin ["OK to create " NOTE-FILE-ID "?"]
        "Yes"
        "No"
    ]
    either GO? [
        NOTE-CREATE-FILE
        NOTE-OPEN
    ] [
        quit
    ]
]
NOTE-BUILD-INDEX

MAIN-WINDOW: [
    across
    VH1 "NOte Recording Assistant"
    return
    MAIN-LIST: text-list 400 data NOTE-ID-LIST [SELECT-NOTE]
    return
    MAIN-TITLE: field 400X25
    return
    label "Note ID"
    MAIN-ID: text "Note ID number"
    label "Last updated on"
    MAIN-DATE: text "Last update date"
    return
    MAIN-TEXT: area 400x400
    return
    across
    button 60x24 "New" [NEW-BUTTON]
    button 60x24 "Save" [SAVE-BUTTON]
    button 60x24 "Delete" [DELETE-BUTTON]
    button 60x24 "Quit" [QUIT-BUTTON]
]

view center-face layout MAIN-WINDOW