I need a text editor control for column edit in my application.
Like notepad++ alt+mouse drag to select a text block , or pull down a long vertical cursor and press a key to insert a char to every lines over cursor.
I tried ScintillaNET but it not suppor the column-modet for insert, just can remove selected text block
I need below effect(notepad++), But ScintillaNET got:
I found a way to solve. Still using ScintillaNET.
But coding not enough beauty :)
class SelWrap
{
public int Begin { get; set; }
public int Length { get; set; }
}
//...
editor.KeyDown += (s, e) =>
{
// filter alt we will hold down alt to make vertical selection
if (e.Alt) return;
var tb = editor;
if (tb.Selections.Count < 2) return; // no in column mode
e.SuppressKeyPress = true; //block input, handle by below code
//refered from post #5825820
var input = Utility.GetCharFromKey(e.KeyCode).ToString();
if (input == "\0")
{
//SystemSounds.Beep.Play();
return;
}
var array = tb.Selections
.OrderBy(p => p.Start)
.Select(p => new SelWrap{Begin=p.Start, Length=p.End - p.Start })
.ToArray();
//do process every caret(or selection)
for (var i = 0; i < array.Length; i++)
{
var item = array[i];
if (item.Length > 0)
{
//if has selected text, just clean
tb.DeleteRange(item.Begin, item.Length);
for (var j = i + 1; j < array.Length; j++)
{
array[j].Begin -= item.Length;
}
}
if (input == "\b") //backspace
{
if (item.Length != 0) continue;
//delete a char before caret
tb.DeleteRange(item.Begin - 1, 1);
for (var j = i; j < array.Length; j++)
{
array[j].Begin--;
}
}
else //just insert that
{
tb.InsertText(item.Begin, input);
for (var j = i; j < array.Length; j++)
{
array[j].Begin++;
}
}
}
//restore caret status to keep column mode
tb.ClearSelections();
tb.SetSelection(array[0].Begin, array[0].Begin);
for (var i = 1; i < array.Length; i++)
{
var item = array[i];
tb.AddSelection(item.Begin, item.Begin);
}
};
//...
You need to turn on Scintilla rectangular selection by sending required messages to initialize rectangular selection. Example,
CallScintilla(SCI_SETSELECTIONMODE, SC_SEL_STREAM);
CallScintilla(SCI_SETMOUSESELECTIONRECTANGULARSWITCH, true);
CallScintilla(SCI_SETADDITIONALSELECTIONTYPING, true); // so you can type in the selection
CallScintilla(SCI_SETMULTIPLESELECTION, false);
// on paste, paste into rectangular selection
CallScintilla(SCI_SETMULTIPASTE, SC_MULTIPASTE_EACH);
After that, it works like Visual Studio, you hold down the Alt key and drag with the mouse to start a rectangular selection.
Related
enter image description here
// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
// add all booleans , and check each column if its still all false, if not ! , cntrl z .
// for each elemnt in pairs
for (int i = 0; i < pair_count; i++)
{
// add first pair edge locked by boolean true
locked[pairs[i].winner][pairs[i].loser] = true;
// check if we cause ' no column with full false situation '
bool there_is_column_full_false;
for (int column = 0; column < candidate_count; column++) // for each column
{
there_is_column_full_false = false;
for (int row = 0; row < candidate_count; row++) // for each row
{
if (locked[column][row])
{
break;
}
there_is_column_full_false = true;
}
if (there_is_column_full_false)
{
break;
}
}
if (!there_is_column_full_false)
{
locked[pairs[i].winner][pairs[i].loser] = false;
}
}
}
So i am adding some elements to a map control like this
foreach (var res in results)
{
if (res.geometry.location != null)
{
var pushpin = new Image();
pushpin.Name = "a";
BasicGeoposition bs = new BasicGeoposition { Latitude = res.geometry.location.lat, Longitude = res.geometry.location.lng };
pushpin.Source = new BitmapImage(uri);
pushpin.Height = 50;
pushpin.Width = 50;
myMap.Children.Add(pushpin);
MapControl.SetLocation(pushpin, new Geopoint(bs));
}
}
Now i want to remove elements names "a" form the control and i am using following code
int c = myMap.Children.Count;
for (int i = 0; i < c; i++)
{
if(myMap.Children.ElementAt(i) is Image)
{
var z = myMap.Children.ElementAt(i) as Image;
if(z.Name.Equals("a"))
{
myMap.Children.Remove(myMap.Children.ElementAt(i));
}
}
}
But always some elements are not getting removed ,for example the count of children is coming 21,but the loop is looping only 10 time.
How can i solve this problem?
try it with looping backwards so you dont mess up your collection during the loop.
int c = myMap.Children.Count - 1;
for (int i = c; i >= 0; i--)
{
if (myMap.Children.ElementAt(i) is Image)
{
var z = myMap.Children.ElementAt(i) as Image;
if(z.Name.Equals("a"))
{
myMap.Children.Remove(myMap.Children.ElementAt(i));
}
}
}
So, basically I'm reinventing the wheel by trying to make a sort of spreadsheet in flash for tracking member growth in a game. In this one section I'm adding member names to an array and then placing the names into dynamically created tiles with text fields attached to display the name.
I have a save button which saves the array, and if I save, close, and reopen, then I can see the members I have added. However, I would like it to refresh the stage as soon as the array is changed to reflect the changes made (update the spreadsheet). I will also be adding and removing other tiles dynamically, but I can extrapolate the solution to this problem to all of those later.
Here's the code I have to add members and create the display:
public var mainArray:Array = new Array;
public var i1:Number = 0;
public var memberBox:MovieClip = new MovieClip();
public var MAX_ROWS = 0;
public var MAX_COLS = 0;
public function Tracker() {
MAX_COLS = mainArray.length - 1;
addBtn.addEventListener(MouseEvent.CLICK, addFun); //atchaed to button on stage
public function addFun($e:MouseEvent):void{
mainArray[i1] = [];
mainArray[i1][0] = addNameTxt.text //attached to textfield on stage
i1++;
loadMembers();
public function loadMembers():void{
var multiDimensionalArray:Array = new Array();
//initalize the arrays
for (var row = 0; row <= MAX_ROWS; row++)
{
var boolArray:Array = new Array();
for (var col = 0; col <= MAX_COLS; col++){
boolArray.push(false);
}
multiDimensionalArray.push(boolArray);
}
//now we can set the values of the array as usual
for (var row = 0; row <= MAX_ROWS; row++)
{
for (var col = 0; col <= MAX_COLS; col++){
multiDimensionalArray.push(1); ;
}
}
//create a column of tiles based on mainArray length with a textfield attached to each
buildLevel(multiDimensionalArray);
}
public function buildLevel(s:Array){
var txtArray:Array = new Array();
memberBox.name = "tileHolder";
for(var i=0; i < MAX_ROWS + 1; i++){
for(var o=0; o < MAX_COLS + 1; o++){
var currentTile:MemberBox = new MemberBox();
currentTile.x = i*150;
currentTile.y = o*25;
currentTile.name = "b"+o;
memberBox.addChild(currentTile);
//currentTile.gotoAndStop(int(s[o][i]));
var memberTxt:TextField=new TextField();
currentTile.addChild(memberTxt);
memberTxt.width = 150;
memberTxt.height = 25;
txtArray[o] = memberTxt;
txtArray[o].text = mainArray[o][0];
}
}
memberBox.x = 60;
memberBox.y = 170;
addChild(memberBox);
}
}
The ideal solution would be to attach event listeners to the Array, however, Arrays don't fire events.
Solution #1: Managed Updates
Rather than allowing any piece of code to modify your array directly, write a function that handles updating your array. This way, you can know when to update the display at the same time.
public var mainArray:Array = new Array;
public var i1:Number = 0;
public var memberBox:MovieClip = new MovieClip();
public var MAX_ROWS = 0;
public var MAX_COLS = 0;
public function Tracker() {
MAX_COLS = mainArray.length - 1;
addBtn.addEventListener(MouseEvent.CLICK, addFun); //attached to button on stage
public function addFun(e:MouseEvent):void {
mainArray[i1] = [];
mainArray[i1][0] = addNameTxt.text //attached to textfield on stage
i1++;
loadMembers();
}
public function loadMembers():void {
var multiDimensionalArray:Array = new Array();
//initalize the arrays
for (var row = 0; row <= MAX_ROWS; row++) {
var boolArray:Array = new Array();
for (var col = 0; col <= MAX_COLS; col++) {
boolArray.push(false);
}
multiDimensionalArray.push(boolArray);
}
//now we can set the values of the array as usual
for (var row = 0; row <= MAX_ROWS; row++) {
for (var col = 0; col <= MAX_COLS; col++) {
multiDimensionalArray.push(1); ;
}
}
//create a column of tiles based on mainArray length with a textfield attached to each
buildLevel(multiDimensionalArray);
}
public function buildLevel(s:Array) {
var txtArray:Array = new Array();
memberBox.name = "tileHolder";
for (var i:int = 0; i < MAX_ROWS + 1; i++) {
for(var o=0; o < MAX_COLS + 1; o++) {
var currentTile:MemberBox = new MemberBox();
currentTile.name = i + "_" + o;
currentTile.x = i*150;
currentTile.y = o*25;
memberBox.addChild(currentTile);
//currentTile.gotoAndStop(int(s[o][i]));
var memberTxt:TextField=new TextField();
currentTile.addChild(memberTxt);
memberTxt.width = 150;
memberTxt.height = 25;
txtArray[o] = memberTxt;
txtArray[o].text = mainArray[o][0];
}
}
memberBox.x = 60;
memberBox.y = 170;
addChild(memberBox);
}
public function modifyArray(row:int, col:int, value:*):void {
// Update our array.
mainArray[row][col] = value;
// Update our tile.
var tile:MemberBox = memberBox.getChildByName(row + "_" + col);
tile.getChildAt(0).text = value;
}
}
When you actually modified your array, you'd do that with your function, rather than a direct mainArray[o][i] approach.
// Instead of this
mainArray[o][i] = "foo";
// You'd do this
modifyArray(o, i, "foo");
Update: I'm not sure how better to explain this, so I've posted working example that you can view. It contains the class, and the document code, the fla, and the swf with working updates to the spreadsheet. Let me know if that resolves the issue: SpreadSheet Test # Dropbox
Solution #2: Poll for Changes
Hold two versions of your array in memory. The first is the one you're actively editing, the second is an untouched duplicate to compare against the first. Every so often (once per second?) you iterate over the entire dataset and look for differences. When one is found, update the duplicate array and the displayed spreadsheet.
Here's an example of how you'd do that:
import flash.utils.*;
var last:Number = flash.utils.getTimer();
this.addEventListener("enterFrame", checkData);
var foo:Array = ["a", "b", "c"];
var fooBackup:Array = [];
function update():void {
var txt:TextField;
for (var i:int = 0; i < foo.length; i++) {
// If we don't have a representation of this index, create one.
if (!this.getChildByName("tile"+i)) {
txt = new TextField();
txt.name = "tile" + i;
txt.text = foo[i];
this[txt.name] = txt;
addChild(txt);
txt.y = i * 20;
}
// Update the data if inconsistent.
txt = this.getChildByName("tile"+i) as TextField;
if (fooBackup.hasOwnProperty(i) == false || foo[i] != fooBackup[i]) {
fooBackup[i] = foo[i]
txt.text = foo[i];
trace("Index " + i + " changed. Updated backup, and display.");
}
}
}
function checkData(e:Event):void {
var now:Number = flash.utils.getTimer();
// If the last time we checked is greater than a second, run the check again.
if (now - last > 1000) {
// Update last checked index;
last += 1000;
// For the sake of argument, let's randomly change an index to a random value.
foo[random(0, foo.length-1)] = random(-1000, 1000);
update();
}
}
function random(low:Number=0, high:Number=1):Number {
/* Returns a random number between the low and high values given. */
return Math.floor(Math.random() * (1+high-low)) + low;
}
Solution #3: Roll your own Array
As outlined by Adobe's Extending the Array class, you can subclass Arrays and from there add your own event dispatches when you add/remove entries. This is the more adventurous solution, and (in my humble opinion) the best as it maintains code consistency, while giving broad and efficient powers.
i'm urgently looking for a solution to delete all duplicates from an array in Flex.
I need to filter out all duplicates in the filteredData array.
The indexOf doesn't do the trick here, i believe because objects are being loaded into the array.
Thanks in advance
var columnArray:Array = ['ingenre']; //Datagrid column names to filter on
var songArray:Array = ['id']; //Datagrid column names to filter on
var gridDataProvider:ArrayCollection = PhotoData; //Name of datagrid's dataprovider. In this case e.g. databases
var dataGridName:String = 'dgUserRequest'; //Name of the datagrid you are filtering by
//Do not edit code past this point
var filteredData:Array = [];
var added:Boolean=false;
var i:int;
var j:int;
var filtertxt: String;
var matchtxt:String;
// Loop Through Grid
for(i=0; i < gridDataProvider.length; i++){
added = false;
//Loop through grid column
for(j=0; j<columnArray.length; j++){
if(gridDataProvider[i][columnArray[j]]!=null){
//Grab datagrid cell contents, trim it, and convert to lowercase for comparison.
var filterString:String = gridDataProvider[i][columnArray[j]].toString().toLowerCase();
var songIdString:String = gridDataProvider[i][songArray[j]].toString().toLowerCase();
//Compare the datagrid string(filterString) to the user typed string(filterText).
if(!added){
// loop through the Array & check if there is a match.
for (var k:int = 0;k < filterText.length; k++)
{
filtertxt = filterString.toLowerCase();
matchtxt = filterText[k].toLowerCase();
//if(filterString.toLowerCase() == filterText[k].toLowerCase()){
if(filtertxt.search(matchtxt) >= 0){
if (cmd_classic.selected == true)
{
trace(filteredData.indexOf(songIdString));
if(filteredData.indexOf(songIdString) < 0)
{
if ((filtertxt.search("class") == -1) && (filtertxt.search("baroque") == -1))
{
if (filteredData.indexOf(songIdString) == -1)
{
filteredData.push(gridDataProvider[i]);
added = true;
}
}
else
{
break;
}
}
}
else
{
if (filteredData.lastIndexOf(songIdString) <= 0)
{
filteredData.push(gridDataProvider[i]);
added = true;
}
}
}
}
}else{
//Do nothing, break out.
break;
}
}
}
}
Flex doesn't really have a Set class that I'm aware of. But what I've done in the past when I need to get a list of unique values from a list is to create a new Dictionary. Loop through the list using the values I want to dedupe as the key and getting the key set when I'm finished.
I'm building a word search game using the following AS3 code. My problem is I need to have spaces in my array of words, so that I can have things like states names, but I need the spaces removed before the words go into the puzzle.
The other concern is also that when a person selects a word from the puzzle will it still match the word in the list even though the word in the list still has a space.
I've struggled with this for a few days now and could use some help.
I've pasted all appropriate code below, I believe. Thanks.
Rich
// words and grid
private var wordList:Array;
private var usedWords:Array;
private var grid:Array;
// sprites
private var letterSprites:Sprite;
private var wordsSprite:Sprite;
wordList = ("New York,New Jersey,South Carolina,North Carolina").split(",");
// set up the sprites
gameSprite = new Sprite();
addChild(gameSprite);
letterSprites = new Sprite();
gameSprite.addChild(letterSprites);
wordsSprite = new Sprite();
gameSprite.addChild(wordsSprite);
// array of letters
var letters:Array = placeLetters();
// create word list fields and sprites
for(var i:int=0;i<usedWords.length;i++) {
var newWord:TextField = new TextField();
newWord.defaultTextFormat = letterFormatForList;
if(i < 20){ // first list
newWord.x = listXposition;
newWord.y = i*spacingForList+listYposition;
} else { // second list
newWord.x = listXposition + 130;
newWord.y = i*spacingForList+listYposition - (20 * 19);
}
newWord.width = 135;
newWord.height = spacingForList;
newWord.text = usedWords[i];
newWord.selectable = false;
wordsSprite.addChild(newWord);
}
// set game state
dragMode = "none";
numFound = 0;
}
// place the words in a grid of letters
public function placeLetters():Array {
// create empty grid
var letters:Array = new Array();
for(var x:int=0;x<puzzleSize;x++) {
letters[x] = new Array();
for(var y:int=0;y<puzzleSize;y++) {
letters[x][y] = "*";
}
}
// make copy of word list
var wordListCopy:Array = wordList.concat();
usedWords = new Array();
// make 1000 attempts to add words
var repeatTimes:int = 1000;
repeatLoop:while (wordListCopy.length > wordsLeft) {
if (repeatTimes-- <= 0) break;
// pick a random word, location and direction
var wordNum:int = Math.floor(Math.random()*wordListCopy.length);
var word:String = wordListCopy[wordNum].toUpperCase();
x = Math.floor(Math.random()*puzzleSize);
y = Math.floor(Math.random()*puzzleSize);
var dx:int = Math.floor(Math.random()*3)-1;
var dy:int = Math.floor(Math.random()*3)-1;
if ((dx == 0) && (dy == 0)) continue repeatLoop;
// check each spot in grid to see if word fits
letterLoop:for (var j:int=0;j<word.length;j++) {
if ((x+dx*j < 0) || (y+dy*j < 0) || (x+dx*j >= puzzleSize) || (y+dy*j >= puzzleSize)) continue repeatLoop;
var thisLetter:String = letters[x+dx*j][y+dy*j];
if ((thisLetter != "*") && (thisLetter != word.charAt(j))) continue repeatLoop;
}
// insert word into grid
insertLoop:for (j=0;j<word.length;j++) {
letters[x+dx*j][y+dy*j] = word.charAt(j);
}
// remove word from list
wordListCopy.splice(wordNum,1);
usedWords.push(word);
}
// fill rest of grid with random letters
for(x=0;x<puzzleSize;x++) {
for(y=0;y<puzzleSize;y++) {
if (letters[x][y] == "*") {
letters[x][y] = String.fromCharCode(65+Math.floor(Math.random()*26));
}
}
}
return letters;
}
// player clicks down on a letter to start
public function clickLetter(event:MouseEvent) {
var letter:String = event.currentTarget.getChildAt(0).text;
startPoint = findGridPoint(event.currentTarget);
dragMode = "drag";
}
// player dragging over letters
public function overLetter(event:MouseEvent) {
if (dragMode == "drag") {
endPoint = findGridPoint(event.currentTarget);
// if valid range, show outline
outlineSprite.graphics.clear();
if (isValidRange(startPoint,endPoint)) {
drawOutline(outlineSprite,startPoint,endPoint,0xCCCCCC);
}
}
}
// mouse released
public function mouseRelease(event:MouseEvent) {
if (dragMode == "drag") {
dragMode = "none";
outlineSprite.graphics.clear();
// get word and check it
if (isValidRange(startPoint,endPoint)) {
var word = getSelectedWord();
checkWord(word);
}
}
}
// when a letter is clicked, find and return the x and y location
public function findGridPoint(letterSprite:Object):Point {
// loop through all sprites and find this one
for(var x:int=0;x<puzzleSize;x++) {
for(var y:int=0;y<puzzleSize;y++) {
if (grid[x][y] == letterSprite) {
return new Point(x,y);
}
}
}
return null;
}
// determine if range is in the same row, column, or a 45 degree diagonal
public function isValidRange(p1,p2:Point):Boolean {
if (p1.x == p2.x) return true;
if (p1.y == p2.y) return true;
if (Math.abs(p2.x-p1.x) == Math.abs(p2.y-p1.y)) return true;
return false;
}
// draw a thick line from one location to another
public function drawOutline(s:Sprite,p1,p2:Point,c:Number) {
var off:Point = new Point(offset.x+spacing/2, offset.y+spacing/2);
s.graphics.lineStyle(outlineSize,c);
s.graphics.moveTo(p1.x*spacing+off.x ,p1.y*spacing+off.y-3);
s.graphics.lineTo(p2.x*spacing+off.x ,p2.y*spacing+off.y-3);
}
// find selected letters based on start and end points
public function getSelectedWord():String {
// determine dx and dy of selection, and word length
var dx = endPoint.x-startPoint.x;
var dy = endPoint.y-startPoint.y;
var wordLength:Number = Math.max(Math.abs(dx),Math.abs(dy))+1;
// get each character of selection
var word:String = "";
for(var i:int=0;i<wordLength;i++) {
var x = startPoint.x;
if (dx < 0) x -= i;
if (dx > 0) x += i;
var y = startPoint.y;
if (dy < 0) y -= i;
if (dy > 0) y += i;
word += grid[x][y].getChildAt(0).text;
}
return word;
}
// check word against word list
public function checkWord(word:String) {
// loop through words
for(var i:int=0;i<usedWords.length;i++) {
// compare word
if (word == usedWords[i].toUpperCase()) {
foundWord(word);
}
// compare word reversed
var reverseWord:String = word.split("").reverse().join("");
if (reverseWord == usedWords[i].toUpperCase()) {
foundWord(reverseWord);
}
}
}
// word found, remove from list, make outline permanent
public function foundWord(word:String) {
sndSuccess=new success_sound();
sndSuccessChannel=sndSuccess.play(200);
so.data.totalWordsFound = so.data.totalWordsFound + 1;
so.flush();
// draw outline in permanent sprite
drawOutline(oldOutlineSprite,startPoint,endPoint,0xDDDDDD);
// find text field and set it to gray
for(var i:int=0;i<wordsSprite.numChildren;i++) {
if (TextField(wordsSprite.getChildAt(i)).text.toUpperCase() == word) {
TextField(wordsSprite.getChildAt(i)).textColor = 0x777777;
}
}
// see if all have been found
numFound++;
if (numFound == usedWords.length) {
if (so.data.difficulty == "Easy") {
so.data.easyWon = so.data.easyWon + 1;
}
if (so.data.difficulty == "Medium") {
so.data.mediumWon = so.data.mediumWon + 1;
}
if (so.data.difficulty == "Hard") {
so.data.hardWon = so.data.hardWon + 1;
}
so.flush();
endGame();
}
}
I'm hesitant to post an answer as I'm not really sure what's going on, but hopefully this info will help:
Keep your words in the arrays with spaces, that's a good idea. When you want to check against the words in your game, just convert both words to the same format using a function. eg:
var wordList:Array = ("New York,New Jersey,South Carolina,North Carolina").split(",");
var selectedWord:String = "NEWYORK";
// minWord converts all words to the same format of no spaces and all lower case.
function minWord(word:String):String {
return word.replace(/\s/g, "").toLowerCase();
}
// this loop checks all the words from your array against the selectedWord.
for each(var word:String in wordList) {
trace(minWord(word) + " == " + minWord(selectedWord) + "; ", minWord(word) == minWord(selectedWord));
}
Hope that helps!