Wednesday, December 21, 2011

Creating a Flex AIR text editor: Part 72

In this tutorial we will rework our Recent files feature to put it into the sidePane instead of the native menu.

You must be wondering right now - why do that? It works perfectly under the native menu. Well, it almost does. Unfortunately though, native menu dosen't support certain characters to be in the menu items, that can be found in the file names though, for example, the underscore "_" character.

As far as I know it is impossible to fix this in the native menu, so I decided to move the whole contruction to the side panel. Let's get started.

Go to the view stack object which has all the NavigatorContent blocks for the side pane content, and add a new NavigatorContent. Inside of it, add a label and, most importantly, a List object with an id of recentList, dataProvider to pref_recent, labelField to label and change event handler to selectRecent().

<s:NavigatorContent id="recent">
<s:VGroup height="100%" width="100%">
<s:Label text="Recently closed files:"/>
<s:List id="recentList" width="{sideContentWidth}" height="100%" 
dataProvider="{pref_recent}" labelField="label" change="selectRecent();" />
</s:VGroup>
</s:NavigatorContent>

Add an element to the sidePaneData array in the declarations tags, specify a tool tip and icon. The icon i chose is folder_page_white.png from Famfamfam's Silk icon set, and it is located in my lib folder.

<mx:ArrayCollection id="sidePaneData">
<fx:Object icon="@Embed('../lib/page.png')" tip="Tab management" />
<fx:Object icon="@Embed('../lib/folder_magnify.png')" tip="File browsing" />
<fx:Object icon="@Embed('../lib/book.png')" tip="Snippets" />
<fx:Object icon="@Embed('../lib/find.png')" tip="Search and replace" />
<fx:Object icon="@Embed('../lib/folder_page_white.png')" tip="Recent files" />
</mx:ArrayCollection>

Now add an element to the sidePaneTabHeadings array with the heading:

<mx:ArrayCollection id="sidePaneTabHeadings">
<fx:String>Tab management</fx:String>
<fx:String>File browsing</fx:String>
<fx:String>Snippets</fx:String>
<fx:String>Search and replace</fx:String>
<fx:String>Recent files:</fx:String>
</mx:ArrayCollection>

Up until now, we had a function called displayRecent(). Delete it, and delete the lines that call this function in the code.

Our pref_recent variable will not be an array anymore, but an ArrayCollection.

[Bindable]
private var pref_recent:ArrayCollection = new ArrayCollection();

So in the init function, find the piece of code that sets the preferences variables and update the setting of pref_recent to a new array collection object with the array data of preferences.data.recent:

// Set preferences loaded from local storage
pref_wrap = preferences.data.wrap;
pref_status = preferences.data.status;
pref_toolbar = preferences.data.toolbar;
pref_sidepane = preferences.data.sidepane;
pref_fontsettings = preferences.data.fontsettings;
pref_linecount = preferences.data.linecount;
pref_recent = new ArrayCollection(preferences.data.recent);

In savePreferences, use toArray() method to turn the pref_recent array collection into an array when passing it to the shared object:

private function savePreferences():void {
preferences.data.wrap = pref_wrap;
preferences.data.status = pref_status;
preferences.data.toolbar = pref_toolbar;
preferences.data.fontsettings = pref_fontsettings;
preferences.data.sidepane = pref_sidepane;
preferences.data.linecount = pref_linecount;
preferences.data.recent = pref_recent.toArray();
preferences.flush();
}

We're going to leave our Recent item in the native menu, but it will have a different functionality now - it will now simply call an openRecent() function which will open the sidepane and the Recent tab in it:

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(void);
(evt.item.@label == "Open")?(doOpen()):(void);
(evt.item.@label == "Save")?(doSave(tabSelectedIndex)):(void);
(evt.item.@label == "Save As")?(doSaveAs(textArea.text, tabSelectedIndex)):(void);
(evt.item.@label == "Save All")?(doSaveAll()):(void);
(evt.item.@label == "Word wrap")?(pref_wrap = !pref_wrap):(void);
(evt.item.@label == "Cut")?(doCut()):(void);
(evt.item.@label == "Copy")?(doCopy()):(void);
(evt.item.@label == "Paste")?(doPaste()):(void);
(evt.item.@label == "Select all")?(doSelectall()):(void);
(evt.item.@label == "Status bar")?(pref_status = !pref_status):(void);
(evt.item.@label == "Tool bar")?(pref_toolbar = !pref_toolbar):(void);
(evt.item.@label == "Side pane")?(pref_sidepane = !pref_sidepane):(void);
(evt.item.@label == "Line count")?(pref_linecount = !pref_linecount):(void);
(evt.item.@label == "Font...")?(doFont()):(void);
(evt.item.@label == "Print")?(doPrint()):(void);
(evt.item.@label == "Undo")?(doUndo()):(void);
(evt.item.@label == "Redo")?(doRedo()):(void);
(evt.item.@label == "Find/Replace...")?(doFind()):(void);
(evt.item.@label == "Recent")?(openRecent()):(void);
("@encoding" in evt.item)?(doEncode(evt.item.@encoding, evt.item.@ind)):(void);
savePreferences();
updateStatus();
if (pref_wrap) {
pref_linecount = false;
}
updateTextSize();
countLines();
}

The openRecent() function:

private function openRecent():void{
pref_sidepane = true;
sidePaneButtons.selectedIndex = 4;
}

We need to update addRecent() function to change certain methods, for example, splice to removeItemAt, etc. Also, because the order is different from the XML, we remove the last item and when we add an item, we add it in the beginning.

private function addRecent(obj:Object):void {
for (var i:int = 0; i < pref_recent.length; i++) {
// remove if location is the same
if (pref_recent[i].location == obj.location) {
pref_recent.removeItemAt(i);
break;
}
}
pref_recent.addItemAt( { label:obj.title, location:obj.location }, 0);
if (pref_recent.length > 20) {
pref_recent.removeItemAt(pref_recent.length-1);
}
savePreferences();
}

Now, the selectRecent() function that is called when the user selects an item in the list. The code remains the same except for the fact that we don't have a parameter now and the location variable is now set to the location property of the needed element in the array, like this:

private function selectRecent():void {
var location:String = pref_recent[recentList.selectedIndex].location;
if (new File(location).exists) {
loadFile(new File(location));
}else {
Alert.show("File not found at specified location.","Oops!");
}
}

And that is all. The recent feature now works, but in side pane! Now not only does it work and display correctly, but it is also faster and easier to use.

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"
   xmlns:custom="*"
   creationComplete="init();" title="Kirpad" showStatusBar="{pref_status}"
   minWidth="400" minHeight="200" height="700" width="900">
   
<s:menu>
<mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" />
</s:menu>

<fx:Script>
<![CDATA[
import flash.data.SQLConnection;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NativeWindowBoundsEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.net.SharedObject;
import flashx.textLayout.accessibility.TextAccImpl;
import flashx.textLayout.edit.EditManager;
import flashx.textLayout.edit.TextScrap;
import flashx.textLayout.formats.TextLayoutFormat;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.controls.TextArea;
import mx.events.FlexNativeMenuEvent;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.elements.Configuration;
import flash.system.System;
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flash.ui.Mouse;
import mx.events.CloseEvent;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;
import mx.events.ResizeEvent;
import mx.core.FlexGlobals;
import mx.printing.FlexPrintJob;
import mx.printing.FlexPrintJobScaleType;
import flashx.undo.UndoManager;
import flashx.textLayout.operations.UndoOperation;
import flash.data.SQLConnection;
import flash.events.SQLEvent;
import flash.data.SQLStatement;
import flash.data.SQLResult;
import flash.data.SQLMode;
import flash.net.Responder;
import XML;
import XMLList;

private var preferences:SharedObject = SharedObject.getLocal("kirpadPreferences");
[Bindable]
private var pref_wrap:Boolean = true;
[Bindable]
private var pref_status:Boolean = true;
[Bindable]
private var pref_toolbar:Boolean = true;
[Bindable]
private var pref_sidepane:Boolean = true;
[Bindable]
private var pref_linecount:Boolean = true;
[Bindable]
public var pref_fontsettings:Object = new Object();
[Bindable]
private var pref_recent:ArrayCollection = new ArrayCollection();

private var initHeight:Number;
private var heightFixed:Boolean = false;

private var statusMessage:String;
[Bindable]
private var textHeight:Number;
[Bindable]
private var textWidth:Number;
[Bindable]
private var textY:Number;
[Bindable]
private var textX:Number;
[Bindable]
private var tabY:Number;
[Bindable]
private var sidePaneY:Number;
[Bindable]
private var sidePaneX:Number;
[Bindable]
private var sidePaneHeight:Number;
[Bindable]
private var sidePaneWidth:Number = 180;
[Bindable]
private var sideContentWidth:Number = 170;
[Bindable]
private var tabWidth:Number;
[Bindable]
private var lineCountWidth:Number = 40;
[Bindable]
private var lineNumbers:String = "1";
[Bindable]
private var lineDisplayedNum:int = 1;

[Bindable]
private var tabSelectedIndex:int = 0;

[Bindable]
private var canUndo:Boolean = false;
[Bindable]
private var canRedo:Boolean = false;
[Bindable]
private var canPaste:Boolean = false;

private var previousTextInOperation:String = "";
private var currentTextInOperation:String = "";

private var previousIndex:int = 0;
private var rightclickTabIndex:int = 0;
private var untitledNum:int = 0;
private var tabsToClose:int = 0;
private var closeAfterConfirm:Boolean = false;

public var fontWindow:FontWindow = new FontWindow();
public var snippetWindow:SnippetWindow = new SnippetWindow();

private var undoManager:UndoManager;
private var editManager:EditManager;

private var saveWait:Boolean = false;
private var saveAsQueue:Array = [];

public var connection:SQLConnection;
public var connection2:SQLConnection;

private var snippetData:Array;
private var categoryData:Array;

[Bindable]
public var snippetXML:XMLList;

private var searchResults:Array = [];
private var selectedResult:int = 0;

private var currentEncoding:String = "utf-8";
[Bindable]
private var enc:ArrayCollection = new ArrayCollection([true, false, false]);

private function init():void {
// Create a listener for every frame
addEventListener(Event.ENTER_FRAME, everyFrame);

// Set initHeight to the initial height value on start
initHeight = height;

//preferences.data.firsttime = null;

// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.wrap = false;
preferences.data.status = true;
preferences.data.toolbar = true;
preferences.data.sidepane = true;
preferences.data.linecount = true;
preferences.data.recent = [];
preferences.data.fontsettings = {fontfamily:"Lucida Console", fontsize:14, fontstyle:"normal", fontweight:"normal", fontcolor:0x000000, bgcolor:0xffffff};
preferences.flush();
}

// Set preferences loaded from local storage
pref_wrap = preferences.data.wrap;
pref_status = preferences.data.status;
pref_toolbar = preferences.data.toolbar;
pref_sidepane = preferences.data.sidepane;
pref_fontsettings = preferences.data.fontsettings;
pref_linecount = preferences.data.linecount;
pref_recent = new ArrayCollection(preferences.data.recent);

// Allow insertion of tabs
var textFlow:TextFlow = textArea.textFlow;
var config:Configuration = Configuration(textFlow.configuration);
config.manageTabKey = true;

// Set status message
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Kirpad initialized";
updateStatus();

// Close all sub-windows if main window is closed
addEventListener(Event.CLOSING, onClose);

// Add listener for the event that is dispatched when new font settings are applied
fontWindow.addEventListener(Event.CHANGE, fontChange);

// Update real fonts with the data from the settings values
updateFonts();

// Create a listener for resizing
addEventListener(NativeWindowBoundsEvent.RESIZE, onResize);

// Context menu declaration for the tabbar control
var cm_close:ContextMenuItem = new ContextMenuItem("Close tab");
cm_close.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextClose);
var cm_closeother:ContextMenuItem = new ContextMenuItem("Close other tabs");
cm_closeother.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextCloseOther);
var cm_save:ContextMenuItem = new ContextMenuItem("Save");
cm_save.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextSave);
var cm_duplicate:ContextMenuItem = new ContextMenuItem("Duplicate");
cm_duplicate.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextDuplicate);

var cm:ContextMenu = new ContextMenu();
cm.items = [cm_close, cm_closeother, cm_save, cm_duplicate];
cm.hideBuiltInItems();
tabBar.contextMenu = cm;
tabBar.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, tabRightClick);

// Context menu declaration for the tab management list control
sideList.contextMenu = cm;
sideList.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, listRightClick);

// Listen to keyboard
addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);

// Undo management
undoManager = new UndoManager();
editManager = new EditManager(undoManager);
textArea.textFlow.interactionManager = editManager;

// Select first tab
tabChange();

// Database management
var dbFile:File = File.applicationStorageDirectory.resolvePath("database.db");
connection = new SQLConnection();
connection2 = new SQLConnection();
connection2.open(dbFile, SQLMode.CREATE);
connection.addEventListener(SQLEvent.OPEN, onOpen);
connection.openAsync(dbFile, SQLMode.CREATE);
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(void);
(evt.item.@label == "Open")?(doOpen()):(void);
(evt.item.@label == "Save")?(doSave(tabSelectedIndex)):(void);
(evt.item.@label == "Save As")?(doSaveAs(textArea.text, tabSelectedIndex)):(void);
(evt.item.@label == "Save All")?(doSaveAll()):(void);
(evt.item.@label == "Word wrap")?(pref_wrap = !pref_wrap):(void);
(evt.item.@label == "Cut")?(doCut()):(void);
(evt.item.@label == "Copy")?(doCopy()):(void);
(evt.item.@label == "Paste")?(doPaste()):(void);
(evt.item.@label == "Select all")?(doSelectall()):(void);
(evt.item.@label == "Status bar")?(pref_status = !pref_status):(void);
(evt.item.@label == "Tool bar")?(pref_toolbar = !pref_toolbar):(void);
(evt.item.@label == "Side pane")?(pref_sidepane = !pref_sidepane):(void);
(evt.item.@label == "Line count")?(pref_linecount = !pref_linecount):(void);
(evt.item.@label == "Font...")?(doFont()):(void);
(evt.item.@label == "Print")?(doPrint()):(void);
(evt.item.@label == "Undo")?(doUndo()):(void);
(evt.item.@label == "Redo")?(doRedo()):(void);
(evt.item.@label == "Find/Replace...")?(doFind()):(void);
(evt.item.@label == "Recent")?(openRecent()):(void);
("@encoding" in evt.item)?(doEncode(evt.item.@encoding, evt.item.@ind)):(void);
savePreferences();
updateStatus();
if (pref_wrap) {
pref_linecount = false;
}
updateTextSize();
countLines();
}

private function savePreferences():void {
preferences.data.wrap = pref_wrap;
preferences.data.status = pref_status;
preferences.data.toolbar = pref_toolbar;
preferences.data.fontsettings = pref_fontsettings;
preferences.data.sidepane = pref_sidepane;
preferences.data.linecount = pref_linecount;
preferences.data.recent = pref_recent.toArray();
preferences.flush();
}

private function doCut():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
insertText("");
}

private function doCopy():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
}

private function doPaste():void{
var myClip:Clipboard = Clipboard.generalClipboard;
var pastedText:String = myClip.getData(ClipboardFormats.TEXT_FORMAT) as String;
insertText(pastedText);
}

private function doSelectall():void {
textArea.selectAll();
}

private function insertText(str:String):void {
editManager.insertText(str);
}

private function cursorFix():void{
Mouse.cursor = "ibeam";
}

private function everyFrame(evt:Event):void {
if (!heightFixed && height==initHeight) {
height = initHeight - 20;
if (height != initHeight) {
heightFixed = true;
updateTextSize();
}
}
updateLineScroll();
if (sideList.selectedIndices.length==0) {
sideList.selectedIndex = tabSelectedIndex;
}
canPaste = Clipboard.generalClipboard.hasFormat(ClipboardFormats.TEXT_FORMAT);
}

private function onResize(evt:ResizeEvent):void {
updateTextSize();
}

private function updateTextSize():void {
tabY = (toolBar.visible)?(toolBar.height):(0);
textX = (pref_linecount)?(lineCountWidth):(0);
var statusHeight:Number = (pref_status)?(statusBar.height):(0);
textWidth = (pref_sidepane)?(width - sidePaneWidth - textX):(width - textX);
tabWidth = textWidth + textX;
var tabbarScrollHeight:Number = (tabData.length * 170 > tabWidth)?(15):(0);
textY = tabBar.height + tabY + tabbarScrollHeight;
textHeight = height - textY - statusHeight;
focusManager.setFocus(textArea);
sidePaneHeight = textHeight + tabBar.height + tabbarScrollHeight;
sidePaneY = textY - tabBar.height - tabbarScrollHeight;
sidePaneX = width - sidePaneWidth;
}

private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = str + "\t" + statusMessage;
}

private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("\n");
var line:int = lines.length;
var col:int = lines[lines.length - 1].length + 1;

return "Ln " + line + ", Col " + col;
}

private function doFont():void{
fontWindow.open();
fontWindow.activate();
fontWindow.visible = true;
fontWindow.setValues(pref_fontsettings.fontsize, pref_fontsettings.fontfamily, pref_fontsettings.fontstyle, pref_fontsettings.fontweight, pref_fontsettings.fontcolor, pref_fontsettings.bgcolor);
}

private function onClose(evt:Event):void {
if(!closeAfterConfirm){
evt.preventDefault();
var allWindows:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 1; i < allWindows.length; i++)
{
allWindows[i].close();
}

// Check if there are any unsaved tabs
var needSaving:Boolean = false;
tabsToClose = 0;

for (var u:int = 0; u < tabData.length; u++) {
if (tabData[u].saved == false) {
needSaving = true;
tabsToClose++;
}
}

// If there are unsaved tabs, dont close window yet, set closeAfterConfirm to true and close all tabs
if (needSaving) {
closeAfterConfirm = true;
for (var t:int = 0; t < tabData.length; t++) {
closeTab(t);
}
}
if (!needSaving) {
removeEventListener(Event.CLOSING, onClose);
FlexGlobals.topLevelApplication.close();
}
}
}

private function fontChange(evt:Event):void{
pref_fontsettings.fontfamily = fontWindow.fontCombo.selectedItem.fontName;
pref_fontsettings.fontsize = fontWindow.sizeStepper.value;

if (fontWindow.styleCombo.selectedIndex == 0) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 1) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 2) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "bold";
}
if (fontWindow.styleCombo.selectedIndex == 3) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "bold";
}

pref_fontsettings.fontcolor = fontWindow.colorPicker.selectedColor;
pref_fontsettings.bgcolor = fontWindow.bgColorPicker.selectedColor;

savePreferences();
updateFonts();
}

private function updateFonts():void{
textArea.setStyle("fontFamily", pref_fontsettings.fontfamily);
textArea.setStyle("fontSize", pref_fontsettings.fontsize);
textArea.setStyle("fontStyle", pref_fontsettings.fontstyle);
textArea.setStyle("fontWeight", pref_fontsettings.fontweight);
textArea.setStyle("color", pref_fontsettings.fontcolor);
textArea.setStyle("contentBackgroundColor", pref_fontsettings.bgcolor);

lineCount.setStyle("fontFamily", pref_fontsettings.fontfamily);
lineCount.setStyle("fontSize", pref_fontsettings.fontsize);
lineCount.setStyle("fontStyle", pref_fontsettings.fontstyle);
lineCount.setStyle("fontWeight", pref_fontsettings.fontweight);
lineCount.setStyle("color", pref_fontsettings.fontcolor);
lineCount.setStyle("contentBackgroundColor", pref_fontsettings.bgcolor);
updateSearch();
}

private function onTabClose(evt:Event):void {
var tabWidth:Number = tabBar.width / tabData.length;
var cIndex:int = Math.floor(tabBar.mouseX / tabWidth);
tabSelectedIndex = cIndex;
tabChange();
closeTab(tabSelectedIndex);
}

private function onListClose(evt:Event):void {
tabSelectedIndex = sideList.selectedIndex;
tabChange();
closeTab(tabSelectedIndex);
}

private function closeTab(index:int):void {
if (tabData[index].location == "" && tabData[index].textData == "" && tabData.length == 1) {
FlexGlobals.topLevelApplication.close();
} else
if (tabData[index].location == "" && tabData[index].textData == "") {
tabsToClose--;
removeTab(index);
} else
if (tabData[index].saved) {
removeTab(index);
} else
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", "Confirmation", Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
tabsToClose--;
if (evt.detail == Alert.YES) {
tabSelectedIndex = index;
tabChange();
removeTab(index, true);
doSave(index, false);
}else {
removeTab(index);
}
}
}

private function removeTab(index:int, waitForSave:Boolean = false):void {
if (tabData[index].location != "") {
addRecent(tabData[index]);
}
if(!closeAfterConfirm){
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false, location:""} );
}
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Tab closed: " + tabData[index].title;
updateStatus();
if (tabData.length > index+1) {
tabSelectedIndex = index;
}else
if (tabData.length <= index+1) {
tabSelectedIndex = tabData.length-2;
}
tabData.removeItemAt(index);
previousIndex = tabSelectedIndex;

// refresh tabBar tabs to clean component glitch
tabBar.dataProvider = new ArrayCollection([]);
tabBar.dataProvider = tabData;
tabBar.selectedIndex = tabSelectedIndex;

textArea.text = tabData[tabSelectedIndex].textData;
textArea.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
}
if (closeAfterConfirm && tabsToClose == 0 && waitForSave == false) {
FlexGlobals.topLevelApplication.close();
}
if (waitForSave) {
saveWait = true;
}
countLines();
updateTextSize();
undoManager.clearAll();
textChange();
}

private function doNew():void {
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] New tab created";
updateStatus();
untitledNum++;
tabData.addItem( { title:"Untitled("+untitledNum+")", textData:"", saved:false, location:""} );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}

public function tabChange(from:String = "none", ind:int = 0, createOperation:Boolean = true):void {
if (from == "tabbar") {
tabSelectedIndex = tabBar.selectedIndex;
sideList.selectedIndex = tabSelectedIndex;
}
if (from == "sidelist") {
tabSelectedIndex = sideList.selectedIndex;
tabBar.selectedIndex = tabSelectedIndex;
}
if (from == "operation") {
tabSelectedIndex = ind;
}
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
if (createOperation) {
var operation:TabOperation = new TabOperation(previousIndex, tabSelectedIndex, undoManager);
undoManager.pushUndo(operation);
}
previousIndex = tabSelectedIndex;
textArea.text = tabData[tabSelectedIndex].textData;
textArea.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
updateStatus();
countLines();
textChange();
changeTitle();
updateSearch();
}

private function tabContextClose(evt:ContextMenuEvent):void{
closeTab(rightclickTabIndex);
}

private function tabContextCloseOther(evt:ContextMenuEvent):void {
var len:int = tabData.length-1;
for (var i:int = len; i >= 0; i--) {
if (i != rightclickTabIndex && (tabData[i].saved || (!tabData[i].saved && tabData[i].textData=="" && tabData[i].location==""))) {
closeTab(i);
}
}

len = tabData.length-1;
for (var u:int = 0; u < len; u++) {
if (u != rightclickTabIndex) {
closeTab(u);
}
}
}

private function tabContextSave(evt:ContextMenuEvent):void {
tabSelectedIndex = rightclickTabIndex;
tabChange();
doSave(rightclickTabIndex);
}

private function tabContextDuplicate(evt:ContextMenuEvent):void {
tabSelectedIndex = rightclickTabIndex;
tabChange();
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + tabData[rightclickTabIndex].title + " duplicated";
updateStatus();
tabData.addItem( { title:"Copy of " + tabData[rightclickTabIndex].title, textData: tabData[rightclickTabIndex].textData, saved:false, location:""} );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}

private function tabRightClick(evt:MouseEvent):void {
var tabWidth:Number = tabBar.width / tabData.length;
var rcIndex:int = Math.floor(tabBar.mouseX / tabWidth);
rightclickTabIndex = rcIndex;
}

private function listRightClick(evt:MouseEvent):void {
var tabHeight:Number = 20;
var rcIndex:int = Math.floor((sideList.mouseY + sideList.scroller.verticalScrollBar.value) / tabHeight);
rightclickTabIndex = rcIndex;
}

private function onKeyDown(evt:KeyboardEvent):void{
if (evt.ctrlKey) {
// Ctrl+TAB - next tab
if (evt.keyCode == 9 && !evt.shiftKey) {
if (tabData.length - tabSelectedIndex > 1) {
tabSelectedIndex++;
tabChange();
}
}
// Ctrl+Shift+TAB - previous tab
if (evt.keyCode == 9 && evt.shiftKey) {
if (tabSelectedIndex > 0) {
tabSelectedIndex--;
tabChange();
}
}
// Ctrl+number (1-8) - go to numbered tab
if (evt.keyCode >= 49 && evt.keyCode <= 56) {
var num:int = evt.keyCode - 48;
if (tabData.length > num - 1) {
tabSelectedIndex = num - 1;
tabChange();
}
}
// Ctrl+9 - go to last tab
if (evt.keyCode == 57) {
tabSelectedIndex = tabData.length - 1;
tabChange();
}
}
}

private function closeSidePane():void {
if(searchInput != null){
searchInput.text = "";
}
updateSearch();
pref_sidepane = !pref_sidepane
savePreferences();
updateTextSize();
}

private function countLines():void {
if (pref_linecount && !pref_wrap) {
var totalLines:int = textArea.text.split("\n").length;
if (totalLines != lineDisplayedNum) {
updateTextSize();
updateLineCount(totalLines, totalLines-lineDisplayedNum, lineDisplayedNum);
lineDisplayedNum = totalLines;
}
}
}

private function updateLineCount(total:int, difference:int, current:int):void {
if (difference > 0) {
for (var i:int = current + 1; i < (total+1); i++) {
lineNumbers += "\n" + (i);
}
}
if (difference < 0) {
var charsInTheEnd:int = 0;
for (var u:int = 0; u < -difference; u++) {
charsInTheEnd += ((current - u).toString().length + 1);
}
lineNumbers = lineCount.text.substring(0, lineCount.text.length - charsInTheEnd);
}
}

private function updateLineScroll():void{
lineCount.scroller.verticalScrollBar.value = textArea.scroller.verticalScrollBar.value;
}

private function doPrint():void {
var printJob:FlexPrintJob = new FlexPrintJob();
if (!printJob.start()) return;
tempText.visible = true;
tempText.setStyle("lineBreak", "toFit");
tempText.text = textArea.text;
tempText.width = printJob.pageWidth;
tempText.heightInLines = NaN;
tempText.setStyle("horizontalScrollPolicy", "off");
tempText.setStyle("verticalScrollPolicy", "off");
printJob.printAsBitmap = false;
printJob.addObject(tempText, "matchWidth");
printJob.send();
tempText.visible = false;
}

private function textChange():void {
tabData[tabSelectedIndex].textData = textArea.text;
canUndo = undoManager.canUndo();
canRedo = undoManager.canRedo();
focusManager.setFocus(textArea);
}

private function doUndo():void {
undoManager.undo();
textChange();
}

private function doRedo():void {
undoManager.redo();
textChange();
}

private function doOpen():void {
var file:File = new File();
file.browseForOpen("Open document", [new FileFilter("Text documents", "*.txt"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, fileLoad);

function fileLoad(evt:Event):void {
loadFile(file);
}
}

private function loadFile(file:File):void {
if(fileDuplicateCheck(file.nativePath)){
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var str:String = stream.readMultiByte(stream.bytesAvailable, currentEncoding);
stream.close();
str = str.replace(File.lineEnding, "\n");
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + file.name + " opened";
updateStatus();
tabData.addItem( { title:file.name, textData: str, saved:true, location:file.nativePath } );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}else {
Alert.show("File " + file.name + " is already open","Error");
}
}

private function fileDuplicateCheck(loc:String):Boolean {
var toReturn:Boolean = true;
for (var i:int = 0; i < tabData.length; i++) {
if (tabData[i].location == loc) {
toReturn = false;
tabSelectedIndex = i;
tabChange();
break;
}
}
return toReturn;
}

private function saveUpdate():void {
if (tabData[tabSelectedIndex].saved) {
tabData[tabSelectedIndex].saved = false;

tabBar.dataProvider = new ArrayCollection([]);
tabBar.dataProvider = tabData;
tabBar.selectedIndex = tabSelectedIndex;

sideList.dataProvider = tabData;
changeTitle();
}
}

private function doSave(ind:int, updateIndexNeeded:Boolean = true):void {
if(!tabData[ind].saved){
if (tabData[ind].location != "") {
saveFile(textArea.text, tabData[ind].location, updateIndexNeeded);
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + tabData[ind].title + " saved";
updateStatus();
tabData[ind].saved = true;
}else{
doSaveAs(textArea.text, ind, updateIndexNeeded);
}
}
}

private function doSaveAll():void {
saveAsQueue = [];
for (var i:int = 0; i < tabData.length; i++) {
saveAsQueue.push(i);
}
doSaveQueue();
}

private function doSaveQueue():void {
if (saveAsQueue.length > 0) {
var ind:int = saveAsQueue[0];
saveAsQueue.splice(0, 1);
tabSelectedIndex = ind;
tabChange();
doSave(ind);
}
}

private function refreshData():void {
tabBar.dataProvider = new ArrayCollection([]);
tabBar.dataProvider = tabData;
tabBar.selectedIndex = 0;
tabBar.selectedIndex = tabSelectedIndex;

sideList.dataProvider = tabData;
}

private function doSaveAs(text:String, ind:int, updateIndexNeeded:Boolean = true):void {
var file:File = new File();
file.browseForSave("Save " + tabData[ind].title);
file.addEventListener(Event.SELECT, fileSave);
file.addEventListener(Event.CANCEL, fileCancel);

function fileSave(evt:Event):void {
if (file.name.length > 0) {
// See if user entered extension for the file (for example .txt)
// If not, add .txt by default
var extReg:RegExp = /\.([a-z0-9]{2,})/i;
if (extReg.test(file.name)) {
tabData[ind].location = file.nativePath;
tabData[ind].title = file.name;
tabData[ind].saved = true;
saveFile(text, file.nativePath, updateIndexNeeded);
}else {
tabData[ind].location = file.nativePath + ".txt";
tabData[ind].title = file.name + ".txt";
tabData[ind].saved = true;
saveFile(text, file.nativePath + ".txt", updateIndexNeeded);
}
}else{
Alert.show("You need to enter a name for your file.", "Error");
}
}

function fileCancel(evt:Event):void {
if (closeAfterConfirm && tabsToClose == 0 && saveWait) {
FlexGlobals.topLevelApplication.close();
}
doSaveQueue();
}
}

private function saveFile(text:String, location:String, updateIndexNeeded:Boolean = true):void {
var file:File = new File(location);
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
var str:String = text;
str = str.replace(/\n/g, File.lineEnding);
stream.writeMultiByte(str, currentEncoding);
stream.close();
if (closeAfterConfirm && tabsToClose == 0 && saveWait) {
FlexGlobals.topLevelApplication.close();
}
saveWait = false;
if (updateIndexNeeded) {
refreshData();
}
doSaveQueue();
changeTitle();
 }
 
private function updateDirectoryPath():void{
directoryLabel.text = fileList.directory.nativePath;
}

private function fileListLoad():void{
var file:File = new File(fileList.selectedPath);
loadFile(file);
}

private function onOpen(evt:SQLEvent):void {
// create snippets table
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
//stat.text = "DELETE FROM snippets";
stat.text = "CREATE TABLE IF NOT EXISTS snippets (id INTEGER PRIMARY KEY AUTOINCREMENT, snippetName TEXT, snippetText TEXT, categoryID INTEGER, snippetPosition INTEGER)";
stat.execute( -1, new Responder(onCreateSnippets));
}

private function onCreateSnippets(evt:SQLResult):void {
// create category table
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection2;
//stat.text = "DELETE FROM categories";
stat.text = "CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY AUTOINCREMENT, categoryName TEXT, categoryPosition INTEGER)";
stat.execute( -1, new Responder(onCreateCategory));
}

private function onCreateCategory(evt:SQLResult):void {
// select snippets
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT id, snippetName, categoryID, snippetPosition FROM snippets ORDER BY id";
stat.execute( -1, new Responder(onSelectedSnippets));
}

private function onSelectedSnippets(evt:SQLResult):void {
// save selected snippets to a variable
snippetData = evt.data;

// select categories
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection2;
stat.text = "SELECT id, categoryName, categoryPosition FROM categories ORDER BY id";
stat.execute( -1, new Responder(onSelectedCategories));
}

private function onSelectedCategories(evt:SQLResult):void {
// save selected categories to a variable
categoryData = evt.data;

// call function to start creating the XML
createSnippetXML();
}

private function createSnippetXML():void {
snippetXML = new XMLList();

// add root tags if there are any items in any database
if (snippetData || categoryData) {
snippetXML = new XMLList(<root></root>);
}

// Create variable to store snippets which will be added in a batch
var prepareSnippets:Array = [];

// loop through categories to add them and their content
if (categoryData) {
// Sort categories
categoryData.sortOn("categoryPosition", Array.NUMERIC);
for (var i:int = 0; i < categoryData.length; i++) {
prepareSnippets = [];
var aCategory:XML = <category/>
aCategory.@id = categoryData[i].id;
aCategory.@label = symbolEncode(categoryData[i].categoryName);
aCategory.@categoryPosition = categoryData[i].categoryPosition;
aCategory.@isBranch = true;
snippetXML[0].appendChild(aCategory);
if(snippetData){
for (var a:int = snippetData.length-1; a >= 0; a--) {
if (snippetData[a].categoryID == categoryData[i].id) {
prepareSnippets.push(snippetData[a]);
snippetData.splice(a, 1);
}
}
}

// sort and add snippets to this category
prepareSnippets.sortOn("snippetPosition", Array.NUMERIC);
for (var u:int = 0; u < prepareSnippets.length; u++) {
var aSnippet:XML = <snippet/>;
aSnippet.@id = prepareSnippets[u].id;
aSnippet.@label = symbolEncode(prepareSnippets[u].snippetName);
aSnippet.@categoryID = prepareSnippets[u].categoryID;
aSnippet.@snippetPosition = prepareSnippets[u].snippetPosition;
aCategory.appendChild(aSnippet);
}

}
// look for snippets located in root tags (not in a category)
if(snippetData){
snippetData.sortOn("snippetPosition", Array.NUMERIC);
for (var t:int = 0; t < snippetData.length; t++) {
var aRootSnippet:XML = <snippet/>;
aRootSnippet.@id = snippetData[t].id;
aRootSnippet.@label = symbolEncode(snippetData[t].snippetName);
aRootSnippet.@categoryID = snippetData[t].categoryID;
aRootSnippet.@snippetPosition = snippetData[t].snippetPosition;
snippetXML[0].appendChild(aRootSnippet);
}
}
}
}

private function snippetTreeChange(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch == false) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT snippetText FROM snippets WHERE id=" + evt.currentTarget.selectedItem.@id;
stat.execute( -1, new Responder(insertSnippet));
evt.currentTarget.selectedIndex = -1;
}
}

private function insertSnippet(evt:SQLResult):void {
insertText(evt.data[0].snippetText);
}

private function doSnippet(createNew:Boolean = false, newText:String = ""):void{
snippetWindow.open();
snippetWindow.activate();
snippetWindow.visible = true;
snippetWindow.setValues(snippetXML, connection, connection2, createNew, newText);
}

private function symbolEncode(str:String):String {
str = str.replace(/&/g, '&amp;');
str = str.replace(/"/g, '&quot;');
str = str.replace(/'/g, '&apos;');
str = str.replace(/\</g, '&lt;');
str = str.replace(/\>/g, '&gt;');
return str;
}

private function symbolDecode(str:String):String{
str = str.replace(/&quot;/g, '"');
str = str.replace(/&apos;/g, "'");
str = str.replace(/&lt;/g, '<');
str = str.replace(/&gt;/g, '>');
str = str.replace(/&amp;/g, '&');
return str;
}

private function labelDecode(obj:XML):String{
return symbolDecode(obj.@label);
}

private function doFind():void {
pref_sidepane = true
sidePaneButtons.selectedIndex = 3;
savePreferences();
updateTextSize();
countLines();
}

private function changePanelTab():void {
if (sidePaneButtons.selectedIndex == 3) {
focusManager.setFocus(searchInput);
}else {
focusManager.setFocus(textArea);
resetSearch();
}
updateSearch();
}

private function updateSearch():void {
var allFormat:TextLayoutFormat = new TextLayoutFormat();
allFormat.color = pref_fontsettings.fontcolor;
textArea.setFormatOfRange(allFormat, 0, textArea.text.length);
if (searchInput != null) {
searchResults = [];
if (searchInput.text != "") {
var lastIndex:int = 0;
var selectFormat:TextLayoutFormat = new TextLayoutFormat();
selectFormat.color = 0xff0000;

var tempText:String = textArea.text;
var tempInput:String = searchInput.text;
if (!checkCase.selected) {
tempText = textArea.text.toLowerCase();
tempInput = searchInput.text.toLowerCase();
}
while (tempText.indexOf(tempInput, lastIndex) >= 0) {
var thisIndex:int = tempText.indexOf(tempInput, lastIndex);
searchResults.push(thisIndex);
lastIndex = thisIndex + 1;
if(checkHighlight.selected){
textArea.setFormatOfRange(selectFormat, thisIndex, thisIndex + searchInput.text.length);
}
}
moveResults(0);
resultText.text = "Matches found: " + searchResults.length;
}else{
resultText.text = "";
}
}
}

private function replaceSearch():void {
if (searchResults.length > 0) {
var flags:String = "g";
if (!checkCase.selected) {
flags += "i";
}
var pattern:RegExp = new RegExp(searchInput.text, flags);
textArea.text = textArea.text.replace(pattern, replaceInput.text);
resetSearch();
}
}

private function resetSearch():void {
if(searchInput!=null){
searchInput.text = "";
updateSearch();
}
}

private function moveResults(ind:int):void {
if (searchResults.length > 0) {
focusManager.setFocus(textArea);
textArea.selectRange(searchResults[ind], searchResults[ind] + searchInput.text.length);
textArea.scrollToRange(searchResults[ind], searchResults[ind] + searchInput.text.length);
selectedResult = ind;
}
}

private function resultsUp():void{
if (searchResults.length-1 > selectedResult) {
moveResults(selectedResult + 1);
}else
if (searchResults.length-1 == selectedResult) {
moveResults(0);
}
}

private function resultsDown():void{
if (selectedResult > 0) {
moveResults(selectedResult - 1);
}else
if (selectedResult == 0) {
moveResults(searchResults.length-1);
}
}

private function addRecent(obj:Object):void {
for (var i:int = 0; i < pref_recent.length; i++) {
// remove if location is the same
if (pref_recent[i].location == obj.location) {
pref_recent.removeItemAt(i);
break;
}
}
pref_recent.addItemAt( { label:obj.title, location:obj.location }, 0);
if (pref_recent.length > 20) {
pref_recent.removeItemAt(pref_recent.length-1);
}
savePreferences();
}

private function selectRecent():void {
var location:String = pref_recent[recentList.selectedIndex].location;
if (new File(location).exists) {
loadFile(new File(location));
}else {
Alert.show("File not found at specified location.","Oops!");
}
}

private function doEncode(newEncoding:String, index:Number):void{
currentEncoding = newEncoding;
for (var i:int = 0; i < enc.length; i++) {
enc.setItemAt(false, i);
}
enc.setItemAt(true, index);
}

private function changeTitle():void {
// refresh tabs
refreshData();
// refresh title
var savedSymbol:String = (tabData[tabSelectedIndex].saved)?(""):("*");
title = "Kirpad - " + tabData[tabSelectedIndex].title + savedSymbol;
}

private function openRecent():void{
pref_sidepane = true;
sidePaneButtons.selectedIndex = 4;
}
]]>
</fx:Script>

<fx:Declarations>
<fx:XML id="windowMenu">
<root>
<menuitem label="File">
<menuitem label="New" key="n" controlKey="true" />
<menuitem label="Open" key="o" controlKey="true" />
<menuitem label="Recent">
</menuitem>
<menuitem type="separator"/>
<menuitem label="Save" key="s" controlKey="true" />
<menuitem label="Save As" key="s" controlKey="true" shiftKey="true" />
<menuitem label="Save All" key="s" controlKey="true" altKey="true" />
<menuitem type="separator"/>
<menuitem label="Print" key="p" controlKey="true" />
</menuitem>
<menuitem label="Edit">
<menuitem label="Undo" key="z" controlKey="true" enabled="{canUndo}" />
<menuitem label="Redo" key="y" controlKey="true" enabled="{canRedo}" />
<menuitem type="separator"/>
<menuitem label="Cut" key="x" controlKey="true" enabled="{textArea.selectionActivePosition!=textArea.selectionAnchorPosition}" />
<menuitem label="Copy" key="c" controlKey="true" enabled="{textArea.selectionActivePosition!=textArea.selectionAnchorPosition}" />
<menuitem label="Paste" key="v" controlKey="true" enabled="{canPaste}" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Find/Replace..." key="f" controlKey="true" />
</menuitem>
<menuitem label="Settings">
<menuitem label="Word wrap" type="check" toggled="{pref_wrap}" />
<menuitem label="Font..."/>
</menuitem>
<menuitem label="View">
<menuitem label="Tool bar" type="check" toggled="{pref_toolbar}" />
<menuitem label="Status bar" type="check" toggled="{pref_status}" />
<menuitem label="Line count" type="check" toggled="{pref_linecount}" />
<menuitem label="Side pane" type="check" toggled="{pref_sidepane}" />
</menuitem>
<menuitem label="Encoding">
<menuitem label="UTF-8" type="check" toggled="{enc.getItemAt(0)}" ind="0" encoding="utf-8" />
<menuitem label="Unicode" type="check" toggled="{enc.getItemAt(1)}" ind="1" encoding="unicode" />
<menuitem label="ASCII" type="check" toggled="{enc.getItemAt(2)}" ind="2" encoding="us-ascii" />
</menuitem>
</root>
</fx:XML>
<mx:ArrayCollection id="tabData">
<fx:Object title="Untitled" textData="" saved="false" seletedActive="0" selectedAnchor="0" location="" />
</mx:ArrayCollection>
<mx:ArrayCollection id="sidePaneData">
<fx:Object icon="@Embed('../lib/page.png')" tip="Tab management" />
<fx:Object icon="@Embed('../lib/folder_magnify.png')" tip="File browsing" />
<fx:Object icon="@Embed('../lib/book.png')" tip="Snippets" />
<fx:Object icon="@Embed('../lib/find.png')" tip="Search and replace" />
<fx:Object icon="@Embed('../lib/folder_page_white.png')" tip="Recent files" />
</mx:ArrayCollection>
<mx:ArrayCollection id="sidePaneTabHeadings">
<fx:String>Tab management</fx:String>
<fx:String>File browsing</fx:String>
<fx:String>Snippets</fx:String>
<fx:String>Search and replace</fx:String>
<fx:String>Recent files:</fx:String>
</mx:ArrayCollection>
</fx:Declarations>

<s:Group width="100%" height="100%">
<s:TextArea id="textArea" width="{textWidth}" height="{textHeight}" y="{textY}" x="{textX}" lineBreak="{(pref_wrap)?('toFit'):('explicit')}"  click="cursorFix(); updateStatus();" change="updateStatus(); countLines(); textChange(); saveUpdate(); resetSearch();" keyDown="updateStatus();" borderVisible="false" focusThickness="0" />
<s:Scroller horizontalScrollPolicy="auto" verticalScrollPolicy="off" width="{tabWidth}" y="{tabY}">
<s:Group>
<custom:CustomTabBar id="tabBar" dataProvider="{tabData}" itemRenderer="CustomTab" height="22" tabClose="onTabClose(event);" change="tabChange('tabbar');" selectedIndex="{tabSelectedIndex}" labelField="saved">
<custom:layout>
<s:HorizontalLayout gap="-1" columnWidth="170" variableColumnWidth="false"/>
</custom:layout>
</custom:CustomTabBar>
</s:Group>
</s:Scroller>
<s:TextArea id="lineCount" width="{lineCountWidth}" text="{lineNumbers}" visible="{pref_linecount}" height="{textHeight}" y="{textY}" editable="false" selectable="false" mouseEnabled="false" textAlign="right" verticalScrollPolicy="off" horizontalScrollPolicy="off" />
<mx:HBox id="toolBar" width="100%" backgroundColor="#dddddd" height="30" visible="{pref_toolbar}" paddingTop="2" paddingLeft="3">
<custom:IconButton icon="@Embed('../lib/page.png')" toolTip="New document" click="doNew();" />
<custom:IconButton icon="@Embed('../lib/folder_page.png')" toolTip="Open" click="doOpen();" />
<custom:IconButton icon="@Embed('../lib/disk.png')" toolTip="Save" click="doSave(tabSelectedIndex);" />
<custom:IconButton icon="@Embed('../lib/disk_multiple.png')" toolTip="Save all" click="doSaveAll();" />
<custom:IconButton icon="@Embed('../lib/printer.png')" toolTip="Print" click="doPrint();" />
<custom:IconButton icon="@Embed('../lib/find.png')" toolTip="Find/replace" click="doFind();" />
<s:Label text="|" fontSize="18" color="#bbbbbb" paddingTop="4" />
<custom:IconButton icon="@Embed('../lib/arrow_undo.png')" toolTip="Undo" enabled="{canUndo}" click="doUndo();" />
<custom:IconButton icon="@Embed('../lib/arrow_redo.png')" toolTip="Redo" enabled="{canRedo}" click="doRedo();" />
<s:Label text="|" fontSize="18" color="#bbbbbb" paddingTop="4" />
<custom:IconButton icon="@Embed('../lib/cut.png')" toolTip="Cut" click="doCut();" enabled="{textArea.selectionActivePosition!=textArea.selectionAnchorPosition}" />
<custom:IconButton icon="@Embed('../lib/page_white_copy.png')" toolTip="Copy" click="doCopy();" enabled="{textArea.selectionActivePosition!=textArea.selectionAnchorPosition}" />
<custom:IconButton icon="@Embed('../lib/paste_plain.png')" toolTip="Paste" click="doPaste();" enabled="{canPaste}" />
</mx:HBox>
<mx:Box id="sidePane" width="{sidePaneWidth}" y="{sidePaneY}" x="{sidePaneX}" height="{sidePaneHeight}" backgroundColor="#dddddd" visible="{pref_sidepane}" paddingTop="5" paddingLeft="5" horizontalScrollPolicy="off">
<s:Group>
<s:Label text="{sidePaneTabHeadings.getItemAt(sidePaneButtons.selectedIndex)}" width="{sidePaneWidth}" top="1" />
<custom:IconButton icon="@Embed('../lib/bullet_go.png')" toolTip="Hide side pane" click="closeSidePane();" top="-4" right="12"/>
</s:Group>
<mx:ToggleButtonBar id="sidePaneButtons" dataProvider="{sidePaneData}" iconField="icon" width="{sidePaneWidth-10}" toolTipField="tip" />
<mx:ViewStack id="sidePaneStack" height="100%" selectedIndex="{sidePaneButtons.selectedIndex}" change="changePanelTab();" >
<s:NavigatorContent id="tabs">
<custom:CustomList id="sideList" dataProvider="{tabData}" width="{sideContentWidth}" height="100%" itemRenderer="CustomListItem" selectedIndex="{tabSelectedIndex}" change="tabChange('sidelist');" tabClose="onListClose(event);" />
</s:NavigatorContent>
<s:NavigatorContent id="files">
<s:VGroup height="100%">
<s:HGroup width="100%">
<custom:IconButton icon="@Embed('../lib/arrow_up.png')" toolTip="Up" click="fileList.navigateUp();" enabled="{fileList.canNavigateUp}"/>
<s:Label width="140" id="directoryLabel" paddingTop="6" />
</s:HGroup>
<mx:FileSystemList width="{sidePaneWidth-10}" height="100%" id="fileList" directory="{File.desktopDirectory}" directoryChange="updateDirectoryPath();" creationComplete="updateDirectoryPath();" fileChoose="fileListLoad();" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent id="snippets">
<s:VGroup height="100%" width="100%">
<mx:Tree height="100%" width="100%" id="snippetTree" showRoot="false" labelFunction="labelDecode" dataProvider="{snippetXML}" change="snippetTreeChange(event);" />
<s:Button width="100%" label="New snippet" click="doSnippet(true, textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition));" />
<s:Button width="100%" label="Manage snippets" click="doSnippet();" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent id="search">
<s:VGroup height="100%" width="100%">
<s:Label text="Find:"/>
<s:TextInput id="searchInput" width="100%" enter="updateSearch();" />
<s:Button id="searchButton" label="Search"  width="100%" click="updateSearch();" />
<s:Label id="resultText" />
<s:HGroup>
<s:Button id="findPrevious" label="Previous"  width="50%" click="resultsDown();" />
<s:Button id="findNext" label="Next"  width="50%" click="resultsUp();" />
</s:HGroup>
<s:CheckBox label="Match case" id="checkCase" change="updateSearch()" />
<s:CheckBox label="Highlight all" id="checkHighlight" change="updateSearch()" />
<s:Label text=""/>
<s:Label text="Replace with:"/>
<s:TextInput id="replaceInput" width="100%" enter="replaceSearch()" />
<s:Button id="replaceButton" label="Replace all"  width="100%" click="replaceSearch()" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent id="recent">
<s:VGroup height="100%" width="100%">
<s:Label text="Recently closed files:"/>
<s:List id="recentList" width="{sideContentWidth}" height="100%" dataProvider="{pref_recent}" labelField="label" change="selectRecent();" />
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</s:Group>
<s:TextArea id="tempText" borderVisible="false" visible="false"/>

</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment