#include "bookmarks.h" #include "cstrptr.h" #include #include namespace Fm { std::weak_ptr Bookmarks::globalInstance_; static inline CStrPtr get_legacy_bookmarks_file(void) { return CStrPtr{g_build_filename(g_get_home_dir(), ".gtk-bookmarks", nullptr)}; } static inline CStrPtr get_new_bookmarks_file(void) { return CStrPtr{g_build_filename(g_get_user_config_dir(), "gtk-3.0", "bookmarks", nullptr)}; } Bookmarks::Bookmarks(QObject* parent): QObject(parent), idle_handler{false} { /* trying the gtk-3.0 first and use it if it exists */ auto fpath = get_new_bookmarks_file(); file = FilePath::fromLocalPath(fpath.get()); load(); if(items_.empty()) { /* not found, use legacy file */ fpath = get_legacy_bookmarks_file(); file = FilePath::fromLocalPath(fpath.get()); load(); } mon = GObjectPtr{g_file_monitor_file(file.gfile().get(), G_FILE_MONITOR_NONE, nullptr, nullptr), false}; if(mon) { g_signal_connect(mon.get(), "changed", G_CALLBACK(_onFileChanged), this); } } Bookmarks::~Bookmarks() { if(mon) { g_signal_handlers_disconnect_by_data(mon.get(), this); } } const std::shared_ptr& Bookmarks::insert(const FilePath& path, const QString& name, int pos) { const auto insert_pos = (pos < 0 || static_cast(pos) > items_.size()) ? items_.cend() : items_.cbegin() + pos; auto it = items_.insert(insert_pos, std::make_shared(path, name)); queueSave(); return *it; } void Bookmarks::remove(const std::shared_ptr& item) { items_.erase(std::remove(items_.begin(), items_.end(), item), items_.end()); queueSave(); } void Bookmarks::reorder(const std::shared_ptr& item, int pos) { auto old_it = std::find(items_.cbegin(), items_.cend(), item); if(old_it == items_.cend()) return; std::shared_ptr newItem = item; auto old_pos = old_it - items_.cbegin(); items_.erase(old_it); if(old_pos < pos) --pos; auto new_it = items_.cbegin() + pos; if(new_it > items_.cend()) new_it = items_.cend(); items_.insert(new_it, std::move(newItem)); queueSave(); } void Bookmarks::rename(const std::shared_ptr& item, QString new_name) { auto it = std::find_if(items_.cbegin(), items_.cend(), [item](const std::shared_ptr& elem) { return elem->path() == item->path(); }); if(it != items_.cend()) { // create a new item to replace the old one // we do not modify the old item directly since this data structure is shared with others it = items_.insert(it, std::make_shared(item->path(), new_name)); items_.erase(it + 1); // remove the old item queueSave(); } } std::shared_ptr Bookmarks::globalInstance() { auto bookmarks = globalInstance_.lock(); if(!bookmarks) { bookmarks = std::make_shared(); globalInstance_ = bookmarks; } return bookmarks; } void Bookmarks::save() { std::string buf; // G_LOCK(bookmarks); for(auto& item: items_) { auto uri = item->path().uri(); buf += uri.get(); buf += ' '; buf += item->name().toUtf8().constData(); buf += '\n'; } idle_handler = false; // G_UNLOCK(bookmarks); GError* err = nullptr; if(!g_file_replace_contents(file.gfile().get(), buf.c_str(), buf.length(), nullptr, FALSE, G_FILE_CREATE_NONE, nullptr, nullptr, &err)) { g_critical("%s", err->message); g_error_free(err); } /* we changed bookmarks list, let inform who interested in that */ Q_EMIT changed(); } void Bookmarks::load() { auto fpath = file.localPath(); FILE* f; char buf[1024]; /* load the file */ f = fopen(fpath.get(), "r"); if(f) { while(fgets(buf, 1024, f)) { // format of each line in the bookmark file: // \n char* sep; sep = strchr(buf, '\n'); if(sep) { *sep = '\0'; } QString name; sep = strchr(buf, ' '); // find the separator between URI and name if(sep) { *sep = '\0'; name = sep + 1; } auto uri = buf; if(uri[0] != '\0') { items_.push_back(std::make_shared(FilePath::fromUri(uri), name)); } } fclose(f); } } void Bookmarks::onFileChanged(GFileMonitor* /*mon*/, GFile* /*gf*/, GFile* /*other*/, GFileMonitorEvent /*evt*/) { // reload the bookmarks items_.clear(); load(); Q_EMIT changed(); } void Bookmarks::queueSave() { if(!idle_handler) { QTimer::singleShot(0, this, &Bookmarks::save); idle_handler = true; } } } // namespace Fm