mirror of
https://github.com/gen2brain/cbconvert
synced 2025-10-13 18:18:52 +02:00
933 lines
26 KiB
Go
933 lines
26 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
_ "embed"
|
|
"errors"
|
|
"fmt"
|
|
"image/gif"
|
|
"image/png"
|
|
"net/url"
|
|
"os"
|
|
"runtime/debug"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gen2brain/cbconvert"
|
|
"github.com/gen2brain/iup-go/iup"
|
|
)
|
|
|
|
//go:generate rsrc --ico dist/windows/icon.ico --arch amd64 -o main_windows_amd64.syso
|
|
|
|
//go:embed assets/icon.png
|
|
var appIcon []byte
|
|
|
|
//go:embed assets/loading.gif
|
|
var appLoading []byte
|
|
|
|
var appVersion string
|
|
|
|
var (
|
|
index = -1
|
|
files []cbconvert.File
|
|
)
|
|
|
|
func init() {
|
|
_ = os.Setenv("GDK_BACKEND", "x11")
|
|
|
|
if appVersion != "" {
|
|
return
|
|
}
|
|
|
|
info, ok := debug.ReadBuildInfo()
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if info.Main.Version != "" {
|
|
appVersion = info.Main.Version
|
|
}
|
|
|
|
for _, kv := range info.Settings {
|
|
if kv.Value == "" {
|
|
continue
|
|
}
|
|
if kv.Key == "vcs.revision" {
|
|
appVersion = kv.Value
|
|
if len(appVersion) > 7 {
|
|
appVersion = kv.Value[:7]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
iup.Open()
|
|
defer iup.Close()
|
|
|
|
iup.SetGlobal("UTF8MODE", "YES")
|
|
iup.SetGlobal("UTF8MODE_FILE", "YES")
|
|
|
|
img, _ := png.Decode(bytes.NewReader(appIcon))
|
|
iup.ImageFromImage(img).SetHandle("icon")
|
|
|
|
dlg := iup.Dialog(layout()).SetAttributes(fmt.Sprintf(`TITLE="CBconvert %s", ICON=icon`, appVersion)).SetHandle("dlg")
|
|
|
|
dlg.SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int {
|
|
sp := strings.Split(s, ": ")
|
|
if len(sp) > 1 {
|
|
iup.MessageError(ih, fmt.Sprintf("%s\n\n%s", sp[0], strings.Join(sp[1:], ": ")))
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}))
|
|
|
|
dlg.SetCallback("RESIZE_CB", iup.ResizeFunc(func(ih iup.Ihandle, width, height int) int {
|
|
iup.GetHandle("Preview").SetAttribute("IMAGE", "")
|
|
iup.Refresh(ih)
|
|
|
|
previewPost()
|
|
|
|
return iup.DEFAULT
|
|
}))
|
|
|
|
iup.Map(dlg)
|
|
setActive()
|
|
|
|
iup.ShowXY(dlg, iup.CENTER, iup.CENTER)
|
|
iup.MainLoop()
|
|
}
|
|
|
|
func options() cbconvert.Options {
|
|
var opts cbconvert.Options
|
|
opts.Recursive = iup.GetHandle("Recursive").GetAttribute("VALUE") == "ON"
|
|
opts.NoRGB = iup.GetHandle("NoRGB").GetAttribute("VALUE") == "ON"
|
|
opts.NoCover = iup.GetHandle("NoCover").GetAttribute("VALUE") == "ON"
|
|
opts.Size = iup.GetHandle("Size").GetInt("VALUE")
|
|
opts.OutDir = iup.GetHandle("OutDir").GetAttribute("VALUE")
|
|
opts.Suffix = iup.GetHandle("Suffix").GetAttribute("VALUE")
|
|
opts.NoConvert = iup.GetHandle("NoConvert").GetAttribute("VALUE") == "ON"
|
|
opts.NoNonImage = iup.GetHandle("NoNonImage").GetAttribute("VALUE") == "ON"
|
|
opts.Archive = strings.ToLower(iup.GetHandle("Archive").GetAttribute("VALUESTRING"))
|
|
opts.Format = strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
|
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
|
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
|
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
|
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
|
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
|
opts.Grayscale = iup.GetHandle("Grayscale").GetAttribute("VALUE") == "ON"
|
|
opts.Brightness = iup.GetHandle("Brightness").GetInt("VALUE")
|
|
opts.Contrast = iup.GetHandle("Contrast").GetInt("VALUE")
|
|
opts.Rotate = iup.GetHandle("Rotate").GetInt("VALUESTRING")
|
|
|
|
return opts
|
|
}
|
|
|
|
func setActive() {
|
|
opts := options()
|
|
count := iup.GetHandle("List").GetInt("COUNT")
|
|
|
|
if count == 0 {
|
|
iup.GetHandle("Remove").SetAttribute("ACTIVE", "NO")
|
|
iup.GetHandle("RemoveAll").SetAttribute("ACTIVE", "NO")
|
|
|
|
iup.GetHandle("Preview").SetAttribute("IMAGE", "")
|
|
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", "")
|
|
} else {
|
|
if index != -1 {
|
|
iup.GetHandle("Remove").SetAttribute("ACTIVE", "YES")
|
|
}
|
|
iup.GetHandle("RemoveAll").SetAttribute("ACTIVE", "YES")
|
|
}
|
|
|
|
if opts.OutDir == "" {
|
|
iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO`)
|
|
iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO`)
|
|
iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO`)
|
|
if count > 0 {
|
|
iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`)
|
|
iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`)
|
|
iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`)
|
|
}
|
|
} else {
|
|
if count > 0 {
|
|
iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=YES, TIP=""`)
|
|
iup.GetHandle("Cover").SetAttributes(`ACTIVE=YES, TIP=""`)
|
|
iup.GetHandle("Convert").SetAttributes(`ACTIVE=YES, TIP=""`)
|
|
} else {
|
|
iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO`)
|
|
iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO`)
|
|
iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO`)
|
|
}
|
|
}
|
|
|
|
if opts.NoConvert {
|
|
iup.GetHandle("VboxImage").SetAttribute("ACTIVE", "NO")
|
|
iup.GetHandle("VboxTransform").SetAttribute("ACTIVE", "NO")
|
|
} else {
|
|
iup.GetHandle("VboxImage").SetAttribute("ACTIVE", "YES")
|
|
iup.GetHandle("VboxTransform").SetAttribute("ACTIVE", "YES")
|
|
}
|
|
|
|
if (opts.Format == "jpeg" || opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl") && !opts.NoConvert {
|
|
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "YES")
|
|
} else {
|
|
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "NO")
|
|
}
|
|
|
|
if opts.Width != 0 && opts.Height != 0 && !opts.NoConvert {
|
|
iup.GetHandle("Fit").SetAttribute("ACTIVE", "YES")
|
|
} else {
|
|
iup.GetHandle("Fit").SetAttribute("ACTIVE", "NO")
|
|
}
|
|
}
|
|
|
|
func layout() iup.Ihandle {
|
|
return iup.Vbox(
|
|
iup.Hbox(
|
|
preview(),
|
|
iup.Hbox(
|
|
iup.Vbox(
|
|
list(),
|
|
tabs(),
|
|
).SetAttributes("NGAP=5"),
|
|
).SetAttributes("NGAP=5"),
|
|
buttons(),
|
|
).SetAttributes("NGAP=5, NMARGIN=5x5"),
|
|
|
|
iup.Label("").SetAttributes("SEPARATOR=HORIZONTAL"),
|
|
status(),
|
|
)
|
|
}
|
|
|
|
func list() iup.Ihandle {
|
|
return iup.Vbox(
|
|
iup.List().SetAttributes("EXPAND=YES, VISIBLECOLUMNS=16, VISIBLELINES=5").SetHandle("List").
|
|
SetCallback("ACTION", iup.ListActionFunc(func(ih iup.Ihandle, text string, item int, state int) int {
|
|
if state == 1 {
|
|
index = item - 1
|
|
setActive()
|
|
previewPost()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("DROPFILES_CB", iup.DropFilesFunc(func(ih iup.Ihandle, fileName string, num, x, y int) int {
|
|
dec, err := url.QueryUnescape(fileName)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
conv := cbconvert.New(options())
|
|
|
|
fs, err := conv.Files([]string{dec})
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
for _, file := range fs {
|
|
iup.SetAttribute(iup.GetHandle("List"), "APPENDITEM", fmt.Sprintf("%s (%s)", file.Name, file.SizeHuman))
|
|
files = append(files, file)
|
|
}
|
|
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
)
|
|
}
|
|
|
|
func previewPost() {
|
|
if index == -1 || len(files) == 0 {
|
|
return
|
|
}
|
|
|
|
width, height := previewSize()
|
|
iup.GetHandle("Loading").SetAttributes("VISIBLE=YES, START=YES")
|
|
if strings.ToLower(iup.GetGlobal("DRIVER")) == "motif" {
|
|
iup.GetHandle("Preview").SetAttribute("IMAGE", "")
|
|
}
|
|
|
|
opts := options()
|
|
|
|
go func(opts cbconvert.Options) {
|
|
conv := cbconvert.New(opts)
|
|
|
|
conv.Initialize()
|
|
defer conv.Terminate()
|
|
|
|
file := files[index]
|
|
img, err := conv.Preview(file.Path, file.Stat, width, height)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
iup.PostMessage(iup.GetHandle("Preview"), "", 0, img)
|
|
}(opts)
|
|
}
|
|
|
|
func previewSize() (int, int) {
|
|
var width, height int
|
|
sp := strings.Split(iup.GetHandle("Preview").GetAttribute("RASTERSIZE"), "x")
|
|
if len(sp) == 2 {
|
|
width, _ = strconv.Atoi(sp[0])
|
|
height, _ = strconv.Atoi(sp[1])
|
|
}
|
|
|
|
return width, height
|
|
}
|
|
|
|
func preview() iup.Ihandle {
|
|
return iup.Frame(
|
|
iup.Vbox(
|
|
iup.Label("").SetAttributes("EXPAND=YES, ALIGNMENT=ACENTER, MINSIZE=400x, IMAGE=cover").SetHandle("Preview").
|
|
SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int {
|
|
img := p.(cbconvert.Image)
|
|
|
|
if img.Image != nil {
|
|
iup.Destroy(iup.GetHandle("cover"))
|
|
iup.ImageFromImage(img.Image).SetHandle("cover")
|
|
ih.SetAttribute("IMAGE", "cover")
|
|
}
|
|
|
|
iup.GetHandle("Loading").SetAttributes("VISIBLE=NO, STOP=YES")
|
|
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", fmt.Sprintf("%s (%dx%d)", img.SizeHuman, img.Width, img.Height))
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
iup.Label("").SetAttributes("EXPAND=HORIZONTAL, ALIGNMENT=ACENTER").SetHandle("PreviewInfo"),
|
|
),
|
|
)
|
|
}
|
|
|
|
func tabs() iup.Ihandle {
|
|
vboxInput := iup.Vbox(
|
|
iup.Toggle(" Recurse SubDirectories").SetHandle("Recursive").
|
|
SetAttributes(`TIP="Process subdirectories recursively"`),
|
|
iup.Toggle(" Only Grayscale Images").SetHandle("NoRGB").
|
|
SetAttributes(`TIP="Do not convert images that have RGB colorspace"`),
|
|
iup.Toggle(" Exclude Cover").SetHandle("NoCover").
|
|
SetAttributes(`TIP="Do not convert the cover image"`),
|
|
iup.Toggle(" Do not Transform or Convert Images").SetHandle("NoConvert").
|
|
SetAttributes(`TIP="Copy images from archive or directory without modifications"`).
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
iup.Vbox(
|
|
iup.Label("Minimum Size (MiB):"),
|
|
iup.Text().SetAttributes(`SPIN=YES, SPINMAX=2048, VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Size").
|
|
SetAttributes(`TIP="Process only files larger than minimum size"`),
|
|
),
|
|
iup.Space().SetAttributes("EXPAND=HORIZONTAL"),
|
|
).SetHandle("VboxInput").SetAttributes("MARGIN=5x5, GAP=5")
|
|
|
|
vboxOutput := iup.Vbox(
|
|
iup.Vbox(
|
|
iup.Label("Output Directory:"),
|
|
iup.Text().SetAttributes("VISIBLECOLUMNS=16, MINSIZE=100x").SetHandle("OutDir").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
iup.Button("Browse...").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onOutputDirectory)),
|
|
),
|
|
iup.Vbox(
|
|
iup.Label("Add Suffix to Output File:"),
|
|
iup.Text().SetAttributes("VISIBLECOLUMNS=16, MINSIZE=100x").SetHandle("Suffix").
|
|
SetAttribute("TIP", "Add suffix to filename, i.e. filename_suffix.cbz"),
|
|
),
|
|
iup.Vbox(
|
|
iup.Toggle(" Remove Non-Image Files from the Archive").SetHandle("NoNonImage").
|
|
SetAttribute("TIP", "Remove .nfo, .xml, .txt files from the archive"),
|
|
),
|
|
iup.Vbox(
|
|
iup.Label("Archive Format:"),
|
|
iup.List().SetAttributes(map[string]string{
|
|
"DROPDOWN": "YES",
|
|
"VALUE": "1",
|
|
"1": "ZIP",
|
|
"2": "TAR",
|
|
}).SetHandle("Archive"),
|
|
),
|
|
).SetHandle("VboxOutput").SetAttributes("MARGIN=5x5, GAP=5")
|
|
|
|
vboxImage := iup.Vbox(
|
|
iup.Vbox(
|
|
iup.Label("Format:"),
|
|
iup.List().SetAttributes(map[string]string{
|
|
"DROPDOWN": "YES",
|
|
"VALUE": "1",
|
|
"1": "JPEG",
|
|
"2": "PNG",
|
|
"3": "TIFF",
|
|
"4": "BMP",
|
|
"5": "WEBP",
|
|
"6": "AVIF",
|
|
"7": "JXL",
|
|
}).SetHandle("Format").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
setActive()
|
|
previewPost()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
),
|
|
iup.Vbox(
|
|
iup.Label("Size:"),
|
|
iup.Hbox(
|
|
iup.Text().SetAttributes(`CUEBANNER=" width", VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Width").
|
|
SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
setActive()
|
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
|
if ih.GetAttribute("MYVALUE") != "" {
|
|
previewPost()
|
|
}
|
|
ih.SetAttribute("MYVALUE", "")
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
iup.Label("x"),
|
|
iup.Text().SetAttributes(`CUEBANNER=" height", VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Height").
|
|
SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
setActive()
|
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
|
if ih.GetAttribute("MYVALUE") != "" {
|
|
previewPost()
|
|
}
|
|
ih.SetAttribute("MYVALUE", "")
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"),
|
|
),
|
|
iup.Vbox(
|
|
iup.Toggle(" Best Fit").SetHandle("Fit").
|
|
SetAttributes(`TIP="Best fit for required width and height"`),
|
|
),
|
|
iup.Vbox(
|
|
iup.Label("Resize Filter:"),
|
|
iup.List().SetAttributes(map[string]string{
|
|
"DROPDOWN": "YES",
|
|
"VALUE": "3",
|
|
"TIP": "Linear is the bilinear filter, smooth and reasonably fast",
|
|
"1": "NearestNeighbor",
|
|
"2": "Box",
|
|
"3": "Linear",
|
|
"4": "MitchellNetravali",
|
|
"5": "CatmullRom",
|
|
"6": "Gaussian",
|
|
"7": "Lanczos",
|
|
}).SetHandle("Filter").SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(onFilterChanged)),
|
|
),
|
|
iup.Vbox(
|
|
iup.Hbox(
|
|
iup.Label("Quality: "),
|
|
iup.Label("75").SetHandle("LabelQuality"),
|
|
).SetAttributes("MARGIN=0"),
|
|
iup.Val("").SetAttributes(`MIN=0, MAX=100, VALUE=75, SHOWTICKS=10`).SetHandle("Quality").
|
|
SetAttribute("TIP", "Quality affects JPEG, WEBP, AVIF and JXL").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
iup.GetHandle("LabelQuality").SetAttribute("TITLE", ih.GetInt("VALUE"))
|
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
|
if ih.GetAttribute("MYVALUE") != "" {
|
|
previewPost()
|
|
}
|
|
ih.SetAttribute("MYVALUE", "")
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
).SetHandle("VboxQuality"),
|
|
iup.Vbox(
|
|
iup.Toggle(" Grayscale").SetHandle("Grayscale").
|
|
SetAttributes(`TIP="Convert images to grayscale (monochromatic)"`).
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
previewPost()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
),
|
|
).SetHandle("VboxImage").SetAttributes("MARGIN=5x5, GAP=5")
|
|
|
|
vboxTransform := iup.Vbox(
|
|
iup.Vbox(
|
|
iup.Hbox(
|
|
iup.Label("Brightness: "),
|
|
iup.Label("0").SetHandle("LabelBrightness"),
|
|
).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"),
|
|
iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Brightness").
|
|
SetAttributes(`TIP="Adjust the brightness of the images"`).
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
iup.GetHandle("LabelBrightness").SetAttribute("TITLE", iup.GetHandle("Brightness").GetInt("VALUE"))
|
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
|
if ih.GetAttribute("MYVALUE") != "" {
|
|
previewPost()
|
|
}
|
|
ih.SetAttribute("MYVALUE", "")
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
),
|
|
iup.Vbox(
|
|
iup.Hbox(
|
|
iup.Label("Contrast: "),
|
|
iup.Label("0").SetHandle("LabelContrast"),
|
|
).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"),
|
|
iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Contrast").
|
|
SetAttributes(`TIP="Adjust the contrast of the images"`).
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
iup.GetHandle("LabelContrast").SetAttribute("TITLE", iup.GetHandle("Contrast").GetInt("VALUE"))
|
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
|
|
|
return iup.DEFAULT
|
|
})).
|
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
|
if ih.GetAttribute("MYVALUE") != "" {
|
|
previewPost()
|
|
}
|
|
ih.SetAttribute("MYVALUE", "")
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
),
|
|
iup.Vbox(
|
|
iup.Label("Rotate:"),
|
|
iup.List().SetAttributes(map[string]string{
|
|
"DROPDOWN": "YES",
|
|
"VALUE": "1",
|
|
"1": "0",
|
|
"2": "90",
|
|
"3": "180",
|
|
"4": "270",
|
|
}).SetHandle("Rotate").
|
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
|
previewPost()
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
),
|
|
).SetHandle("VboxTransform").SetAttributes("MARGIN=5x5, GAP=5")
|
|
|
|
return iup.Tabs(
|
|
vboxInput.SetAttributes("TABTITLE=Input"),
|
|
vboxOutput.SetAttributes("TABTITLE=Output"),
|
|
vboxImage.SetAttributes("TABTITLE=Image"),
|
|
vboxTransform.SetAttributes("TABTITLE=Transform"),
|
|
).SetHandle("Tabs").SetAttributes("MINSIZE=320x400, EXPAND=HORIZONTAL")
|
|
}
|
|
|
|
func buttons() iup.Ihandle {
|
|
return iup.Vbox(
|
|
iup.Frame(
|
|
iup.Vbox(
|
|
iup.Button("Add &Files...").SetHandle("AddFiles").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onAddFiles)),
|
|
iup.Button("Add &Dir...").SetHandle("AddDir").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onAddDir)),
|
|
iup.Button("Remove").SetHandle("Remove").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onRemove)),
|
|
iup.Button("Remove All").SetHandle("RemoveAll").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onRemoveAll)),
|
|
).SetAttributes("NGAP=5"),
|
|
),
|
|
iup.Frame(
|
|
iup.Vbox(
|
|
iup.Button("Thumbnail").SetHandle("Thumbnail").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onThumbnail)),
|
|
iup.Button("Cover").SetHandle("Cover").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onCover)),
|
|
).SetAttributes("NGAP=5"),
|
|
),
|
|
iup.Frame(
|
|
iup.Vbox(
|
|
iup.Button("&Convert").SetHandle("Convert").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING").
|
|
SetCallback("ACTION", iup.ActionFunc(onConvert)),
|
|
),
|
|
),
|
|
).SetHandle("Buttons").SetAttributes("ALIGNMENT=ACENTER, NGAP=10")
|
|
}
|
|
|
|
func status() iup.Ihandle {
|
|
return iup.Hbox(
|
|
loading(),
|
|
iup.Fill(),
|
|
iup.Label("File 1 of 1").SetHandle("LabelStatus1").SetAttributes("VISIBLE=NO"),
|
|
iup.Space().SetAttribute("SIZE", "5x0"),
|
|
iup.Label("(000/000)").SetHandle("LabelStatus2").SetAttributes("VISIBLE=NO"),
|
|
iup.Space().SetAttribute("SIZE", "5x0"),
|
|
iup.ProgressBar().SetAttributes("RASTERSIZE=200x15, VISIBLE=NO").SetHandle("ProgressBar").
|
|
SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int {
|
|
switch s {
|
|
case "convert":
|
|
conv := p.(*cbconvert.Converter)
|
|
ih.SetAttributes("VALUE=0, VISIBLE=YES")
|
|
ih.SetAttribute("MAX", conv.Ncontents)
|
|
|
|
iup.GetHandle("List").SetAttributes("ACTIVE=NO")
|
|
iup.GetHandle("Tabs").SetAttributes("ACTIVE=NO")
|
|
iup.GetHandle("Buttons").SetAttributes("ACTIVE=NO")
|
|
|
|
iup.GetHandle("LabelStatus1").SetAttribute("TITLE", fmt.Sprintf("File %d of %d", conv.CurrFile, conv.Nfiles))
|
|
iup.GetHandle("LabelStatus1").SetAttributes("VISIBLE=YES")
|
|
iup.GetHandle("LabelStatus2").SetAttributes("VISIBLE=YES")
|
|
|
|
iup.Refresh(iup.GetHandle("StatusBar"))
|
|
case "start":
|
|
conv := p.(*cbconvert.Converter)
|
|
ih.SetAttributes("VALUE=0, VISIBLE=YES")
|
|
ih.SetAttribute("MAX", conv.Nfiles)
|
|
|
|
iup.GetHandle("List").SetAttributes("ACTIVE=NO")
|
|
iup.GetHandle("Tabs").SetAttributes("ACTIVE=NO")
|
|
iup.GetHandle("Buttons").SetAttributes("ACTIVE=NO")
|
|
|
|
iup.GetHandle("LabelStatus2").SetAttributes("VISIBLE=YES")
|
|
case "progress":
|
|
conv := p.(*cbconvert.Converter)
|
|
ih.SetAttribute("VALUE", conv.CurrContent)
|
|
iup.GetHandle("LabelStatus2").SetAttribute("TITLE", fmt.Sprintf("(%03d/%03d)", conv.CurrContent, conv.Ncontents))
|
|
|
|
iup.Refresh(iup.GetHandle("StatusBar"))
|
|
case "progress2":
|
|
conv := p.(*cbconvert.Converter)
|
|
ih.SetAttribute("VALUE", conv.CurrFile)
|
|
iup.GetHandle("LabelStatus2").SetAttribute("TITLE", fmt.Sprintf("(%03d/%03d)", conv.CurrFile, conv.Nfiles))
|
|
|
|
iup.Refresh(iup.GetHandle("StatusBar"))
|
|
case "finish":
|
|
iup.GetHandle("List").SetAttributes("ACTIVE=YES")
|
|
iup.GetHandle("Tabs").SetAttributes("ACTIVE=YES")
|
|
iup.GetHandle("Buttons").SetAttributes("ACTIVE=YES")
|
|
|
|
iup.GetHandle("LabelStatus1").SetAttributes(`TITLE="", VISIBLE=NO`)
|
|
iup.GetHandle("LabelStatus2").SetAttributes(`TITLE="", VISIBLE=NO`)
|
|
ih.SetAttributes("VALUE=0, VISIBLE=NO")
|
|
|
|
iup.Refresh(iup.GetHandle("StatusBar"))
|
|
|
|
iup.GetHandle("dlg").SetCallback("K_ANY", nil)
|
|
iup.GetHandle("dlg").SetCallback("CLOSE_CB", nil)
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
})),
|
|
iup.Space().SetAttribute("SIZE", "5x0"),
|
|
).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=5x5").SetHandle("StatusBar")
|
|
}
|
|
|
|
func loading() iup.Ihandle {
|
|
img, _ := gif.DecodeAll(bytes.NewReader(appLoading))
|
|
animation := iup.User()
|
|
|
|
for idx, i := range img.Image {
|
|
name := fmt.Sprintf("Loading%d", idx)
|
|
iup.ImageFromImage(i).SetHandle(name)
|
|
iup.Append(animation, iup.GetHandle(name))
|
|
}
|
|
|
|
return iup.AnimatedLabel(animation).SetAttributes("VISIBLE=NO").SetHandle("Loading")
|
|
}
|
|
|
|
func onAddFiles(ih iup.Ihandle) int {
|
|
args, err := fileDlg("Add Files", true, false)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
if len(args) > 0 {
|
|
conv := cbconvert.New(options())
|
|
|
|
fs, err := conv.Files(args)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
for _, file := range fs {
|
|
iup.SetAttribute(iup.GetHandle("List"), "APPENDITEM", fmt.Sprintf("%s (%s)", file.Name, file.SizeHuman))
|
|
files = append(files, file)
|
|
}
|
|
|
|
setActive()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onAddDir(ih iup.Ihandle) int {
|
|
args, err := fileDlg("Add Directory", false, true)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
if len(args) > 0 {
|
|
conv := cbconvert.New(options())
|
|
|
|
fs, err := conv.Files(args)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
for _, file := range fs {
|
|
iup.SetAttribute(iup.GetHandle("List"), "APPENDITEM", fmt.Sprintf("%s (%s)", file.Name, file.SizeHuman))
|
|
files = append(files, file)
|
|
}
|
|
|
|
setActive()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onRemove(ih iup.Ihandle) int {
|
|
if index == -1 || len(files) == 0 {
|
|
return iup.IGNORE
|
|
}
|
|
|
|
files = append(files[:index], files[index+1:]...)
|
|
|
|
iup.GetHandle("List").SetAttribute("REMOVEITEM", iup.GetHandle("List").GetAttribute("VALUE"))
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onRemoveAll(ih iup.Ihandle) int {
|
|
index = -1
|
|
files = make([]cbconvert.File, 0)
|
|
|
|
iup.GetHandle("List").SetAttribute("REMOVEITEM", "ALL")
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onThumbnail(ih iup.Ihandle) int {
|
|
conv := cbconvert.New(options())
|
|
conv.Nfiles = len(files)
|
|
|
|
conv.OnProgress = func() {
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress2", 0, conv)
|
|
}
|
|
|
|
var canceled = false
|
|
conv.OnCancel = func() {
|
|
canceled = true
|
|
}
|
|
|
|
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
|
if c == iup.K_ESC {
|
|
conv.Cancel()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}))
|
|
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
|
|
|
|
go func(c *cbconvert.Converter) {
|
|
c.Initialize()
|
|
defer c.Terminate()
|
|
|
|
for _, file := range files {
|
|
if canceled {
|
|
break
|
|
}
|
|
|
|
if err := c.Thumbnail(file.Path, file.Stat); err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
|
}(conv)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onCover(ih iup.Ihandle) int {
|
|
conv := cbconvert.New(options())
|
|
conv.Nfiles = len(files)
|
|
|
|
conv.OnProgress = func() {
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress2", 0, conv)
|
|
}
|
|
|
|
var canceled = false
|
|
conv.OnCancel = func() {
|
|
canceled = true
|
|
}
|
|
|
|
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
|
if c == iup.K_ESC {
|
|
conv.Cancel()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}))
|
|
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
|
|
|
|
go func(c *cbconvert.Converter) {
|
|
c.Initialize()
|
|
defer c.Terminate()
|
|
|
|
for _, file := range files {
|
|
if canceled {
|
|
break
|
|
}
|
|
|
|
if err := c.Cover(file.Path, file.Stat); err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
|
}(conv)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onConvert(ih iup.Ihandle) int {
|
|
conv := cbconvert.New(options())
|
|
conv.Nfiles = len(files)
|
|
|
|
conv.OnStart = func() {
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "convert", 0, conv)
|
|
}
|
|
|
|
conv.OnProgress = func() {
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress", 0, conv)
|
|
}
|
|
|
|
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
|
if c == iup.K_ESC {
|
|
conv.Cancel()
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
})).SetCallback("CLOSE_CB", iup.CloseFunc(func(ih iup.Ihandle) int {
|
|
if err := os.RemoveAll(conv.Workdir); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
return iup.DEFAULT
|
|
}))
|
|
|
|
go func(c *cbconvert.Converter) {
|
|
c.Initialize()
|
|
defer c.Terminate()
|
|
|
|
for _, file := range files {
|
|
if err := c.Convert(file.Path, file.Stat); err != nil {
|
|
if errors.Is(err, context.Canceled) {
|
|
if err := os.RemoveAll(c.Workdir); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
if err := os.RemoveAll(c.Workdir); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
|
}(conv)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onOutputDirectory(ih iup.Ihandle) int {
|
|
args, err := fileDlg("Output Directory", false, true)
|
|
if err != nil {
|
|
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
|
fmt.Println(err)
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
if len(args) == 1 {
|
|
iup.GetHandle("OutDir").SetAttribute("VALUE", args[0])
|
|
}
|
|
|
|
setActive()
|
|
|
|
return iup.DEFAULT
|
|
}
|
|
|
|
func onFilterChanged(ih iup.Ihandle) int {
|
|
switch ih.GetInt("VALUE") {
|
|
case 1:
|
|
ih.SetAttribute("TIP", "NearestNeighbor is the fastest resampling filter, no antialiasing")
|
|
case 2:
|
|
ih.SetAttribute("TIP", "Box filter (averaging pixels)")
|
|
case 3:
|
|
ih.SetAttribute("TIP", "Linear is the bilinear filter, smooth and reasonably fast")
|
|
case 4:
|
|
ih.SetAttribute("TIP", "MitchellNetravali is a smooth bicubic filter")
|
|
case 5:
|
|
ih.SetAttribute("TIP", "CatmullRom is a sharp bicubic filter")
|
|
case 6:
|
|
ih.SetAttribute("TIP", "Gaussian is a blurring filter that uses gaussian function, useful for noise removal")
|
|
case 7:
|
|
ih.SetAttribute("TIP", "Lanczos is a high-quality resampling filter, it's slower than cubic filters")
|
|
}
|
|
|
|
previewPost()
|
|
|
|
return iup.DEFAULT
|
|
}
|