#ifndef DOCUMENT_H_MIJOONQT #define DOCUMENT_H_MIJOONQT #include #include #include #include #include #include #include #include #include #include #include #include #include namespace document { struct watch_t; struct document_t; typedef std::tr1::shared_ptr watch_ptr; typedef std::tr1::shared_ptr document_ptr; typedef std::tr1::shared_ptr document_const_ptr; typedef std::tr1::weak_ptr document_weak_ptr; struct PUBLIC open_callback_t : file::open_callback_t { virtual ~open_callback_t () { } virtual void show_content (std::string const& path, io::bytes_ptr content, std::map const& attributes, std::string const& pathAttributes, std::string const& fileType, std::string const& encoding, bool bom, std::string const& lineFeeds, std::vector const& binaryImportFilters, std::vector const& textImportFilters) { } virtual void show_document (std::string const& path, document_ptr document) = 0; virtual void show_error (std::string const& path, document_ptr document, std::string const& message, oak::uuid_t const& filter) = 0; virtual void show_error (std::string const& path, std::string const& message, oak::uuid_t const& filter) { } }; typedef std::tr1::shared_ptr open_callback_ptr; struct PUBLIC save_callback_t : file::save_callback_t { virtual ~save_callback_t () { } virtual void did_save_document (document_ptr document, std::string const& path, bool success, std::string const& message, oak::uuid_t const& filter) = 0; virtual void did_save (std::string const& path, io::bytes_ptr content, std::string const& pathAttributes, std::string const& encoding, bool bom, std::string const& lineFeeds, bool success, std::string const& message, oak::uuid_t const& filter) { } }; typedef std::tr1::shared_ptr save_callback_ptr; struct PUBLIC document_t : std::tr1::enable_shared_from_this { WATCH_LEAKS(document_t); document_t () : _did_load_marks(false), _selection(NULL_STR), _folded(NULL_STR), _visible_rect(NULL_STR), _disable_callbacks(false), _revision(0), _disk_revision(0), _modified(false), _path(NULL_STR), _open_count(0), _has_lru(false), _is_on_disk(false), _recent_tracking(true), _backup_path(NULL_STR), _backup_revision(0), _virtual_path(NULL_STR), _custom_name(NULL_STR), _untitled_count(0), _grammar_callback(*this), _file_type(NULL_STR), _path_attributes(NULL_STR), /*_folder(NULL_STR),*/ _disk_encoding(NULL_STR), _disk_newlines(NULL_STR), _disk_bom(false) { } ~document_t (); bool operator== (document_t const& rhs) const { return _identifier == rhs._identifier; } bool operator!= (document_t const& rhs) const { return _identifier != rhs._identifier; } // ============================================================ // = Doing one-pass reading of file (find in arbitrary files) = // ============================================================ struct reader_t { virtual io::bytes_ptr next () = 0; virtual ~reader_t () { } }; typedef std::tr1::shared_ptr reader_ptr; reader_ptr create_reader () const; // ====================================================== // = Performing replacements (from outside a text view) = // ====================================================== void replace (std::multimap const& replacements); // =================================================================== // = Controlling marks (bookmarks, warnings, errors, search matches) = // =================================================================== struct mark_t { WATCH_LEAKS(document_t::mark_t); mark_t (char const* type = "bookmark") : type(type), info("") { } mark_t (std::string const& type, std::string const& info = "") : type(type), info(info) { } bool operator== (mark_t const& rhs) const { return type == rhs.type && info == rhs.info; } bool operator!= (mark_t const& rhs) const { return type != rhs.type || info != rhs.info; } std::string type, info; }; void add_mark (text::range_t const& range, mark_t const& mark = mark_t()); void remove_all_marks (std::string const& typeToClear = NULL_STR); std::multimap marks () const; private: void load_marks (std::string const& src) const; void setup_marks (std::string const& src, ng::buffer_t& buf) const; std::string marks_as_string () const; mutable std::multimap _marks; mutable bool _did_load_marks; std::string _selection; std::string _folded; std::string _visible_rect; io::bytes_ptr _content; // =============== // = Symbol list = // =============== public: std::map symbols (); // =================== // = Callback system = // =================== struct callback_t { WATCH_LEAKS(document_t::callback_t); enum event_t { did_save, did_change_open_status, did_change_modified_status, did_change_on_disk_status, did_change_path, did_change_file_type, did_change_indent_settings, // did_change_display_name, did_change_marks, // did_change_symbols, }; virtual ~callback_t () { } virtual void handle_document_event (document_ptr document, event_t event) = 0; }; void add_callback (callback_t* callback) { _callbacks.add(callback); } void remove_callback (callback_t* callback) { _callbacks.remove(callback); } private: void check_modified (ssize_t diskRev, ssize_t rev); void broadcast (callback_t::event_t event, bool cascade = true) { if(_disable_callbacks) return; _disable_callbacks = !cascade; _callbacks(&callback_t::handle_document_event, shared_from_this(), event); _disable_callbacks = false; } oak::callbacks_t _callbacks; bool _disable_callbacks; // =================== // = For OakTextView = // =================== void post_load (std::string const& path, io::bytes_ptr content, std::map const& attributes, std::string const& fileType, std::string const& pathAttributes, std::string const& encoding, bool bom, std::string const& newlines); struct open_callback_wrapper_t : file::open_callback_t { open_callback_wrapper_t (document::document_ptr doc, document::open_callback_ptr callback) : _document(doc), _callbacks(1, callback) { } void select_encoding (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { _callbacks[0]->select_encoding(path, content, context); } void select_line_feeds (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { _callbacks[0]->select_line_feeds(path, content, context); } void select_file_type (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { if(_document->file_type() == NULL_STR) _callbacks[0]->select_file_type(path, content, context); else context->set_file_type(_document->file_type()); } void add_callback (document::open_callback_ptr callback) { _callbacks.push_back(callback); } void show_content (std::string const& path, io::bytes_ptr content, std::map const& attributes, std::string const& fileType, std::string const& pathAttributes, std::string const& encoding, bool bom, std::string const& lineFeeds, std::vector const& binaryImportFilters, std::vector const& textImportFilters) { // we are deleted in post_load() so make a copy of relevant data std::vector callbacks(_callbacks); document::document_ptr doc = _document; _document->post_load(path, content, attributes, fileType, pathAttributes, encoding, bom, lineFeeds); iterate(cb, callbacks) (*cb)->show_document(path, doc); } void show_error (std::string const& path, std::string const& message, oak::uuid_t const& filter) { // we are deleted in post_load() so make a copy of relevant data std::vector callbacks(_callbacks); document::document_ptr doc = _document; _document->post_load(path, io::bytes_ptr(), std::map(), NULL_STR, NULL_STR, NULL_STR, false, NULL_STR); iterate(cb, callbacks) (*cb)->show_error(path, doc, message, filter); } private: document::document_ptr _document; std::vector _callbacks; }; typedef std::tr1::shared_ptr open_callback_wrapper_ptr; open_callback_wrapper_ptr _open_callback; void post_save (std::string const& path, io::bytes_ptr content, std::string const& pathAttributes, std::string const& encoding, bool bom, std::string const& lineFeeds, bool succes); public: bool try_open (document::open_callback_ptr callback); void open (); void close (); void show (); void hide (); oak::date_t const& lru () const; void try_save (document::save_callback_ptr callback); bool save (); bool backup (); void set_path (std::string const& newPath); void set_virtual_path (std::string const& virtualPath) { _virtual_path = virtualPath; } void set_custom_name (std::string const& newCustomName) { _custom_name = newCustomName; } void set_file_type (std::string const& newFileType); std::string path () const { return _path; } std::string virtual_path () const { return _virtual_path == NULL_STR ? _path : _virtual_path; } std::string backup_path () const; std::string display_name () const; bool recent_tracking () const { return _recent_tracking && _path != NULL_STR; } void set_recent_tracking (bool flag) { _recent_tracking = flag; } ng::buffer_t& buffer () { ASSERT(_buffer); return *_buffer; } ng::buffer_t const& buffer () const { ASSERT(_buffer); return *_buffer; } ng::undo_manager_t& undo_manager () { ASSERT(_undo_manager); return *_undo_manager; } ng::undo_manager_t const& undo_manager () const { ASSERT(_undo_manager); return *_undo_manager; } // ============= // = Accessors = // ============= oak::uuid_t identifier () const { return _identifier; } ssize_t revision () const { return _revision; } void set_revision (ssize_t rev) { check_modified(_disk_revision, rev); } bool is_open () const { return _open_count != 0 && !_open_callback; } std::string file_type () const; std::string path_attributes () const { return _path_attributes; } scope::scope_t scope () const { return file_type() + " " + path_attributes(); } settings_t const settings () const { return settings_for_path(virtual_path(), scope(), path::parent(_path), identifier(), variables(std::map(), false)); } std::map variables (std::map map, bool sourceFileSystem = true) const; bool is_modified () const; bool is_on_disk () const { return is_open() ? _is_on_disk : path::exists(path()); } void set_disk_revision (ssize_t rev) { check_modified(rev, _revision); } std::string const& selection () const { return _selection; } std::string const& folded () const { return _folded; } std::string visible_rect () const { return _visible_rect; } void set_selection (std::string const& sel) { _selection = sel; _visible_rect = NULL_STR; } void set_folded (std::string const& folded) { _folded = folded; } void set_visible_rect (std::string const& rect) { _visible_rect = rect; } void set_authorization (osx::authorization_t const& auth) { _authorization = auth; } private: void setup_buffer (); void grammar_did_change (); void set_content (io::bytes_ptr const& bytes); std::string content () const { ASSERT(_buffer); return _buffer->substr(0, _buffer->size()); } void set_modified (bool flag); // ============== // = Properties = // ============== friend document_ptr create (std::string const& path); friend document_ptr from_content (std::string const& content, std::string const& fileType); friend document_ptr find (oak::uuid_t const& uuid, bool searchBackups); oak::uuid_t _identifier; // to identify this document when there is no path path::identifier_t _key; ssize_t _revision; ssize_t _disk_revision; bool _modified; std::string _path; // does not imply there actually is a file size_t _open_count; // document open in some window/tab mutable oak::date_t _lru; // last time document was shown mutable bool _has_lru; bool _is_on_disk; bool _recent_tracking; mutable std::string _backup_path; // if there is a backup, this is set — we can have a backup even when there is no path mutable ssize_t _backup_revision; std::string _virtual_path; std::string _custom_name; mutable size_t _untitled_count; // this is ≠ 0 if the document is untitled struct grammar_callback_t : parse::grammar_t::callback_t { grammar_callback_t (document_t& doc) : _document(doc) { } void grammar_did_change () { _document.grammar_did_change(); } private: document_t& _document; }; parse::grammar_ptr _grammar; grammar_callback_t _grammar_callback; mutable std::string _file_type; // this may also be in the settings mutable std::string _path_attributes; // oak::uuid_t _grammar_uuid; std::tr1::shared_ptr _buffer; std::string _pristine_buffer = NULL_STR; std::tr1::shared_ptr _undo_manager; void mark_pristine (); // std::string _folder; // when there is no path, this value is where the document will likely end up, i.e, used for retrieving settings and default save location osx::authorization_t _authorization; // when opened via sudo friend struct document_tracker_t; size_t untitled_count () const { return _untitled_count; } std::string _disk_encoding; std::string _disk_newlines; bool _disk_bom; protected: // so that we can trigger the callback in unit tests watch_ptr _file_watcher; friend struct watch_t; void watch_callback (int flags, std::string const& newPath, bool async = true); }; PUBLIC document_ptr create (std::string const& path = NULL_STR); PUBLIC document_ptr find (oak::uuid_t const& uuid, bool searchBackups = true); PUBLIC document_ptr from_content (std::string const& content, std::string const& fileType = NULL_STR); PUBLIC bool is_binary (std::string const& path); // ==================== // = Document scanner = // ==================== struct PUBLIC scanner_t { WATCH_LEAKS(scanner_t); scanner_t (std::string const& path, std::string const& glob = "*", std::string const& excludeGlob = "", bool follow_links = false, bool follow_hidden_folders = false, bool depth_first = false); ~scanner_t (); bool is_running () const { return is_running_flag; } void stop () { should_stop_flag = true; } void wait () const { pthread_join(thread, NULL); } static std::vector open_documents (); std::vector accept_documents (); std::string get_current_path () const; private: std::string path, glob, exclude_glob; bool follow_links, follow_hidden_folders, depth_first; pthread_t thread; mutable pthread_mutex_t mutex; volatile bool is_running_flag, should_stop_flag; void thread_main (); void scan_dir (std::string const& dir); std::string current_path; std::vector documents; std::set< std::pair > seen_paths; }; typedef std::tr1::shared_ptr scanner_ptr; } /* document */ #endif /* end of include guard: DOCUMENT_H_MIJOONQT */