Friday, April 27, 2012

KirSizer - Flex AIR Image Sizer app: Part 11

Today we will add the main feature for our resizing program - the resizing of images!

First we need to add the ability to select an output destination. Declare a variable destinationPicked, a String value with blank text as default:

private var destinationPicked:String = "";

Now go to the part where destination-related objects are put on screen and set the text input's id to destinationInput, and the "Browse" button's click event handler to pickDestination():

<s:Label>Output destination:</s:Label>
<s:HGroup verticalAlign="middle">
<s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" />
<s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" />
</s:HGroup>
<s:HGroup verticalAlign="middle" width="100%">
<s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" />
<s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" />
</s:HGroup>

Find the part where you create the JPG quality slider and set the id of the slider to qualitySlider:

<s:Label>Output JPG quality:</s:Label>
<s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" />

Now go to beginResize() function and add a new if statement which doesn't let the user to start resizing if the destination is not set to "the same destination" and is not picked:

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') < 0 && nameInput.text.indexOf('%num%') < 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected && destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')>=0 || testName.indexOf("\\")>=0 || testName.indexOf('?')>=0 || testName.indexOf('%')>=0 ||
testName.indexOf('*')>=0 || testName.indexOf(':')>=0 || testName.indexOf('|')>=0 || testName.indexOf('"')>=0 ||
testName.indexOf('<') >= 0 || testName.indexOf('>') >= 0 || testName.indexOf('.') >= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" < > . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
progressBar.setProgress(0, totalFiles);
nextAction();
}
}
}

Create a new function pickDestination(), which sets destinationPicked value to the selected directory's nativePath:

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}

Now let's go to resizeNext() function and work on actually exporting the result files. We're only going to have to work in the internal onTimer() function.

Firstly, let's fix the %num% wildcard changing to currentNum and not currentProgress, displaying incorrect number:

newName = newName.replace("%num%", currentProgress);

Now, before we call nextAction() in the end of the function, let's add some code that creates a BitmapData object with the picture in it, resize it using a Matrix object, encode it using an approporiate encoder (depending on newExtension value), and save the output file to the correct destination.

First we create the bitmap data object and add the image to it using its draw() object. Create a Matrix object beforehand and use its scale() method to resize the picture:

var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

Then we check what extension we are saving the file in, and create an approporiate encoder object and encode the bitmap data into a byte array:

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

Then we pick the correct destination by checking which of the destination radio buttons is currently selected, and apply the correct path to a "newfile" object. Then we create a stream to write the bytes into the file:

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();

And we are done! The program can actually resize stuff now!

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer" creationComplete="init();">
   
<fx:Declarations>
<mx:ArrayCollection id="measures">
<fx:String>%</fx:String>
<fx:String>px</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="actions">
<fx:String>Fixed width, fixed height</fx:String>
<fx:String>Fixed width, proportional height</fx:String>
<fx:String>Proportional width, fixed height</fx:String>
<fx:String>Proportional sizes to fit specified sizes</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="formats">
<fx:String>Same format as initial file</fx:String>
<fx:String>Convert all to JPG</fx:String>
<fx:String>Convert all to PNG</fx:String>
</mx:ArrayCollection>
<mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/>
<mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/>
<mx:TitleWindow id="waitWindow" title="Please wait" width="240" height="70" showCloseButton="false">
<s:Group width="100%" height="100%">
<s:Label top="10" left="10" id="waitLabel" width="220" color="0x000000" />
</s:Group>
</mx:TitleWindow>
</fx:Declarations>

<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}

#logArea{
contentBackgroundColor: #111111;
focusedTextSelectionColor: #0000ff;
fontFamily: "Courier New";
color: #aaaaaa;
}
</fx:Style>

<fx:Script>
<![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.system.ImageDecodingPolicy;
import flash.utils.ByteArray;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.controls.Image;
import mx.effects.easing.Linear;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.events.StateChangeEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import mx.managers.PopUpManager;

[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);
private var totalFiles:int;
private var currentNum:int;
private var totalErrors:int;
private var folderArray:Array = [];
private var destinationPicked:String = "";

private function init():void {
addEventListener(KeyboardEvent.KEY_DOWN, keybDown);
}

private function keybDown(evt:KeyboardEvent):void {
if (evt.ctrlKey && evt.keyCode == 65) {
var arr:Array = [];
for (var i:int = 0; i < selectedFiles.length; i++) {
arr.push(i);
}
tileList.selectedIndices = arr;
}
}

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 3:
newWidth.enabled = true;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = false;
widthMeasure.selectedIndex = 1;
heightMeasure.selectedIndex = 1;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting files...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFiles(evt.files);
PopUpManager.removePopUp(waitWindow);
}
}

private function addFolder():void {
var file:File = new File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, folderSelected);
}

private function folderSelected(evt:Event):void {
var file:File = evt.currentTarget as File;
Alert.show("Do you want to select subfolders too?", "Recursive selection?", Alert.YES | Alert.NO, null, warningClose);

function warningClose(ev:CloseEvent):void {
if (ev.detail == Alert.YES) {
startFolder(file, true);
}
if (ev.detail == Alert.NO) {
startFolder(file, false);
}
}
}

private function startFolder(file:File, recurs:Boolean):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting folders...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFolder(file, recurs);
PopUpManager.removePopUp(waitWindow);
}
}

private function doFiles(files:Array):void {
for (var i:int = 0; i < files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u < selectedFiles.length; u++) {
if (selectedFiles[u].type == 0 && selectedFiles[u].path == files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem({type:0, path:files[i].nativePath});
}
updateTotalFiles();
}

private function doFolder(file:File, isRecursive:Boolean):void {
var picturesInFolder:int = 0;
var childFiles:Array = file.getDirectoryListing();
for (var i:int = 0; i < childFiles.length; i++) {
if (childFiles[i].extension == "png" || childFiles[i].extension == "jpg" || childFiles[i].extension == "jpeg") {
picturesInFolder++;
}
if (childFiles[i].isDirectory && isRecursive) {
doFolder(childFiles[i], true);
}
}
if (picturesInFolder > 0) {
var alreadySelected:Boolean = false;
for (var a:int = 0; a < selectedFiles.length; a++) {
if (selectedFiles[a].type == 1 && selectedFiles[a].path == file.nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem( { type:1, path:file.nativePath, name:file.name, num:picturesInFolder } );
updateTotalFiles();
}
}

private function updateTotalFiles():void {
totalFiles = 0;
for (var i:int = 0; i < selectedFiles.length; i++) {
if (selectedFiles[i].type==1) {
totalFiles += selectedFiles[i].num;
}else {
totalFiles++;
}
}
labelSelected.text = totalFiles + " files selected";
}

private function removeSelected():void {
while (tileList.selectedItems.length > 0) {
selectedFiles.removeItemAt(tileList.selectedIndices[0]);
}
updateTotalFiles();
}

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') < 0 && nameInput.text.indexOf('%num%') < 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected && destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')>=0 || testName.indexOf("\\")>=0 || testName.indexOf('?')>=0 || testName.indexOf('%')>=0 ||
testName.indexOf('*')>=0 || testName.indexOf(':')>=0 || testName.indexOf('|')>=0 || testName.indexOf('"')>=0 ||
testName.indexOf('<') >= 0 || testName.indexOf('>') >= 0 || testName.indexOf('.') >= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" < > . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
progressBar.setProgress(0, totalFiles);
nextAction();
}
}
}

private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) currentNum++;
}

logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (!file.exists) {
canProceed = false;
logArea.appendText("ERROR: File not found\n");
totalErrors++;
nextAction();
}


// Error: bad extension
if (file.exists && file.extension.toLowerCase() != "png" && file.extension.toLowerCase() != "jpg" && file.extension.toLowerCase() != "jpeg") {
canProceed = false;
logArea.appendText("ERROR: Incorrect extension\n");
totalErrors++;
nextAction();
}

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}

function onTimer(evt:TimerEvent):void {
// new name
var newName:String = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
var newExtension:String;
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
var currentW:int = loader.content.width;
var currentH:int = loader.content.height;
var newW:int;
var newH:int;
var ratio:Number;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH > newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();

nextAction();
}

}

private function nextAction():void {
if (currentNum < selectedFiles.length) {
var isFolder:Boolean = false;
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length > 0) isFolder = true;
if (selectedFiles.length > currentNum && selectedFiles[currentNum].type == 1 && folderArray.length == 0) {
isFolder = true;
folderArray = [];
var folder:File = new File(selectedFiles[currentNum].path);
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i < allContent.length; i++) {
if (allContent[i].isDirectory == false && (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
}
resizeNext(isFolder);
}else {
logArea.appendText("Operation complete!");
buttonError.enabled = true;
buttonReturn.enabled = true;
}
}

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}
]]>
</fx:Script>
   
<mx:ViewStack id="contentStack" width="100%" height="100%" >
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Label id="labelSelected">0 files selected</s:Label>
<mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" allowMultipleSelection="true" selectionColor="0xff0000" rollOverColor="0xff8888" />
<s:Button label="Add folder" width="100%" click="addFolder();" />
<s:Button label="Add files" width="100%" click="addFiles();" />
<s:Button label="{'Remove ' + tileList.selectedItems.length + ' items'}" width="100%" enabled="{tileList.selectedItems.length>0}" click="removeSelected();" />
<s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" />

<s:Label>Resize options:</s:Label>

<mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
<s:HGroup verticalAlign="middle">
<s:Label width="50">Width:</s:Label>
<s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" />
<mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
</s:HGroup>

<s:HGroup verticalAlign="middle">
<s:Label width="50">Height:</s:Label>
<s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/>
<mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
</s:HGroup>

<s:Label/>

<s:Label>Output file names:</s:Label>
<s:HGroup verticalAlign="middle">
<s:TextInput id="nameInput" width="240" text="%initialName%" />
<s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" />
</s:HGroup>

<s:Label/>

<s:Label>Output destination:</s:Label>
<s:HGroup verticalAlign="middle">
<s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" />
<s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" />
</s:HGroup>
<s:HGroup verticalAlign="middle" width="100%">
<s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" />
<s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" />
</s:HGroup>

<s:Label/>

<s:Label>Output format:</s:Label>
<mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>

<s:Label/>

<s:Label>Output JPG quality:</s:Label>
<s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" />

<s:Label/>

<s:Button label="Resize" width="100%" click="beginResize();" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="280" top="10" left="10">
<s:VGroup width="100%" height="410" gap="20">
<s:Label fontSize="20" width="100%" fontWeight="bold">Output file names</s:Label>
<s:Label width="100%">You can build names for output files using provided wildcards and combining them with text.</s:Label>
<s:Label width="100%">For example, "%initialName%_new" will generate names like "pic_new.jpg", "img_new.png", etc.</s:Label>
<s:Label width="100%">Available wildcards are:</s:Label>
<s:Label fontSize="18" width="100%" fontWeight="bold">%initialName%</s:Label>
<s:Label width="100%">Puts the initial name of the file that's being resized.</s:Label>
<s:Label fontSize="18" width="100%" fontWeight="bold">%num%</s:Label>
<s:Label width="100%">Gives each file a unique id number starting from 1.</s:Label>
</s:VGroup>
<s:Button label="Back" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="280" height="440" top="10" left="10">
<s:Label width="100%" color="0xff3333" fontWeight="bold">Do not add, remove or rename selected files.</s:Label>
<mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" />
<s:TextArea id="logArea" editable="false" width="100%" height="100%" />
<s:HGroup>
<s:Button id="buttonError" label="Save error log" width="136" />
<s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment