using System; //using System.Configuration; //using System.Collections.Generic; //using System.ComponentModel; //using System.Data; using System.Drawing; //using System.Drawing.Drawing2D; //using System.Linq; //using System.Text; //using System.Threading.Tasks; using System.Windows.Forms; using System.Collections;//.ArrayList; using System.IO; using ImageMagick; namespace photo_sorter { public partial class form_main : Form { public form_main() { InitializeComponent(); switch (Properties.Settings.Default.img_format) { case "nef": rad_nef.Checked = true; break; case "cr2": rad_cr2.Checked = true; break; case "dng": rad_dng.Checked = true; break; case "jpg": rad_jpeg.Checked = true; break; case "png": rad_png.Checked = true; break; default: rad_nef.Checked = true; break; } if (Properties.Settings.Default.global_hotkeys) tsi_enableGlobalHotkeys.Checked = true; else tsi_enableGlobalHotkeys.Checked = false; if (Properties.Settings.Default.copy_on_sort) { rad_sort_copy.Checked = true; } else { rad_sort_move.Checked = true; } tbox_dest_dir.Text = Properties.Settings.Default.dest_dir; tbox_source_dir.Text = Properties.Settings.Default.src_dir; if (Directory.Exists(tbox_source_dir.Text)) { btn_reload_src.Enabled = true; reload_source(); } if (Properties.Settings.Default.cbi_primary != null) { cbox_sort_primary.Items.AddRange(Properties.Settings.Default.cbi_primary.ToArray()); } if (Properties.Settings.Default.cbi_secondary != null) { cbox_sort_secondary.Items.AddRange(Properties.Settings.Default.cbi_secondary.ToArray()); } } static class global { public static string dest = Properties.Settings.Default.dest_dir; public static string src = Properties.Settings.Default.src_dir; public static string[] filenames; public static int filecount; public static int position = 0; //File position index public const int cbox_size = 8; //Number of entries allowed in cbox lists } public void status_msg(string msg, int lvl = 1) { string mtype = "UNDEFINED"; switch (lvl) { case 1: mtype = "INFO"; //stat_last_action.ForeColor = new System.Drawing.Color(); stat_last_action.ForeColor = Color.Black; break; case 2: mtype = "WARN"; stat_last_action.ForeColor = Color.Olive; break; case 3: mtype = "ERROR"; System.Media.SystemSounds.Asterisk.Play(); stat_last_action.ForeColor = Color.Red; break; case 4: mtype = "CRITICAL"; System.Media.SystemSounds.Exclamation.Play(); stat_last_action.ForeColor = Color.Red; break; } stat_last_action.Text = mtype + ": " + msg; status_bar.Update(); } private void update_counter() { lbl_counter.Text = (global.position + 1).ToString() + " of " + global.filecount.ToString(); } private void reload_source() { global.filenames = populate_file_names(); global.filecount = global.filenames.Length; global.position = 0; if (global.filecount > 0) { gbox_preview.Enabled = true; lbl_counter.Text = (global.position + 1).ToString() + " of " + global.filecount.ToString(); update_image(); status_msg("Source directory updated, found " + global.filecount.ToString() + " images"); } else { gbox_preview.Enabled = false; status_msg("Source directory updated, but no images found!",3); } } private void populate_directory_lists() { //Exit if no destination directory selected if (global.dest.Length < 1) { return; } lbox_primary_sortlist.Items.Clear(); foreach (var d in Directory.GetDirectories(global.dest)) { var dir = new DirectoryInfo(d).Name; lbox_primary_sortlist.Items.Add(dir); } if (cbox_sort_primary.Text.Length > 0) { if (Directory.Exists(global.dest + "\\" + cbox_sort_primary.Text)) { lbox_secondary_sortlist.Items.Clear(); foreach (var d in Directory.GetDirectories(global.dest + "\\" + cbox_sort_primary.Text)) { var dir = new DirectoryInfo(d).Name; lbox_secondary_sortlist.Items.Add(dir); } } lbox_secondary_sortlist.Enabled = true; } else { lbox_secondary_sortlist.Items.Clear(); lbox_secondary_sortlist.Items.Add("-- Select Primary Sort --"); lbox_secondary_sortlist.Enabled = false; } } private void select_source_dir() { if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) { global.src = folderBrowserDialog1.SelectedPath; tbox_source_dir.Text = global.src; status_msg("Opening source directory: " + global.src); btn_reload_src.Enabled = true; if (Properties.Settings.Default.src_dir != global.src) { Properties.Settings.Default.src_dir = global.src; //Properties.Settings.Default.Save(); } reload_source(); } } private void select_dest_dir() { if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) { global.dest = folderBrowserDialog1.SelectedPath; tbox_dest_dir.Text = global.dest; if (Properties.Settings.Default.dest_dir != global.dest) { Properties.Settings.Default.dest_dir = global.dest; //Properties.Settings.Default.Save(); } status_msg("Destination directory updated"); } } private void update_image() { string fname; if (global.filecount == 0) { fname = "EMPTY - Select new source"; img_preview.Visible = false; gbox_preview.Enabled = false; } else { pbar.Value = 6; pbar.Value -= 1; // This 'hack' forces an instant progress bar update stat_bar_label.Text = "Getting file info..."; stat_bar_label.Visible = true; if (global.filecount <= 1) { btn_next.Enabled = false; btn_prev.Enabled = false; } else { btn_next.Enabled = true; btn_prev.Enabled = true; } status_bar.Refresh(); fname = global.filenames[global.position]; pbar.Value += 5; stat_bar_label.Text = "Allocating memory..."; status_bar.Refresh(); Image img; using (MemoryStream img_stream = new MemoryStream()) { pbar.Value += 10; lbl_current_fname.Text = "Loading: " + Path.GetFileName(fname); //img_preview.Image = ToolStripRenderer.CreateDisabledImage(img_preview.Image); //Grey out image (affects performance negatively) img_preview.Visible = false; gbox_preview.Enabled = false; pbar.Value += 10; stat_bar_label.Text = "Analyzing image..."; status_bar.Refresh(); // Convert RAW file to JPEG in memory using (var raw_img = new MagickImage(fname)) { if (raw_img != null) { pbar.Value += 50; raw_img.AutoOrient(); img_stream.Position = 0; raw_img.Write(img_stream, ImageMagick.MagickFormat.Jpeg); //raw_img.Write(img_stream, ImageMagick.MagickFormat.Png); pbar.Value += 15; stat_bar_label.Text = "Generating preview..."; status_bar.Refresh(); } } img_stream.Position = 0; using (var bmp = new Bitmap(img_stream)) { img = new Bitmap(bmp); } pbar.Value -= 1; pbar.Value += 6; //pbar.Value -= 1; } img_preview.Image = img; stat_bar_label.Text = "Done!"; img_preview.Visible = true; gbox_preview.Enabled = true; stat_bar_label.Visible = false; GC.Collect(); GC.WaitForPendingFinalizers(); } lbl_current_fname.Text = Path.GetFileName(fname); if (global.filecount > 0) status_msg("Loaded image: " + lbl_current_fname.Text); update_counter(); pbar.Value = 0; populate_directory_lists(); } public string[] populate_file_names() { string ext = ""; if (rad_nef.Checked) ext = "nef"; if (rad_cr2.Checked) ext = "cr2"; if (rad_dng.Checked) ext = "dng"; if (rad_jpeg.Checked) ext = "jpg"; if (rad_png.Checked) ext = "png"; string[] files = Directory.GetFiles(global.src, "*." + ext); return files; } public void remove_index(int index) { for (int i = index; i < global.filenames.Length - 1; i++) { global.filenames[i] = global.filenames[i + 1]; } Array.Resize(ref global.filenames, global.filenames.Length - 1); global.filecount--; if (global.position > global.filecount - 1) { global.position = global.filecount - 1; } } protected override bool ProcessCmdKey(ref Message msg, Keys key_data) { if (tsi_enableGlobalHotkeys.Checked) { switch (key_data) { case Keys.PageUp: if (global.filecount > 1) prev_image(); return true; case Keys.PageDown: if (global.filecount > 1) next_image(); return true; case Keys.Control | Keys.Home: if (global.filecount > 1) { status_msg("Loading first image..."); global.position = 0; update_image(); } return true; case Keys.Control | Keys.End: if (global.filecount > 1) { status_msg("Loading final image..."); global.position = global.filecount - 1; update_image(); } return true; case Keys.Enter: sort_image(); return true; case Keys.Shift | Keys.Delete: delete_image(); return true; case Keys.Shift | Keys.Enter: delete_image(); return true; } } return base.ProcessCmdKey(ref msg, key_data); } public void next_image() { status_msg("Loading next image..."); if (global.position < global.filecount - 1) { global.position += 1; } else { global.position = 0; } update_image(); } public void prev_image() { status_msg("Loading previous image..."); if (global.position > 0) { global.position -= 1; } else { global.position = global.filecount - 1; } update_image(); } public void sort_image() { string prim_dir = global.dest + '\\' + cbox_sort_primary.Text; string sec_dir = prim_dir + '\\' + cbox_sort_secondary.Text; string dpath; if (global.dest == "") { status_msg("Destination directory must be set first!", 3); //System.Media.SystemSounds.Exclamation.Play(); //MessageBox.Show("Destination directory must be set\nbefore images can be sorted!", "Error: Primary Sort", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (cbox_sort_primary.Text == "") { status_msg("Primary sort field required to sort!", 3); //System.Media.SystemSounds.Exclamation.Play(); //MessageBox.Show("Primary sort directory name is required!", "Error: Primary Sort", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (cbox_sort_secondary.Text == "") { dpath = prim_dir; } else { dpath = sec_dir; if (!Directory.Exists(sec_dir)) { Directory.CreateDirectory(sec_dir); status_msg("Created directory '" + sec_dir + "'", 2); } } if (!Directory.Exists(prim_dir)) { Directory.CreateDirectory(prim_dir); status_msg("Created directory '" + prim_dir + "'", 2); } string src_fname = global.filenames[global.position]; string dst_fname = dpath + "\\" + Path.GetFileName(global.filenames[global.position]); if (rad_sort_move.Checked) { try { if (File.Exists(@dst_fname)) { if (MessageBox.Show("This file already exists in the destination directory. Would you like to overwrite it?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { File.Delete(dst_fname); status_msg("Deleted " + dst_fname, 2); } else { status_msg("Move aborted for " + src_fname, 2); } } File.Move(@src_fname, @dst_fname); status_msg("Moved " + global.filenames[global.position] + " to " + dpath); remove_index(global.position); update_image(); } catch (IOException ex) { status_msg("Unable to move " + global.filenames[global.position] + " to " + dpath + "!", 4); MessageBox.Show(ex.ToString(), "Critical: IO Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { try { if (File.Exists(@dst_fname)) { if (MessageBox.Show("This file already exists in the destination directory. Would you like to overwrite it?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { File.Delete(dst_fname); status_msg("Deleted " + dst_fname, 2); } else { status_msg("Copy aborted for " + src_fname, 2); return; } } File.Copy(@src_fname, @dst_fname); //status_msg("Copied " + lbl_current_fname.Text + " to " + dpath); status_msg("Copied " + global.filenames[global.position] + " to " + dpath); remove_index(global.position); update_image(); } catch (IOException ex) { status_msg("Unable to copy " + global.filenames[global.position] + " to " + dpath + "!", 4); MessageBox.Show(ex.ToString(), "Critical: IO Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); } // Begin Update comboboxes if (cbox_sort_primary.Items.Contains(cbox_sort_primary.Text)) { //Remove and re-add, to put it at the new end of the list string t = cbox_sort_primary.Text; cbox_sort_primary.Items.Remove(t); cbox_sort_primary.Items.Add(t); cbox_sort_primary.Text = t; } else { cbox_sort_primary.Items.Add(cbox_sort_primary.Text); } if (cbox_sort_secondary.Text.Length > 0) { if (cbox_sort_secondary.Items.Contains(cbox_sort_secondary.Text)) { //Remove and re-add, to put it at the new end of the list string t = cbox_sort_secondary.Text; cbox_sort_secondary.Items.Remove(t); cbox_sort_secondary.Items.Add(t); cbox_sort_secondary.Text = t; } else { cbox_sort_secondary.Items.Add(cbox_sort_secondary.Text); } } //Prune the lists while (cbox_sort_primary.Items.Count > global.cbox_size) { cbox_sort_primary.Items.RemoveAt(0); } while (cbox_sort_secondary.Items.Count > global.cbox_size) { cbox_sort_secondary.Items.RemoveAt(0); } // End combobox updating } } public void delete_image() { if (MessageBox.Show("Are you sure you want to delete this photo?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { string src_fname = global.filenames[global.position]; try { File.Delete(src_fname); status_msg("Deleted file: " + src_fname, 2); remove_index(global.position); update_image(); } catch (IOException ex) { status_msg("Unable to delete " + src_fname + "!", 4); MessageBox.Show(ex.ToString(), "Critical: IO Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } public void prerender_init() { if (MessageBox.Show("WARNING: Pre-rendering your photos may take a long time, and the preview image quality will be lower than normal.\n\nWould you like to proceed?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { MessageBox.Show("Pre-rendering would normally have started, but instead, nothing interesting happens."); } else { MessageBox.Show("You've chickened out and abandoned the pre-render process. Nothing interesting happens."); } } // // UI Interactions // /////////////////////////////////////////////////////// private void btn_open_dir_Click(object sender, EventArgs e) { select_dest_dir(); } private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) { select_source_dir(); } private void selectDestinationDirectoryToolStripMenuItem_Click(object sender, EventArgs e) { select_dest_dir(); } private void quitApplicationToolStripMenuItem_Click(object sender, EventArgs e) { this.Close(); } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("Photo Sorter v1.0.2\n\nThis application is Copyright \u00A9 Aaron Johnson, 2023.\n\nPlease visit https://www.skyfall.tech/ for more information."); } private void btn_open_src_dir_Click(object sender, EventArgs e) { select_source_dir(); } private void btn_prev_Click(object sender, EventArgs e) { prev_image(); } private void btn_next_Click(object sender, EventArgs e) { next_image(); } private void btn_sort_Click(object sender, EventArgs e) { sort_image(); } private void btn_reload_src_Click(object sender, EventArgs e) { reload_source(); } private void globalHotkeysListToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox .Show("Global Hotkeys\n\nPgUp == Previous image\nPgDn == Next image\nCtrl + Home == Go to first unsorted image\nCtrl + End == Go to final unsorted image\n\nEnter == Sort photo to current options\n\nShift + Delete OR Shift + Enter == Delete image\n"); } private void tsi_enableGlobalHotkeys_Click(object sender, EventArgs e) { if (tsi_enableGlobalHotkeys.Checked) Properties.Settings.Default.global_hotkeys = true; else Properties.Settings.Default.global_hotkeys = false; Properties.Settings.Default.Save(); } private void btn_delete_Click(object sender, EventArgs e) { delete_image(); } private void btn_prerender_Click(object sender, EventArgs e) { prerender_init(); } private void cbox_sortfields_Leave(object sender, EventArgs e) { populate_directory_lists(); } private void cbox_sort_primary_Enter(object sender, EventArgs e) { populate_directory_lists(); tabctrl_sort_dir.SelectedTab = tabctrl_sort_dir.TabPages["tab_primesort"]; cbox_sort_primary.Focus(); } private void cbox_sort_secondary_Enter(object sender, EventArgs e) { //if (cbox_sort_primary.Text.Length > 0) //{ populate_directory_lists(); tabctrl_sort_dir.SelectedTab = tabctrl_sort_dir.TabPages["tab_secondsort"]; cbox_sort_secondary.Focus(); //} } private void lbox_primary_sortlist_SelectItem(object sender, EventArgs e) { cbox_sort_primary.Text = lbox_primary_sortlist.SelectedItem.ToString(); populate_directory_lists(); } private void lbox_secondary_sortlist_SelectItem(object sender, EventArgs e) { cbox_sort_secondary.Text = lbox_secondary_sortlist.SelectedItem.ToString(); } private void Form1_FormClosing(Object sender, FormClosingEventArgs e) { if (MessageBox.Show("Are you sure you want to quit the Photo Sorter application?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { ArrayList cbi_primary = new ArrayList(this.cbox_sort_primary.Items); ArrayList cbi_secondary = new ArrayList(this.cbox_sort_secondary.Items); Properties.Settings.Default.cbi_primary = cbi_primary; Properties.Settings.Default.cbi_secondary = cbi_secondary; if (rad_sort_copy.Checked) { Properties.Settings.Default.copy_on_sort = true; } else { Properties.Settings.Default.copy_on_sort = false; } string ext = ""; if (rad_nef.Checked) ext = "nef"; if (rad_cr2.Checked) ext = "cr2"; if (rad_dng.Checked) ext = "dng"; if (rad_jpeg.Checked) ext = "jpg"; if (rad_png.Checked) ext = "png"; Properties.Settings.Default.img_format = ext; Properties.Settings.Default.Save(); } else { e.Cancel = true; } } } }