diff --git a/Cargo.lock b/Cargo.lock
index 501ac22..f199a88 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -72,6 +72,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+[[package]]
+name = "bichannel"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4bdf5473d36d166934ddefbedab84ad123ba887d2dd16eb2e1a036c484c213b"
+
[[package]]
name = "bit_field"
version = "0.10.1"
@@ -879,6 +885,7 @@ dependencies = [
name = "post_maker"
version = "0.4.0-alpha.5"
dependencies = [
+ "bichannel",
"clap",
"dirs",
"fltk",
diff --git a/Cargo.toml b/Cargo.toml
index 4acb9e4..29faf5d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,3 +34,4 @@ infer = "0.7.0"
textwrap = "0.14"
webbrowser = "0.5"
mozjpeg = "0.9.2"
+bichannel = "0.0.4"
diff --git a/src/config_picker.rs b/src/config_picker.rs
index 004acca..8283395 100644
--- a/src/config_picker.rs
+++ b/src/config_picker.rs
@@ -58,7 +58,7 @@ impl ConfigPicker {
let top_padding_btn = Frame::default();
let mut panel_flex = Flex::default().row();
Frame::default();
- let apply_btn = Button::default().with_label("apply");
+ let apply_btn = Button::default().with_label("Apply");
Frame::default();
panel_flex.set_size(&apply_btn, 100);
panel_flex.end();
diff --git a/src/export_all_window.rs b/src/export_all_window.rs
new file mode 100644
index 0000000..ee38309
--- /dev/null
+++ b/src/export_all_window.rs
@@ -0,0 +1,223 @@
+/*
+ This file is part of Post Maker.
+ Post Maker is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Post Maker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Post Maker. If not, see
+*/
+
+//! Picker to pick config if multiple configs are present or defalut config is not present
+use crate::{
+ dialog, globals,
+ result_ext::ResultExt,
+ utils::{self, ImageContainer, ImageInfo, ImageProperties, ImagePropertiesFile},
+};
+use bichannel::Channel;
+use fltk::{
+ app::{self},
+ button::Button,
+ enums,
+ frame::Frame,
+ group::Flex,
+ image::SvgImage,
+ misc::Progress,
+ prelude::*,
+ window::Window,
+};
+use std::{
+ fs::File,
+ sync::{Arc, RwLock},
+ thread,
+};
+
+pub(crate) struct ExportAllWindow {
+ pub(crate) win: Window,
+ pub(crate) progress: Progress,
+ pub(crate) image_name: Frame,
+ pub(crate) close_btn: Button,
+ pub(crate) images_list: Arc>>,
+ pub(crate) channel: Arc>>>,
+}
+
+impl ExportAllWindow {
+ pub(crate) fn new(images_list: Arc>>) -> Self {
+ let mut win = Window::new(0, 0, 500, 130, "Export All").center_screen();
+ win.set_icon(Some(
+ SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
+ ));
+
+ let mut main_flex = Flex::default().size_of_parent().column();
+
+ //label
+ let mut panel_flex = Flex::default().row();
+ panel_flex.set_size(&Frame::default(), 1);
+ Frame::default()
+ .with_label("Exporting all with quotes")
+ .with_align(enums::Align::Left | enums::Align::Inside);
+ panel_flex.end();
+ main_flex.set_size(&panel_flex, 25);
+
+ //image name
+ let mut panel_flex = Flex::default().row();
+ panel_flex.set_size(&Frame::default(), 1);
+ let image_name = Frame::default()
+ .with_label("")
+ .with_align(enums::Align::Left | enums::Align::Inside);
+ panel_flex.end();
+ main_flex.set_size(&panel_flex, 25);
+
+ // progress bar
+ let mut panel_flex = Flex::default().row();
+ Frame::default();
+ let mut progress = Progress::default().with_label("Exporting...");
+ progress.set_maximum(1.0);
+ progress.set_value(0.0);
+ Frame::default();
+ panel_flex.set_size(&progress, 490);
+ panel_flex.end();
+ main_flex.set_size(&panel_flex, 30);
+
+ //close button
+ let mut panel_flex = Flex::default().row();
+ Frame::default();
+ let close_btn = Button::default().with_label("Cancel");
+ panel_flex.set_size(&Frame::default(), 1);
+ panel_flex.set_size(&close_btn, 100);
+ panel_flex.end();
+ main_flex.set_size(&panel_flex, 30);
+
+ main_flex.end();
+
+ win.end();
+ win.make_resizable(true);
+
+ let mut config_picker = Self {
+ win,
+ progress,
+ image_name,
+ close_btn,
+ images_list,
+ channel: Arc::new(RwLock::new(None)),
+ };
+ config_picker.event();
+
+ config_picker
+ }
+
+ pub(crate) fn export(&mut self) {
+ self.image_name.set_label("");
+ self.progress.set_label("Exporting...");
+ self.progress.set_maximum(1.0);
+ self.progress.set_value(0.0);
+ self.win.show();
+ let (left, right) = bichannel::channel();
+ *rw_write!(self.channel) = Some(left);
+ spawn_export_thread(self, right);
+ while self.win.shown() {
+ if let Some(channel) = &*rw_read!(self.channel) {
+ if let Ok(msg) = channel.try_recv() {
+ match msg {
+ ThreadMessage::HideWindow => self.win.hide(),
+ _ => (),
+ }
+ }
+ }
+ app::wait();
+ }
+ }
+
+ // Set callbacks of elements
+ fn event(&mut self) {
+ let channel = Arc::clone(&self.channel);
+ // Close Button
+ self.close_btn.set_callback(move |_| {
+ if dialog::choice_default("Are you sure?", "Yes", "No") == 0 {
+ if let Some(c) = &*rw_read!(channel) {
+ c.send(ThreadMessage::Stop).error_log("Failed to stop task");
+ }
+ }
+ });
+
+ let channel = Arc::clone(&self.channel);
+ // Window Close
+ self.win.set_callback(move |_| {
+ if dialog::choice_default("Are you sure?", "Yes", "No") == 0 {
+ if let Some(c) = &*rw_read!(channel) {
+ c.send(ThreadMessage::Stop).error_log("Failed to stop task");
+ }
+ }
+ });
+ }
+}
+
+pub(crate) enum ThreadMessage {
+ Stop,
+ HideWindow,
+}
+
+fn spawn_export_thread(
+ export_all: &mut ExportAllWindow,
+ channel: Channel,
+) {
+ let mut win = export_all.win.clone();
+ let mut progress = export_all.progress.clone();
+ let mut image_name = export_all.image_name.clone();
+ let images_list = Arc::clone(&export_all.images_list);
+
+ thread::spawn(move || {
+ let total = rw_read!(images_list).len();
+ progress.set_maximum(total as f64);
+ progress.set_value(0.0);
+ for (idx, image) in (*rw_read!(images_list)).iter().enumerate() {
+ image_name.set_label(
+ image
+ .path
+ .file_name()
+ .unwrap_or_default()
+ .to_str()
+ .unwrap_or_default(),
+ );
+ let properties = Arc::new(RwLock::new(ImageProperties::default()));
+ let container = ImageContainer::new(image, properties);
+ let properties_file = utils::get_properties_path(image);
+ let read = match File::open(&properties_file) {
+ Ok(r) => r,
+ Err(_) => continue,
+ };
+ let read = match serde_json::from_reader::(read) {
+ Ok(r) => r,
+ Err(_) => continue,
+ };
+
+ rw_write!(container.properties).merge(read, "", "");
+
+ if rw_read!(container.properties).quote.trim().len() == 0 {
+ continue;
+ }
+
+ container.save();
+
+ progress.set_value(idx as f64 + 1.0);
+ progress.set_label(&format!("[{}/{}]", idx + 1, total));
+ win.redraw();
+ app::awake();
+
+ if let Ok(msg) = channel.try_recv() {
+ match msg {
+ ThreadMessage::Stop => break,
+ _ => (),
+ }
+ }
+ }
+ image_name.set_label("Done");
+ channel
+ .send(ThreadMessage::HideWindow)
+ .error_log("Failed to close window");
+ });
+}
diff --git a/src/main.rs b/src/main.rs
index 8f21f61..2389b6c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -27,6 +27,7 @@ mod config_window;
mod crop_window;
mod dialog;
mod draw_thread;
+mod export_all_window;
mod globals;
mod main_window;
mod result_ext;
diff --git a/src/main_window.rs b/src/main_window.rs
index 583eb6d..551d94f 100644
--- a/src/main_window.rs
+++ b/src/main_window.rs
@@ -19,6 +19,7 @@ use crate::{
crop_window::CropWindow,
dialog,
draw_thread::*,
+ export_all_window::ExportAllWindow,
globals,
result_ext::ResultExt,
utils::{self, ImageInfo, ImageProperties, ImageType},
@@ -422,13 +423,6 @@ impl MainWindow {
if !path.exists() {
return;
}
- let expost_dir = path.join("export");
- if !expost_dir.exists() {
- if let Err(e) = fs::create_dir(expost_dir) {
- Result::<(), _>::Err(e).warn_log("Failed to create export folder!");
- return;
- }
- }
win.set_label(&format!(
"{} - Post Maker",
path.file_name()
@@ -462,6 +456,46 @@ impl MainWindow {
},
);
+ let mut export_all = ExportAllWindow::new(Arc::clone(&self.images_list));
+ let mut win = self.win.clone();
+ self.menubar.add(
+ "&Actions/Export All with Quotes...\t",
+ Shortcut::None,
+ menu::MenuFlag::Normal,
+ move |_| {
+ win.deactivate();
+
+ export_all.export();
+ win.activate();
+ win.redraw();
+ fltk::app::awake();
+ },
+ );
+
+ let properties = Arc::clone(&self.properties);
+ let mut win = self.win.clone();
+ self.menubar.add(
+ "&Actions/Delete Exports...\t",
+ Shortcut::None,
+ menu::MenuFlag::Normal,
+ move |_| {
+ win.deactivate();
+ if dialog::choice_default("Do you want to remove exports?", "Yes", "No") == 0 {
+ let props = rw_read!(properties);
+ if let Some(prop) = &props.image_info {
+ let export = prop.path.parent().unwrap().join("export");
+ if export.exists() {
+ fs::remove_dir_all(&export)
+ .warn_log("Failed to remove export directory");
+ }
+ }
+ }
+ win.activate();
+ win.redraw();
+ fltk::app::awake();
+ },
+ );
+
let mut config_window = ConfigWindow::new();
let sender = self.sender.clone();
let mut image = self.page.image.clone();
diff --git a/src/utils.rs b/src/utils.rs
index df476da..149d66a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -756,13 +756,12 @@ pub(crate) fn get_export_image_path(image_info: &ImageInfo) -> PathBuf {
export_format.as_extension()
);
- let export = image_info
- .path
- .parent()
- .unwrap()
- .join("export")
- .join(&image_name);
+ let expost_dir = image_info.path.parent().unwrap().join("export");
+ if !expost_dir.exists() {
+ fs::create_dir(&expost_dir).expect_log("Failed to create export folder!");
+ }
+ let export = expost_dir.join(&image_name);
export
}