Sunday, April 29, 2012

KirSizer - Flex AIR Image Sizer app: Part 13

In this tutorial we will add color highlighting to the log area, as well as a colorful indicator of the operation's progress.

We'll use colors in 2 situations in the log area. The first one is to highlight errors - we'll use red color for that. The second time we will use highlighting is in the last 3 lines - the results of the operation. We will use yellow color for that.

To highlight a text fragment in a text area, we'll use the setFormatOfRange() method. We'll need to apply a TextLayoutFormat object, and we'll also need 2 integer numbers - the position of the beginning and ending anchors for the text that needs to be highlighted.

We'll create a new function for that, call it appendHighlight.

private function appendHighlight(text:String, color:uint):void {
var formatHighlight:TextLayoutFormat = new TextLayoutFormat();
formatHighlight.color = color;
var formatNormal:TextLayoutFormat = new TextLayoutFormat();
formatNormal.color = 0xaaaaaa;
var firstAnchor:int = logArea.text.length;
logArea.appendText(text);
var secondAnchor:int = logArea.text.length;
logArea.setFormatOfRange(formatHighlight, firstAnchor, secondAnchor);
logArea.setFormatOfRange(formatNormal, secondAnchor, secondAnchor);
}

As you can see, we pass a text value to the function, and the function adds the text to the logArea, while also highlighting it, using the color that we pass as the parameter. After changing the format of the error message, we revert the format to the normal one.

Now, we need to change every line that uses the logArea's appendText() method to display an Error message, or to print the results of the operation and change logArea.appendText to appendHighlight, while also passing the second color value parameter. As I said, we need to do this in every place that displays an error message or the results. I'll include full code below if you have trouble finding them.

Next, let's make a colorful text indicator that is located under the progress bar and turns yellow when the images are being resized and says "IN PROGRESS", and turns green when the operation is completed and asys "COMPLETED".

Go to the last NavigatorContent object and turn the progress bar into a group, then add a new Label object inside. Set its top and left properties so that it is positioned under the progress bar. Set its id to statusText.

<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>
<s:Group width="100%">
<mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" />
<s:Label top="20" left="200" textAlign="right" id="statusText" fontWeight="bold"/>
</s:Group>
<s:TextArea id="logArea" editable="false" width="100%" height="100%" />
<s:HGroup>
<s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" />
<s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>

In beginResize() function, in the last if statement add 2 lines that set the text and color of the statusText object using "text" property and setStyle() method:

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;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
statusText.setStyle("color", 0xffff55);
statusText.text = "IN PROGRESS";
nextAction();
}
}

In nextAction(), when the operation is completed, change the text's color to green and value to "COMPLETED":

private function nextAction():void {
if (currentNum < selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length > 0) isFolder = true;
if (folder.exists && selectedFiles.length > currentNum && selectedFiles[currentNum].type == 1 && folderArray.length == 0) {
isFolder = true;
folderArray = [];
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]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists && selectedFiles.length > currentNum && selectedFiles[currentNum].type == 1 && folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}

Full code:

<?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.text.TextFormat;
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;
import flashx.textLayout.formats.TextLayoutFormat
[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 var errorText:String;
private var successFiles:int;

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;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
statusText.setStyle("color", 0xffff55);
statusText.text = "IN PROGRESS";
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++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null && !file.exists) {
canProceed = false;
appendHighlight("ERROR: File not found\n", 0xff2222);
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null && (file.exists && file.extension.toLowerCase() != "png" && file.extension.toLowerCase() != "jpg" && file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
appendHighlight("ERROR: Incorrect extension\n", 0xff2222);
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
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();
successFiles++;

nextAction();
}

}

private function nextAction():void {
if (currentNum < selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length > 0) isFolder = true;
if (folder.exists && selectedFiles.length > currentNum && selectedFiles[currentNum].type == 1 && folderArray.length == 0) {
isFolder = true;
folderArray = [];
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]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists && selectedFiles.length > currentNum && selectedFiles[currentNum].type == 1 && folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}

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;
}
}

private function saveErrors():void {
var file:File = File.desktopDirectory.resolvePath("errors.txt");
file.browseForSave("Save error log");
file.addEventListener(Event.SELECT, errorSelect);

function errorSelect(evt:Event):void {
if (!file.nativePath.match(/^.*\.(txt)$/i)) {
file.nativePath += ".txt";
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeUTFBytes(errorText);
fileStream.close();
}
}

private function appendHighlight(text:String, color:uint):void {
var formatHighlight:TextLayoutFormat = new TextLayoutFormat();
formatHighlight.color = color;
var formatNormal:TextLayoutFormat = new TextLayoutFormat();
formatNormal.color = 0xaaaaaa;
var firstAnchor:int = logArea.text.length;
logArea.appendText(text);
var secondAnchor:int = logArea.text.length;
logArea.setFormatOfRange(formatHighlight, firstAnchor, secondAnchor);
logArea.setFormatOfRange(formatNormal, secondAnchor, secondAnchor);
}
]]>
</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>
<s:Group width="100%">
<mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" />
<s:Label top="20" left="200" textAlign="right" id="statusText" fontWeight="bold"/>
</s:Group>
<s:TextArea id="logArea" editable="false" width="100%" height="100%" />
<s:HGroup>
<s:Button id="buttonError" label="Save error log" click="saveErrors();" 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