| 1 | // =============================================================== // |
|---|
| 2 | // // |
|---|
| 3 | // File : FileWatch.h // |
|---|
| 4 | // Purpose : Watch awar pointing to file AND file itself // |
|---|
| 5 | // // |
|---|
| 6 | // Coded by Ralf Westram (coder@reallysoft.de) in October 2017 // |
|---|
| 7 | // http://www.arb-home.de/ // |
|---|
| 8 | // // |
|---|
| 9 | // =============================================================== // |
|---|
| 10 | |
|---|
| 11 | #ifndef FILEWATCH_H |
|---|
| 12 | #define FILEWATCH_H |
|---|
| 13 | |
|---|
| 14 | #ifndef AW_AWAR_HXX |
|---|
| 15 | #include <aw_awar.hxx> |
|---|
| 16 | #endif |
|---|
| 17 | #ifndef AW_ROOT_HXX |
|---|
| 18 | #include <aw_root.hxx> |
|---|
| 19 | #endif |
|---|
| 20 | #ifndef AW_INOTIFY_HXX |
|---|
| 21 | #include <aw_inotify.hxx> |
|---|
| 22 | #endif |
|---|
| 23 | #ifndef ARB_FILE_H |
|---|
| 24 | #include <arb_file.h> |
|---|
| 25 | #endif |
|---|
| 26 | |
|---|
| 27 | /* Binds user-callback to an awar which contains a filename (full-path; e.g. from File_selection) |
|---|
| 28 | * - the user-callback will be called whenever the awar triggers |
|---|
| 29 | * - the awar will automatically change when |
|---|
| 30 | * - it points to a file and the file gets modified (awar gets touched) |
|---|
| 31 | * - the file is deleted (awar changes to "") |
|---|
| 32 | * - the file is re-created (if awar is "" -> awar points to file again) |
|---|
| 33 | */ |
|---|
| 34 | |
|---|
| 35 | class FileWatch : virtual Noncopyable { |
|---|
| 36 | AW_awar *awar_selfile; // contains name of file (or sth invalid like "") |
|---|
| 37 | SmartCharPtr watched_file; // if set -> currently watched file |
|---|
| 38 | FileChangedCallback wf_cb; // used to watch file changes |
|---|
| 39 | FileChangedCallback user_cb; // called if awar or (pointed-to) file changes |
|---|
| 40 | // Warning: be aware that callbacks may be called with empty name (""), |
|---|
| 41 | // depending on where awar is used! |
|---|
| 42 | |
|---|
| 43 | void file_modified_handler(const char *file, ChangeReason reason) const { |
|---|
| 44 | // - corrects "selected file" in response to filesystem changes (de- and re-select) |
|---|
| 45 | // - triggers awar callback if file gets modified |
|---|
| 46 | |
|---|
| 47 | switch (reason) { |
|---|
| 48 | case CR_CREATED: |
|---|
| 49 | case CR_MODIFIED: |
|---|
| 50 | if (awar_selfile->read_char_pntr()[0]) { // awar has content (do not overwrite) |
|---|
| 51 | awar_selfile->touch(); |
|---|
| 52 | } |
|---|
| 53 | else if (GB_is_regularfile(file)) { |
|---|
| 54 | awar_selfile->write_string(file); |
|---|
| 55 | } |
|---|
| 56 | break; |
|---|
| 57 | case CR_DELETED: |
|---|
| 58 | aw_assert(!GB_is_regularfile(file)); |
|---|
| 59 | awar_selfile->write_string(""); |
|---|
| 60 | break; |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | void remove_inotification() { |
|---|
| 66 | if (watched_file.isSet()) { |
|---|
| 67 | AW_remove_inotification(&*watched_file, wf_cb); |
|---|
| 68 | watched_file.setNull(); |
|---|
| 69 | } |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | void awar_modified_handler() { |
|---|
| 73 | SmartCharPtr file(awar_selfile->read_string()); |
|---|
| 74 | |
|---|
| 75 | bool points_to_file = GB_is_regularfile(&*file); |
|---|
| 76 | bool need_change_watch = |
|---|
| 77 | points_to_file |
|---|
| 78 | ? (watched_file.isNull() || strcmp(&*watched_file, &*file) != 0) |
|---|
| 79 | : watched_file.isSet(); |
|---|
| 80 | |
|---|
| 81 | if (need_change_watch) { |
|---|
| 82 | remove_inotification(); |
|---|
| 83 | if (points_to_file) { |
|---|
| 84 | watched_file = file; |
|---|
| 85 | AW_add_inotification(&*watched_file, wf_cb); |
|---|
| 86 | } |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | user_cb(&*file, CR_MODIFIED); |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | // callback-wrappers |
|---|
| 93 | static void file_modified_wrapper(const char *file, ChangeReason reason, FileWatch *watch) { watch->file_modified_handler(file, reason); } |
|---|
| 94 | static void awar_modified_wrapper(AW_root*, FileWatch *watch) { watch->awar_modified_handler(); } |
|---|
| 95 | |
|---|
| 96 | public: |
|---|
| 97 | |
|---|
| 98 | FileWatch(const char *sel_file_awar, const FileChangedCallback& file_changed_cb) : |
|---|
| 99 | awar_selfile(AW_root::SINGLETON->awar(sel_file_awar)), |
|---|
| 100 | wf_cb(makeFileChangedCallback(file_modified_wrapper, this)), |
|---|
| 101 | user_cb(file_changed_cb) |
|---|
| 102 | { |
|---|
| 103 | awar_selfile->add_callback(makeRootCallback(awar_modified_wrapper, this)); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | ~FileWatch() { |
|---|
| 107 | awar_selfile->remove_callback(makeRootCallback(awar_modified_wrapper, this)); |
|---|
| 108 | remove_inotification(); |
|---|
| 109 | } |
|---|
| 110 | }; |
|---|
| 111 | |
|---|
| 112 | #else |
|---|
| 113 | #error FileWatch.h included twice |
|---|
| 114 | #endif // FILEWATCH_H |
|---|