|
|
@@ -42,6 +42,24 @@ BookDetails::BookDetails(DatabaseManager& db, BookList& book_list) :
|
|
|
m_add_tag_button.signal_clicked().connect(sigc::mem_fun(*this, &BookDetails::on_add_tag_button_clicked));
|
|
|
tag_entry_box.append(m_add_tag_button);
|
|
|
append(tag_entry_box);
|
|
|
+
|
|
|
+ // Connect to BookList signals to handle book removal/updates
|
|
|
+ m_conn_removed = m_book_list.signalBookRemoved().connect(
|
|
|
+ sigc::mem_fun(*this, &BookDetails::on_book_removed));
|
|
|
+ m_conn_updated = m_book_list.signalBookUpdated().connect(
|
|
|
+ [this](const Book& book) {
|
|
|
+ if (book.id() == m_book_id) {
|
|
|
+ refresh_display();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ m_conn_reset = m_book_list.signalReset().connect(
|
|
|
+ sigc::mem_fun(*this, &BookDetails::on_book_list_changed));
|
|
|
+}
|
|
|
+
|
|
|
+BookDetails::~BookDetails() {
|
|
|
+ m_conn_removed.disconnect();
|
|
|
+ m_conn_updated.disconnect();
|
|
|
+ m_conn_reset.disconnect();
|
|
|
}
|
|
|
|
|
|
Gtk::Widget* BookDetails::create_tag_chip(const std::string& tag_name) {
|
|
|
@@ -74,6 +92,8 @@ void BookDetails::refresh_tags() {
|
|
|
m_tags_box.remove(*child);
|
|
|
}
|
|
|
|
|
|
+ if (m_book_id.empty()) return;
|
|
|
+
|
|
|
// Reload tags from database
|
|
|
auto tags = m_db.get_tags_for_book(m_book_id);
|
|
|
for (const auto& tag : tags) {
|
|
|
@@ -81,39 +101,92 @@ void BookDetails::refresh_tags() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void BookDetails::refresh_display() {
|
|
|
+ if (m_book_id.empty()) return;
|
|
|
+
|
|
|
+ auto book_opt = m_book_list.findById(m_book_id);
|
|
|
+ if (!book_opt) {
|
|
|
+ // Book no longer exists
|
|
|
+ clear();
|
|
|
+ m_signalBookRemoved.emit();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& book = *book_opt;
|
|
|
+ m_cover.set(book.cover());
|
|
|
+ m_title.set_markup("<span weight='bold' size='large'>" + Glib::Markup::escape_text(book.title()) + "</span>");
|
|
|
+ m_author.set_markup("<span size='medium'>" + Glib::Markup::escape_text(book.author()) + "</span>");
|
|
|
+ refresh_tags();
|
|
|
+}
|
|
|
+
|
|
|
void BookDetails::set_book(const Book* book) {
|
|
|
- m_book = book;
|
|
|
- if (m_book) {
|
|
|
- m_book_id = m_book->id();
|
|
|
- m_cover.set(m_book->cover());
|
|
|
- m_title.set_markup("<span weight='bold' size='large'>" + Glib::Markup::escape_text(m_book->title()) + "</span>");
|
|
|
- m_author.set_markup("<span size='medium'>" + Glib::Markup::escape_text(m_book->author()) + "</span>");
|
|
|
+ if (book) {
|
|
|
+ m_book_id = book->id();
|
|
|
+ m_cover.set(book->cover());
|
|
|
+ m_title.set_markup("<span weight='bold' size='large'>" + Glib::Markup::escape_text(book->title()) + "</span>");
|
|
|
+ m_author.set_markup("<span size='medium'>" + Glib::Markup::escape_text(book->author()) + "</span>");
|
|
|
refresh_tags();
|
|
|
} else {
|
|
|
- m_book_id.clear();
|
|
|
+ clear();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void BookDetails::clear() {
|
|
|
+ m_book_id.clear();
|
|
|
+ m_cover.clear();
|
|
|
+ m_title.set_markup("<span weight='bold' size='large'></span>");
|
|
|
+ m_author.set_markup("<span size='medium'></span>");
|
|
|
+ while (auto child = m_tags_box.get_child_at_index(0)) {
|
|
|
+ m_tags_box.remove(*child);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void BookDetails::on_book_removed(const std::string& id) {
|
|
|
+ if (id == m_book_id) {
|
|
|
+ clear();
|
|
|
+ m_signalBookRemoved.emit();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void BookDetails::on_book_list_changed() {
|
|
|
+ // After a full reset, check if our book still exists
|
|
|
+ if (m_book_id.empty()) return;
|
|
|
+
|
|
|
+ auto book_opt = m_book_list.findById(m_book_id);
|
|
|
+ if (!book_opt) {
|
|
|
+ clear();
|
|
|
+ m_signalBookRemoved.emit();
|
|
|
+ } else {
|
|
|
+ refresh_display();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void BookDetails::on_open_button_clicked() {
|
|
|
- if (m_book) {
|
|
|
- const auto& path = m_book->filePath();
|
|
|
- if (path.empty()) {
|
|
|
- std::cerr << "Activated book has no file path: " << m_book->title() << "\n";
|
|
|
+ if (m_book_id.empty()) return;
|
|
|
+
|
|
|
+ auto book_opt = m_book_list.findById(m_book_id);
|
|
|
+ if (!book_opt) {
|
|
|
+ std::cerr << "Book no longer exists: " << m_book_id << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& path = book_opt->filePath();
|
|
|
+ if (path.empty()) {
|
|
|
+ std::cerr << "Activated book has no file path: " << book_opt->title() << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ auto file = Gio::File::create_for_path(path);
|
|
|
+ if (!file->query_exists()) {
|
|
|
+ std::cerr << "Book file is missing: " << path << "\n";
|
|
|
return;
|
|
|
}
|
|
|
- try {
|
|
|
- auto file = Gio::File::create_for_path(path);
|
|
|
- if (!file->query_exists()) {
|
|
|
- std::cerr << "Book file is missing: " << path << "\n";
|
|
|
- return;
|
|
|
- }
|
|
|
- // Ask the desktop to open the file with the user's default application.
|
|
|
- const auto uri = Glib::filename_to_uri(path);
|
|
|
- Gio::AppInfo::launch_default_for_uri(uri);
|
|
|
- } catch (const Glib::Error& e) {
|
|
|
- std::cerr << "Failed to open book '" << m_book->title() << "': "
|
|
|
- << e.what() << "\n";
|
|
|
- }
|
|
|
+ // Ask the desktop to open the file with the user's default application.
|
|
|
+ const auto uri = Glib::filename_to_uri(path);
|
|
|
+ Gio::AppInfo::launch_default_for_uri(uri);
|
|
|
+ } catch (const Glib::Error& e) {
|
|
|
+ std::cerr << "Failed to open book '" << book_opt->title() << "': "
|
|
|
+ << e.what() << "\n";
|
|
|
}
|
|
|
}
|
|
|
|