free-ftp-editor-thumbnailI’ve been looking for a simple free ftp editor. There is one here but it isn’t free. That’s why I decided to craft this poor-man ftp editor. Of course I’m too lazy to craft it from scratch. So I will use the hidden source editor included with Rebol!

Yes, Rebol has an small editor that you can launch by typing:


editor none

rebol_editor

To get the source of the Editor, type this:


write clipboard:// mold :editor

Paste it into notepad, you should get this:


func [file /app app-word /local tmp][
    either link? [
        if block? ctx-edit [
            ctx-edit: context either all [
                value? 'fileset-files?
                tmp: fileset-files? 'desktop
                find tmp %desktop/edit.r
                tmp: load/header link-root/desktop/edit.r
            ] [next tmp] [ctx-edit]
        ]
        either app [ctx-edit/view-file/app file app-word] [ctx-edit/view-file file]
    ] [
        either exists? tmp: view-root/desktop/scripts/edit.r [do/args tmp file] [
            if block? ctx-edit [ctx-edit: context ctx-edit]
            ctx-edit/view-file file
        ]
    ]
]

The source above shows that it is a wrapper for ctx-edit, so let’s get the source of ctx-edit by typing:


write clipboard:// mold :ctx-edit

which gives this block of code for the core implementation:


[
    prefs: either link? [link-root/edit-prefs.r] [view-root/edit-prefs.r]
    dirty-edit: false
    this-file: none
    this-app: none
    t1: s1: h1: f1: none
    edit-prefs: context [
        offset: 40x40
        size: 640x480
        wrap: on
    ]
    ed-lo: layout/offset [
        style tx vtext bold 40x22 font [colors: [0.0.0 200.200.200]] middle center
        size 640x320 origin 0 space 0 across
        tx "Open" [open-as]
        tx "New" [new-file]
        tx "Find" [find-text]
        tx "Save" [save-file]
        tx "Save-As" 60 [save-as]
        tx "Help" [show-help]
        tx "Quit" [quit-edit]
        pad 8
        text "File:" bold black right middle 30x22
        h1: at
        f1: info 300x22 base-color + 40.40.40
        return
        t1: area 624x320 font [name: font-fixed] para [tabs: 28 origin: 4x4]
        [dirty-edit: any [dirty-edit face/dirty?]]
        with [color: snow feel: make feel [redraw: none]]
        s1: scroller 16x320 [scroll-para t1 s1]
    ] 0x0
    ed-lo/color: base-color
    t1/flags: []
    save-lo: layout [
        origin 10x10 space 8x4
        backeffect base-effect
        style btn btn 140
        text bold "Save your changes?" 140 center
        pad 0x4
        btn-enter 140 "Save" #"s" [hide-popup result: save-file]
        btn green "Save As..." [hide-popup result: save-as]
        btn red + 50 "Quit - No Save" [hide-popup quit-now]
        btn-cancel 140 "Cancel" [hide-popup]
    ]
    save-lo/color: base-color
    ff: fb: fc: fp: none
    find-lo: layout [
        space 4x4 across
        origin 16x16
        backeffect base-effect
        lab 32 "Find:"
        ff: field [search ff/text fc/data false]
        pad 4
        fb: btn-enter 80 "Find" [search ff/text fc/data false]
        return
        at ff/offset + 0x32
        fc: check text "Match case"
        at fb/offset + 0x32
        btn-cancel 80 "Cancel" [unview/only find-lo]
        return
        pad 32 vtext bold "Hint: Use Ctrl-G to find next."
    ]
    find-lo/color: base-color
    deflag-face ff tabbed
    resize: func [size] [
        ed-lo/size: size
        t1/size: size - (s1/size * 1x0) - (f1/size * 0x1)
        s1/offset/x: t1/offset/x + t1/size/x
        s1/resize/y t1/size/y
        f1/size/x: size/x - h1/x
        t1/line-list: none
    ]
    refresh: does [
        show ed-lo
        s1/redrag t1/size/y / max 1 second size-text t1
        show s1
    ]
    save-prefs: has [str] [
        str: copy ""
        edit-prefs/offset: ed-lo/offset
        edit-prefs/size: ed-lo/size
        foreach w next first edit-prefs [
            append str rejoin [w ": " edit-prefs/:w newline]
        ]
        write prefs str
    ]
    open-as: has [file] [
        if not save-edit [exit]
        file: request-file/keep/file/title this-file "Select a file to open:" "Open"
        if not all [file file: pick file 1] [return false]
        open-file file
    ]
    new-file: does [
        if not save-edit [exit]
        open-file none
    ]
    save-as: has [file] [
        file: any [this-file %temp.txt]
        file: request-file/keep/file/title/save file "Save file as:" "Save"
        if not all [file file: pick file 1] [return false]
        this-file: file
        save-file
        true
    ]
    save-file: has [file] [
        if not this-file [return save-as]
        f1/text: this-file
        f1/color: gold show f1
        write this-file t1/text
        wait 0.2
        f1/color: base-color + 40.40.40 show f1
        dirty-edit: t1/dirty?: false
        if all [
            link?
            value? 'send-server
            this-app
            link-sync-connected?
            file: fileset-files? this-app
            find file link-relative-path this-file
            request/confirm "Upload the modified file back to the server?"
        ] [up-file]
        true
    ]
    up-file: does [
        if all [link? value? 'add-file this-app] [
            add-file this-app link-relative-path this-file
        ]
    ]
    open-file: func [file /local txt] [
        if file = 'same [file: this-file]
        f1/text: this-file: none
        txt: copy ""
        case [
            none? file []
            any [file? file url? file] [
                f1/text: this-file: file
                either error? try [txt: read file] [
                    request/ok reform ["Cannot read file:" file]
                ] [
                    f1/text: this-file: file
                ]
            ]
            any-string? file [txt: form file]
            true [txt: mold file]
        ]
        ed-lo/changes: [activate text]
        ed-lo/text: form-title file
        show ed-lo
        dirty-edit: false
        t1/dirty?: false
        s1/data: 0
        t1/para/scroll: 0x0
        t1/text: t1/data: copy txt
        t1/line-list: none
        unfocus
        refresh
        focus t1
    ]
    result: none
    ask-save: does [result: none inform save-lo result]
    save-edit: does [either any [dirty-edit t1/dirty?] [ask-save] [true]]
    do-file: does [
        save-file
        either link? [start-app/method this-file 'doer] [
            if any [file? this-file url? this-file] [launch/secure-cmd this-file]
        ]
    ]
    quit-edit: does [if save-edit [quit-now]]
    quit-now: does [
        if viewed? find-lo [unview/only find-lo]
        unview/only ed-lo
        dirty-edit: false
        t1/dirty?: false
    ]
    caret: none
    start: none
    last-str: last-case: last-pat: none
    find-text: does [
        caret: system/view/caret
        view/new/title find-lo "Find"
        find-lo/changes: 'activate
        show find-lo
        focus ff
    ]
    search: func [str case pat] [
        unview/only find-lo
        start: any [caret t1/text]
        last-str: str
        last-case: case
        last-pat: pat
        find-str str case pat
    ]
    find-str: func [str case pat] [
        if not find-next str case pat [
            start: none
            if not find-next str case pat [system/view/caret: caret]
        ]
    ]
    find-again: does [
        start: any [system/view/caret t1/text]
        if last-str [find-str last-str last-case last-pat]
    ]
    find-next: func [str case pat /local txt] [
        if txt: find' any [start t1/text] str case pat none [
            focus t1
            system/view/caret: txt
            if not pat [
                system/view/highlight-start: txt
                system/view/caret:
                system/view/highlight-end: start: find' txt str case pat true
            ]
            scroll-to txt
            true
        ]
    ]
    find': func [txt str case pat tal /local code p] [
        code: copy [find txt str]
        if any [case pat tal] [
            change/only code p: make path! [find]
            if case [insert tail :p 'case]
            if pat [insert tail :p 'any]
            if tal [insert tail :p 'tail]
        ]
        do code
    ]
    scroll-to: func [txt /local xy] [
        xy: (caret-to-offset t1 txt) - t1/para/scroll
        t1/para/scroll: min 0x0 t1/size / 2 - xy
        s1/data: (second xy) / max 1 second size-text t1
        show [s1 t1]
    ]
    show-help: does [
        view/new/title center-face layout [
            origin 10
            backeffect base-effect
            h2 "Editor Shortcuts:"
            text as-is help-text black snow edge [size: 1x1 color: coal] para [origin: margin: 10x10]
            indent 50
            btn-cancel 72 "Close" [unview/only face/parent-face]
        ]
        "Editor Help"
    ]
    help-text: trim/auto {
^-^-Ctrl-A - select all text
^-^-Ctrl-C - copy text
^-^-Ctrl-X - cut text / cut all
^-^-Ctrl-V - paste text
^-^-Ctrl-Q - to quit
^-^-Ctrl-S - to save changes
^-^-Ctrl-O - to open a file
^-^-Ctrl-N - to create a new file
^-^-Ctrl-T - Trim line to end
^-^-Ctrl-E (F5) - to execute (do)
^-^-Ctrl-F (F3) - to find text
^-^-Ctrl-G - to find next text}
    keymap: [
        #"^S" [save-file]
        #"^W" [quit-edit]
        #"^Q" [quit-edit]
        #"^O" [open-as]
        #"^N" [new-file]
        #"^E" [do-file]
        #"^F" [find-text]
        #"^G" [find-again]
        F3 [find-text]
        F5 [do-file]
        page-up [scroll-edit true -1]
        page-down [scroll-edit true 1]
    ]
    scroll-edit: func [page n] [
        s1/data: s1/data + (n *
            (either page [t1/size/y] [t1/font/size]) / max 1 second size-text t1
        )
        show s1
        scroll-para t1 s1
    ]
    init: does [
        ed-lo/feel: make ed-lo/feel [
            detect: func [face event] [
                switch event/type [
                    offset [save-prefs]
                    scroll-line [scroll-edit false event/offset/y]
                    scroll-page [scroll-edit true event/offset/y]
                    resize [
                        resize max 100x64 ed-lo/size
                        refresh
                        save-prefs
                        return true
                    ]
                    key [switch event/key keymap]
                    close [quit-edit]
                ]
                event
            ]
        ]
    ]
    form-title: func [val] [
        val: if any [file? val url? val] [second split-path val]
        reform ["Edit:" any [val "new"]]
    ]
    view-file: func [file /app app-word] [
        if not save-edit [exit]
        if app [this-app: app-word]
        t1/text: none
        t1/line-list: none
        if not viewed? ed-lo [
            if exists? prefs [edit-prefs: make edit-prefs load/all prefs]
            t1/para/wrap?: edit-prefs/wrap
            ed-lo/offset: edit-prefs/offset
            if outside? system/view/screen-face/size ed-lo/offset + 20x20 [
                ed-lo/offset: 40x40
                save-prefs
            ]
            resize edit-prefs/size
            ed-lo/text: form-title file
            view/new/options ed-lo [resize]
            init
            center-face/with find-lo ed-lo
            if not overlap? find-lo system/view/screen-face [find-lo/offset: ed-lo/offset + 20]
        ]
        open-file file
        if 1 = length? system/view/screen-face/pane [do-events]
    ]
]

Under line:


        tx "Save-As" 60 [save-as]

Add this line:


        tx "Save-As-Ftp" 80 [save-as-ftp]

Under


    save-as: has [file] [
        file: any [this-file %temp.txt]
        file: request-file/keep/file/title/save file "Save file as:" "Save"
        if not all [file file: pick file 1] [return false]
        this-file: file
        save-file
        true
    ]

Add


    save-as-ftp: has [file][
      print "to do"
    ]

On the very beginning of the first line add this line


ctx-edit: make Object!

just before the opening bracket to get this:


ctx-edit: make Object! [
    prefs: either link? [link-root/edit-prefs.r] [view-root/edit-prefs.r]
    dirty-edit: false
    this-file: none
    this-app: none
    t1: s1: h1: f1: none
    edit-prefs: context [
        offset: 40x40
        size: 640x480
        wrap: on
    ]

Then copy and paste all into Rebol Console, validate and test by typing once again


editor none

You should see the new menu appear:
free-ftp-editor

In future lesson, we’ll see how to implement it using what we already learned in “FTP Tutorial”. In this implementation we will ask for FTP parameters using a Visual GUI and persist them in a configuration file so as to avoid the User to remember and type them once again.

Bookmark and Share

Recent Articles