Sunday, January 1, 2012

Creating a Flex AIR text editor: Part 83

In this tutorial we will add a new feature to the application - drag and drop a file from anywhere on the computer into the application window will now open the file in a new tab.

Native drag and dropping is discussed more in-depth in my clipboard tutorial series.

First we need to import 2 classes - NativeDragEvent and NativeDragManager:

import flash.events.NativeDragEvent;
import flash.desktop.NativeDragManager;

Now go to the init() function and add listeners for NATIVE_DRAG_ENTER and NATIVE_DRAG_DROP events.

// Create drag drop listeners
addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDrop);

The onDragIn() function tells NativeDragManager to accept the dragging operation using the acceptDragDrop() method. Note that the parameter is "this", not "stage" like in the tutorial that I linked above (this is because we're in Flex environment).

private function onDragIn(evt:NativeDragEvent):void {
NativeDragManager.acceptDragDrop(this);
}

The onDragDrop() function takes the nativePath data from the clipboard and loads the file using loadFile():

private function onDragDrop(evt:NativeDragEvent):void {
var dragFile:File = new File(evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT)[0].nativePath);
loadFile(dragFile);
}

And that's pretty much it!

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;
import mx.managers.PopUpManager;
import flashx.textLayout.operations.InsertTextOperation;
import flashx.textLayout.edit.SelectionState;
import flash.events.NativeDragEvent;
import flash.desktop.NativeDragManager;

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 var cm:ContextMenu = new ContextMenu();
[Bindable]
private var hasLocation:Boolean = false;
[Bindable]
private var fontOpen:Boolean = false;
private var isCtrl:Boolean = 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);
fontWindow.addEventListener(Event.CLOSING, fontClose);

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

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

// Create drag drop listeners
addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDrop);

// 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_rename:ContextMenuItem = new ContextMenuItem("Rename");
cm_rename.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextRename);
var cm_delete:ContextMenuItem = new ContextMenuItem("Delete file");
cm_delete.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextDelete);
var cm_reload:ContextMenuItem = new ContextMenuItem("Reload file");
cm_reload.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextReload);

cm.items = [cm_close, cm_closeother, cm_save, cm_duplicate, cm_rename, cm_delete, cm_reload];
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);
addEventListener(KeyboardEvent.KEY_UP, onKeyUp);

// Listen to mouse wheel
addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);

// 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);
(evt.item.@label == "Rename...")?(openRename()):(void);
(evt.item.@label == "Delete file")?(doDelete()):(void);
(evt.item.@label == "Reload file")?(doReload()):(void);
(evt.item.@label == "From file...")?(doInsertFile()):(void);
(evt.item.@label == "File details")?(doFileDetails()):(void);
(evt.item.@label == "Timestamp")?(doTimestamp()):(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 = "Encoding: " + currentEncoding + "\t" + 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 {
fontOpen = true;
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();
fontOpen = false;
}

private function fontClose(evt:Event):void {
fontOpen = false;
}

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;
changeTextTo(tabData[tabSelectedIndex].textData);
editManager.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;
hasLocation = false;
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);

changeTextTo(tabData[tabSelectedIndex].textData);

var operation2:TabOperation = new TabOperation(previousIndex, tabSelectedIndex, undoManager);
undoManager.pushUndo(operation2);
}

previousIndex = tabSelectedIndex;
updateStatus();
countLines();
textChange();
changeTitle();
updateSearch();
hasLocation = (tabData.getItemAt(tabSelectedIndex).location == '')?(false):(true);
}

private function changeTextTo(str:String):void {
var tempSaved:Boolean = tabData[tabSelectedIndex].saved;
editManager.selectAll();
var insertOp:InsertTextOperation = new InsertTextOperation(editManager.getSelectionState(), str);
editManager.doOperation(insertOp);
tabData[tabSelectedIndex].saved = tempSaved;
editManager.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
textArea.scrollToRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
}

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;
if (tabData[rightclickTabIndex].location == "") {
cm.items[4].enabled = false;
cm.items[5].enabled = false;
cm.items[6].enabled = false;
}else {
cm.items[4].enabled = true;
cm.items[5].enabled = true;
cm.items[6].enabled = true;
}
}

private function listRightClick(evt:MouseEvent):void {
var tabHeight:Number = 20;
var rcIndex:int = Math.floor((sideList.mouseY + sideList.scroller.verticalScrollBar.value) / tabHeight);
rightclickTabIndex = rcIndex;
if (tabData[rightclickTabIndex].location == "") {
cm.items[4].enabled = false;
cm.items[5].enabled = false;
cm.items[6].enabled = false;
}else {
cm.items[4].enabled = true;
cm.items[5].enabled = true;
cm.items[6].enabled = true;
}
}

private function onKeyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 17) {
isCtrl = true;
}
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();
}
// Ctrl + - - zoom out
if (evt.keyCode == 109 && !fontOpen) {
doZoomOut();
}
// Ctrl + + - zoom in
if (evt.keyCode == 107 && !fontOpen) {
doZoomIn();
}
}
}

private function onKeyUp(evt:KeyboardEvent):void {
if (evt.keyCode==17) {
isCtrl = false;
}
}

private function onMouseWheel(evt:MouseEvent):void {
if (isCtrl && !fontOpen) {
if (evt.delta > 0) {
doZoomIn();
}
if (evt.delta < 0) {
doZoomOut();
}
}
}

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 {
if (getClass(undoManager.peekUndo()) == TabOperation) {
undoManager.undo();
var tempSaved:Boolean = tabData[tabSelectedIndex].saved;
undoManager.undo();
tabData[tabSelectedIndex].saved = tempSaved;
}
editManager.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
undoManager.undo();
textChange();
}

private function doRedo():void {
if (getClass(undoManager.peekRedo()) == TabOperation) {
undoManager.redo();
var tempSaved:Boolean = tabData[tabSelectedIndex].saved;
undoManager.redo();
tabData[tabSelectedIndex].saved = tempSaved;
}
editManager.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
undoManager.redo();
textChange();
}

private function getClass(obj:Object):Class {
return Class(getDefinitionByName(getQualifiedClassName(obj)));
}

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, selectedAnchor:0, selectedActive:0} );
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);
}
hasLocation = true;
}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;
aSnippet.@isBranch = false;
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;
aRootSnippet.@isBranch = false;
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);
var resultText:String = textArea.text.replace(pattern, replaceInput.text);
changeTextTo(resultText);
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;
}

private function tabContextRename(evt:ContextMenuEvent):void {
tabSelectedIndex = rightclickTabIndex;
tabChange();
openRename();
}

private function openRename():void{
PopUpManager.addPopUp(renameWindow, this, true);
PopUpManager.centerPopUp(renameWindow);
renameWindow.title = "Rename " + tabData[tabSelectedIndex].title;
renameInput.text = tabData[tabSelectedIndex].title;
focusManager.setFocus(renameInput);
}

private function closeRename():void{
PopUpManager.removePopUp(renameWindow);
}

private function doRename():void {
var location:String = tabData[tabSelectedIndex].location;
var titleOld:String = tabData[tabSelectedIndex].title;
var titleNew:String = renameInput.text;
var locationNew:String = location.substring(0, location.length - titleOld.length) + titleNew;
var validName:RegExp = /^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\";|\/]+$/;

if (validName.test(titleNew)) {

// remove existing file if it exists
if (new File(location).exists) {
new File(location).deleteFile();
}

// Update properties and
// see if user entered extension for the file (for example .txt)
// If not, add .txt by default
// Then create the new file
var extReg:RegExp = /\.([a-z0-9]{2,})/i;
if (extReg.test(titleNew)) {
tabData[tabSelectedIndex].location = locationNew;
tabData[tabSelectedIndex].title = titleNew;
tabData[tabSelectedIndex].saved = true;
saveFile(tabData[tabSelectedIndex].textData, locationNew, true);
}else {
tabData[tabSelectedIndex].location = locationNew + ".txt";
tabData[tabSelectedIndex].title = titleNew + ".txt";
tabData[tabSelectedIndex].saved = true;
saveFile(tabData[tabSelectedIndex].textData, locationNew + ".txt", true);
}

statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + titleNew + " renamed";
updateStatus();
}else {
Alert.show("New file name is blank or contains restricted characters. File was no renamed.", "Error");
}
}

private function tabContextDelete(evt:ContextMenuEvent):void {
tabSelectedIndex = rightclickTabIndex;
tabChange();
doDelete();
}

private function doDelete():void{
Alert.show("Delete " + tabData[tabSelectedIndex].location + " from computer?", "Confirmation", Alert.YES | Alert.NO, null, confirmDelete);

function confirmDelete(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var file:File = new File(tabData[tabSelectedIndex].location);
if (file.exists) {
file.deleteFile();
closeTab(tabSelectedIndex);
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + file.name + " deleted";
updateStatus();
}else {
Alert.show("File not found at specified location!","Oops!");
}
}
}
}

private function tabContextReload(evt:ContextMenuEvent):void {
tabSelectedIndex = rightclickTabIndex;
tabChange();
doReload();
}

private function doReload():void{
var file:File = new File(tabData[tabSelectedIndex].location);
if (file.exists) {
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");
tabData[tabSelectedIndex].textData = str;
tabData[tabSelectedIndex].saved = true;
//textArea.text = str;
changeTextTo(tabData[tabSelectedIndex].textData);
//tabChange();
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + file.name + " reloaded";
updateStatus();
}else {
Alert.show("File not found at specified location!","Oops!");
}
}

private function doInsertFile():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 {
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");
insertText(str);
}
}

private function doFileDetails():void {
var currentFile:File = new File(tabData[tabSelectedIndex].location);
var str:String = "";
str += "File: " + currentFile.nativePath.toString() + "\n";
str += "Created: " + currentFile.creationDate.toLocaleString() + "\n";
str += "Modified: " + currentFile.modificationDate.toLocaleString() + "\n";
str += "Size: " + currentFile.size + " bytes";
insertText(str);
}

private function doTimestamp():void{
insertText(new Date().toLocaleString());
}

private function doZoomIn():void {
if(pref_fontsettings.fontsize<200){
pref_fontsettings.fontsize++;
}
textArea.setStyle("fontSize", pref_fontsettings.fontsize);
lineCount.setStyle("fontSize", pref_fontsettings.fontsize);
savePreferences();
}

private function doZoomOut():void {
if(pref_fontsettings.fontsize>2){
pref_fontsettings.fontsize--;
}
textArea.setStyle("fontSize", pref_fontsettings.fontsize);
lineCount.setStyle("fontSize", pref_fontsettings.fontsize);
savePreferences();
}

private function onDragIn(evt:NativeDragEvent):void {
NativeDragManager.acceptDragDrop(this);
}

private function onDragDrop(evt:NativeDragEvent):void {
var dragFile:File = new File(evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT)[0].nativePath);
loadFile(dragFile);
}
]]>
</fx:Script>

<fx:Declarations>
<s:TitleWindow id="renameWindow" width="300" height="120" close="closeRename();" title="Rename file">
<s:VGroup x="10" y="10" width="300" height="350">
<s:Label width="280" text="Rename to:"/>
<s:TextInput width="280" id="renameInput" maxChars="255" />
<s:HGroup>
<s:Button label="OK" click="doRename(); closeRename();" />
<s:Button label="Cancel" click="closeRename();" />
</s:HGroup>
</s:VGroup>
</s:TitleWindow>
<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="Rename..." enabled="{hasLocation}" />
<menuitem label="Delete file" enabled="{hasLocation}" />
<menuitem label="Reload file" enabled="{hasLocation}" />
<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="Insert">
<menuitem label="From file..." />
<menuitem label="File details" enabled="{hasLocation}"/>
<menuitem label="Timestamp" />
</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}" />
<s:Label text="|" fontSize="18" color="#bbbbbb" paddingTop="4" />
<custom:IconButton icon="@Embed('../lib/zoom_in.png')" toolTip="Zoom in" click="doZoomIn();" enabled="{!fontOpen}" />
<custom:IconButton icon="@Embed('../lib/zoom_out.png')" toolTip="Zoom out" click="doZoomOut();" enabled="{!fontOpen}"/>
</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