Showing posts with label editor. Show all posts
Showing posts with label editor. Show all posts
Sunday, February 1, 2015
Creating a Flex AIR text editor Part 15
In this tutorial we will work on tab closing.
Before we start, theres one more thing we need to include in the tabChange() function that we created in the previous part. When changing between tabs, the text data and selection data is updated, however, the status bar is not. Thus, if word wrapping is off, the caret position is not updated. We can fix this by just calling the updateStatus() function here:
Now, lets get to creating the close functionality for the tabs!
We already have the onTabClose() function, which is called when the user closes a tab using the X button. Lets create a closeTab() function, which we can then use whenever we know the index of the tab we want to remove. For now the code will only remove the tab that is selected (regardless of which tab was closed). This will be fixed in the future tutorials.
We pass the index integer value and then check if the tab was saved or not (is confirmation required or not). If it is saved, remove the tab from the tabData array using the removeTab() function that we will create shortly. If it is not saved, ask for a confirmation using the Alert class. The confirmation asks the user if they want to save the file before closing. If the user chooses to save, then (for now) we just send a debug message, we will later put a function that handles saving the file before closing it. Otherwise, just close the tab.
Now, that removeTab function... it basically deletes the item from the tabData array using the removeItemAt() function and the index value that we pass to the function. However, there are 2 things we need to remember - the first thing is that we need to set the new selected tab of the tabBar and update the text data and selection. The second thing is that if it is the last tab that we are removing, we need to create a new empty tab in its place.
And thats pretty much it for todays tutorial!
Heres the full code:
We still have a long way to go with this application. See you in next part!
Thanks for reading!
Read more »
Before we start, theres one more thing we need to include in the tabChange() function that we created in the previous part. When changing between tabs, the text data and selection data is updated, however, the status bar is not. Thus, if word wrapping is off, the caret position is not updated. We can fix this by just calling the updateStatus() function here:
private function tabChange():void {
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
updateStatus();
}
Now, lets get to creating the close functionality for the tabs!
We already have the onTabClose() function, which is called when the user closes a tab using the X button. Lets create a closeTab() function, which we can then use whenever we know the index of the tab we want to remove. For now the code will only remove the tab that is selected (regardless of which tab was closed). This will be fixed in the future tutorials.
private function onTabClose(evt:Event):void {
closeTab(tabBar.selectedIndex);
}
We pass the index integer value and then check if the tab was saved or not (is confirmation required or not). If it is saved, remove the tab from the tabData array using the removeTab() function that we will create shortly. If it is not saved, ask for a confirmation using the Alert class. The confirmation asks the user if they want to save the file before closing. If the user chooses to save, then (for now) we just send a debug message, we will later put a function that handles saving the file before closing it. Otherwise, just close the tab.
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
}
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", tabData[index].label, Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
Alert.show("Perform save function here and then delete");
}else {
removeTab(index);
}
}
}
Now, that removeTab function... it basically deletes the item from the tabData array using the removeItemAt() function and the index value that we pass to the function. However, there are 2 things we need to remember - the first thing is that we need to set the new selected tab of the tabBar and update the text data and selection. The second thing is that if it is the last tab that we are removing, we need to create a new empty tab in its place.
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
And thats pretty much it for todays tutorial!
Heres the 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}">
<s:menu>
<mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" />
</s:menu>
<fx:Script>
<![CDATA[
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.NativeWindowBoundsEvent;
import flash.net.SharedObject;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
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 mx.events.ResizeEvent;
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]
public var pref_fontsettings:Object = new Object();
private var initHeight:Number;
private var heightFixed:Boolean = false;
private var statusMessage:String;
[Bindable]
private var textHeight:Number;
[Bindable]
private var textY:Number;
[Bindable]
private var tabY:Number;
private var previousIndex:Number = 0;
public var fontWindow:FontWindow = new FontWindow();
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;
// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.wrap = true;
preferences.data.status = true;
preferences.data.toolbar = true;
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_fontsettings = preferences.data.fontsettings;
// 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);
}
private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(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 == "Font...")?(doFont()):(void);
savePreferences();
updateStatus();
}
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.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 {
var substrPositions:int = textArea.selectionActivePosition - textArea.selectionAnchorPosition;
var oldSel1:int = (substrPositions>0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var oldSel2:int = (substrPositions<0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var preText:String = textArea.text.substring(0, oldSel1);
var postText:String = textArea.text.substring(oldSel2);
var newSelectRange:int = preText.length + str.length;
textArea.text = preText + str + postText;
textArea.selectRange(newSelectRange, newSelectRange);
}
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();
}
}
}
private function onResize(evt:ResizeEvent):void {
updateTextSize();
}
private function updateTextSize():void {
tabY = (toolBar.visible)?(toolBar.height):(0);
textY = tabBar.height + tabY;
var statusHeight:Number = (pref_status)?(statusBar.height):(0);
textHeight = height - textY - statusHeight;
focusManager.setFocus(textArea);
}
private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = str + " " + statusMessage;
}
private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("
");
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 {
var allWindows:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 0; i < allWindows.length; i++)
{
allWindows[i].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);
}
private function onTabClose(evt:Event):void {
closeTab(tabBar.selectedIndex);
}
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
}
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", tabData[index].label, Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
Alert.show("Perform save function here and then delete");
}else {
removeTab(index);
}
}
}
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
private function doNew():void{
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
private function tabChange():void {
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
updateStatus();
}
]]>
</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>
<menuitem label="Edit">
<menuitem label="Cut" key="x" controlKey="true" />
<menuitem label="Copy" key="c" controlKey="true" />
<menuitem label="Paste" key="v" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" 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>
</root>
</fx:XML>
<mx:ArrayCollection id="tabData">
<fx:Object title="Untitled" textData="" saved="false" seletedActive="0" selectedAnchor="0" />
</mx:ArrayCollection>
</fx:Declarations>
<s:Group width="100%" height="100%">
<s:TextArea id="textArea" width="100%" height="{textHeight}" y="{textY}" borderVisible="false" lineBreak="{(pref_wrap)?(toFit):(explicit)}" click="cursorFix(); updateStatus();" change="updateStatus();" keyDown="updateStatus();"/>
<custom:CustomTabBar id="tabBar" dataProvider="{tabData}" width="100%" y="{tabY}" itemRenderer="CustomTab" height="22" tabClose="onTabClose(event);" change="tabChange();" />
<mx:Box id="toolBar" width="100%" backgroundColor="#dddddd" height="24" visible="{pref_toolbar}">
</mx:Box>
</s:Group>
</s:WindowedApplication>
We still have a long way to go with this application. See you in next part!
Thanks for reading!
Wednesday, January 28, 2015
Creating a Flex AIR text editor Part 17
In this tutorial we are going to add hotkeys to let the user navigate through tabs using keyboard.
First of all, though, we will add a few lines to our existing code to update the status bar with status messages. Lets do this when we create a new tab (the doNew() function):
And also when we remove one:
There we go. Now we actually can say that we have a use for the status messages area in our status bar :)
Now lets add hotkeys to let the user navigate through the tabs using keyboard. There are a few combinations we are going to implement:
Ctrl+Tab will open the next tab.
Ctrl+Shift+Tab will go to the previous tab.
Ctrl+(1-8) will go to the numbered tab (from 1 to 8).
Ctrl+9 will go to the last tab.
Lets implement this now. Add a listener for the keyboard events in the init() function:
All the key combinations right now require the Ctrl key to be pressed. Lets make a conditional for that:
Now we can write the conditionals for each of the combination. Heres the full function in the end:
It is important to call tabChange() every time we change the tab so that the data gets updated.
Heres the full code:
Thanks for reading!
Read more »
First of all, though, we will add a few lines to our existing code to update the status bar with status messages. Lets do this when we create a new tab (the doNew() function):
private function doNew():void {
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] New tab created";
updateStatus();
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
And also when we remove one:
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Tab closed: " + tabData[index].title;
updateStatus();
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
There we go. Now we actually can say that we have a use for the status messages area in our status bar :)
Now lets add hotkeys to let the user navigate through the tabs using keyboard. There are a few combinations we are going to implement:
Ctrl+Tab will open the next tab.
Ctrl+Shift+Tab will go to the previous tab.
Ctrl+(1-8) will go to the numbered tab (from 1 to 8).
Ctrl+9 will go to the last tab.
Lets implement this now. Add a listener for the keyboard events in the init() function:
// Listen to keyboard
addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
All the key combinations right now require the Ctrl key to be pressed. Lets make a conditional for that:
private function onKeyDown(evt:KeyboardEvent):void{
if (evt.ctrlKey) {
}
}
Now we can write the conditionals for each of the combination. Heres the full function in the end:
private function onKeyDown(evt:KeyboardEvent):void{
if (evt.ctrlKey) {
// Ctrl+TAB - next tab
if (evt.keyCode == 9 && !evt.shiftKey) {
if (tabData.length - tabBar.selectedIndex > 1) {
tabBar.selectedIndex++;
tabChange();
}
}
// Ctrl+Shift+TAB - previous tab
if (evt.keyCode == 9 && evt.shiftKey) {
if (tabBar.selectedIndex > 0) {
tabBar.selectedIndex--;
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) {
tabBar.selectedIndex = num - 1;
tabChange();
}
}
// Ctrl+9 - go to last tab
if (evt.keyCode == 57) {
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
}
It is important to call tabChange() every time we change the tab so that the data gets updated.
Heres the 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}">
<s:menu>
<mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" />
</s:menu>
<fx:Script>
<![CDATA[
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NativeWindowBoundsEvent;
import flash.net.SharedObject;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
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;
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]
public var pref_fontsettings:Object = new Object();
private var initHeight:Number;
private var heightFixed:Boolean = false;
private var statusMessage:String;
[Bindable]
private var textHeight:Number;
[Bindable]
private var textY:Number;
[Bindable]
private var tabY:Number;
private var previousIndex:int = 0;
private var rightclickTabIndex:int = 0;
public var fontWindow:FontWindow = new FontWindow();
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;
// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.wrap = true;
preferences.data.status = true;
preferences.data.toolbar = true;
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_fontsettings = preferences.data.fontsettings;
// 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:ContextMenu = new ContextMenu();
cm.items = [cm_close, cm_closeother];
cm.hideBuiltInItems();
tabBar.contextMenu = cm;
tabBar.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, tabRightClick);
// Listen to keyboard
addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(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 == "Font...")?(doFont()):(void);
savePreferences();
updateStatus();
}
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.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 {
var substrPositions:int = textArea.selectionActivePosition - textArea.selectionAnchorPosition;
var oldSel1:int = (substrPositions>0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var oldSel2:int = (substrPositions<0)?(textArea.selectionAnchorPosition):(textArea.selectionActivePosition);
var preText:String = textArea.text.substring(0, oldSel1);
var postText:String = textArea.text.substring(oldSel2);
var newSelectRange:int = preText.length + str.length;
textArea.text = preText + str + postText;
textArea.selectRange(newSelectRange, newSelectRange);
}
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();
}
}
}
private function onResize(evt:ResizeEvent):void {
updateTextSize();
}
private function updateTextSize():void {
tabY = (toolBar.visible)?(toolBar.height):(0);
textY = tabBar.height + tabY;
var statusHeight:Number = (pref_status)?(statusBar.height):(0);
textHeight = height - textY - statusHeight;
focusManager.setFocus(textArea);
}
private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = str + " " + statusMessage;
}
private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("
");
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 {
var allWindows:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 0; i < allWindows.length; i++)
{
allWindows[i].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);
}
private function onTabClose(evt:Event):void {
closeTab(tabBar.selectedIndex);
}
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
}
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", "Confirmation", Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
Alert.show("Perform save function here and then delete");
}else {
removeTab(index);
}
}
}
private function removeTab(index:int):void {
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
}
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Tab closed: " + tabData[index].title;
updateStatus();
tabData.removeItemAt(index);
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
}
private function doNew():void {
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] New tab created";
updateStatus();
tabData.addItem( { title:"Untitled", textData:"", saved:false } );
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
private function tabChange():void {
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
previousIndex = tabBar.selectedIndex;
textArea.text = tabData[tabBar.selectedIndex].textData;
textArea.selectRange(tabData[tabBar.selectedIndex].selectedAnchor, tabData[tabBar.selectedIndex].selectedActive);
updateStatus();
}
private function tabContextClose(evt:ContextMenuEvent):void{
closeTab(rightclickTabIndex);
}
private function tabContextCloseOther(evt:ContextMenuEvent):void {
var len:int = tabData.length;
for (var i:int = 0; i < len; i++) {
if (i != rightclickTabIndex) {
closeTab(i);
}
}
}
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 onKeyDown(evt:KeyboardEvent):void{
if (evt.ctrlKey) {
// Ctrl+TAB - next tab
if (evt.keyCode == 9 && !evt.shiftKey) {
if (tabData.length - tabBar.selectedIndex > 1) {
tabBar.selectedIndex++;
tabChange();
}
}
// Ctrl+Shift+TAB - previous tab
if (evt.keyCode == 9 && evt.shiftKey) {
if (tabBar.selectedIndex > 0) {
tabBar.selectedIndex--;
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) {
tabBar.selectedIndex = num - 1;
tabChange();
}
}
// Ctrl+9 - go to last tab
if (evt.keyCode == 57) {
tabBar.selectedIndex = tabData.length - 1;
tabChange();
}
}
]]>
</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>
<menuitem label="Edit">
<menuitem label="Cut" key="x" controlKey="true" />
<menuitem label="Copy" key="c" controlKey="true" />
<menuitem label="Paste" key="v" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" 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>
</root>
</fx:XML>
<mx:ArrayCollection id="tabData">
<fx:Object title="Untitled" textData="" saved="false" seletedActive="0" selectedAnchor="0" />
</mx:ArrayCollection>
</fx:Declarations>
<s:Group width="100%" height="100%">
<s:TextArea id="textArea" width="100%" height="{textHeight}" y="{textY}" borderVisible="false" lineBreak="{(pref_wrap)?(toFit):(explicit)}" click="cursorFix(); updateStatus();" change="updateStatus();" keyDown="updateStatus();"/>
<custom:CustomTabBar id="tabBar" dataProvider="{tabData}" width="100%" y="{tabY}" itemRenderer="CustomTab" height="22" tabClose="onTabClose(event);" change="tabChange();" />
<mx:Box id="toolBar" width="100%" backgroundColor="#dddddd" height="24" visible="{pref_toolbar}">
</mx:Box>
</s:Group>
</s:WindowedApplication>
Thanks for reading!
Subscribe to:
Posts (Atom)