i have a TitleWindow popup which opens a videoDisplay to play a video when i click on a thumb.What i want is my popup to resize and the video inside it but to maintain its original aspect ratio and not stretch...
any ideas?
Thanks a lot! Here is my popUp:
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
close="CloseWindow(event)" >
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.CloseEvent;
import mx.events.ResizeEvent;
import mx.managers.PopUpManager;
[Bindable]public var mediaServerUrl:String;
[Bindable]public var videoFolder:String;
[Bindable]public var filename:String;
[Bindable]public var comments:String;
private var ns:NetStream;
private var nc:NetConnection;
private var video:Video;
private var meta:Object;
private function ns_onMetaData(item:Object):void {
trace("meta");
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
myVid.width = video.width;
myVid.height = video.height;
}
private function fetch_rec():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = nsClient;
video = new Video(myVid.width,myVid.height);
video.attachNetStream(ns);
video.smoothing=true;
myVid.addChild(video);
ns.play(mediaServerUrl+"/"+videoFolder+"/"+filename+".flv");
}
protected function CloseWindow(event:CloseEvent):void
{
ns.close();
nc.close();
PopUpManager.removePopUp(this);
}
]]>
</fx:Script>
<mx:VideoDisplay id="myVid" visible="true" x="0" bottom="50" width="100%" height="100%"
maintainAspectRatio="true"
autoPlay="true"
creationComplete="fetch_rec()"
playheadUpdate="progBar.setProgress(myVid.playheadTime,myVid.totalTime)"/>
<mx:ProgressBar id="progBar" left="10" right="10" bottom="60" height="10" label="" mode="manual"/>
<s:Label x="10" bottom="30" text="Σχόλια:"/>
<s:Label x="10" bottom="10" text="{comments}"/></s:TitleWindow>
to call this popup i do:
protected function launchPopUp(event:MouseEvent):void
{
if(list.selectedItem){
win = new ViewVideoPopUp();
win.width = this.width;
win.height = this.height;
//give what is needed to play the video selected
win.videoFolder = videoFolder; // the video's folder name
win.mediaServerUrl = mediaServerUrl; // the media server url
win.filename = list.selectedItem.filename; // the file to be played
win.comments = list.selectedItem.comments; // the comments left for that
win.title = list.selectedItem.name+" στις "+list.selectedItem.date; //title of the window
this.addEventListener(ResizeEvent.RESIZE, window_resize);
PopUpManager.addPopUp(win,this,true);
PopUpManager.centerPopUp(win);
}
}
EDIT (12/15):
OK, I tried your code and added a method to force the aspect ratio of the video based on the aspect ratio of the parent container. I put a HGroup around the VideoDisplay component, and used that to figure out how the video should be sized. It also centers the video in the popup if the window and video are different sizes.
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
close="CloseWindow(event)" autoLayout="true">
<fx:Script>
<![CDATA[
import mx.events.CloseEvent;
import mx.events.ResizeEvent;
import mx.managers.PopUpManager;
[Bindable]public var mediaServerUrl:String;
[Bindable]public var videoFolder:String;
[Bindable]public var filename:String;
[Bindable]public var comments:String;
private var ns:NetStream;
private var nc:NetConnection;
private var video:Video;
private var meta:Object;
private function ns_onMetaData(item:Object):void {
trace("meta");
meta = item;
var vidAspectRatio:Number = item.width / item.height;
var titleWindowAspectRatio:Number = vidContainer.width / vidContainer.height;
// Resize Video object to same size as meta data.
if ( vidAspectRatio < titleWindowAspectRatio ) // TitleWindow too wide
{
video.height = vidContainer.height;
video.width = video.height * vidAspectRatio;
}
else if ( vidAspectRatio > titleWindowAspectRatio ) // TitleWindow too tall
{
video.width = vidContainer.width;
video.height = video.width / vidAspectRatio;
}
else // TitleWindow and Video have same aspect ratio and fits just right
{
video.width = vidContainer.width;
video.height = vidContainer.height;
}
// Resize UIComponent to same size as Video object.
myVid.width = video.width;
myVid.height = video.height;
}
private function fetch_rec():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = nsClient;
video = new Video(myVid.width,myVid.height);
video.attachNetStream(ns);
video.smoothing=true;
myVid.addChild(video);
ns.play("../swf/barsandtone.flv");
}
protected function CloseWindow(event:CloseEvent):void
{
ns.close();
nc.close();
PopUpManager.removePopUp(this);
}
]]>
</fx:Script>
<s:HGroup id="vidContainer" verticalAlign="middle" horizontalAlign="center" height="100%" width="100%" bottom="50" >
<mx:VideoDisplay id="myVid" visible="true"
autoPlay="true"
creationComplete="fetch_rec()"
playheadUpdate="progBar.setProgress(myVid.playheadTime,myVid.totalTime)"/>
</s:HGroup>
<mx:ProgressBar id="progBar" left="10" right="10" bottom="60" height="10" label="" mode="manual"/>
<s:Label x="10" bottom="30" text="Σχόλια:"/>
<s:Label x="10" bottom="10" text="{comments}"/>
</s:TitleWindow>
Related
I call a web service which gets 4 files, and while these files are loading I want to show progress to the user (circular or horizontal it doesn't matter). I've followed the examples on internet but nothing appears on screen.
MobileSellReference.Service1 service = new MobileSellReference.Service1();
service.Url = settings.Synchronization.Msellurl;
ProgressBar progressBar = FindViewById<ProgressBar>(Resource.Id.progressBar);
progressBar.Max = 100;
progressBar.Progress = 0;
byte[][] basedataResult = service.ToPPC(basedataZipName, objectId);
progressBar.IncrementProgressBy(25);
byte[][] dutybasedataResult = service.ToPPC(dutybasedataZipName, objectId);
progressBar.IncrementProgressBy(25);
byte[][] tranbasedataResult = service.ToPPC(tranbasedataZipName, objectId);
progressBar.IncrementProgressBy(25);
byte[][] vendbasedataResult = service.ToPPC(vendbasedataZipName, objectId);
progressBar.IncrementProgressBy(25);
I've found a lot of examples using external progressbar libraries but they all want to change the theme of the Activity. Instead I want some simple ProgressBar built into Xamarin.Android. For example when the first file is downloaded I want 1/4 of the circle to be filled, when 2 files are downloaded 1/2 of the circle to be filled et cetera. Similarly for a horizontal ProgressBar.
Use AsyncTask
.axml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="0" />
<ProgressBar
android:id="#+id/pb"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>
MainActivity:
public class MainActivity : Activity
{
ProgressBar pb;
TextView tv;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
pb = FindViewById<ProgressBar>(Resource.Id.pb);
tv = FindViewById<TextView>(Resource.Id.tv);
UpdatePB uptask = new UpdatePB(this,pb,tv);
uptask.Execute(100);
}
public class UpdatePB : AsyncTask<int, int, string>
{
Activity mcontext;
ProgressBar mpb;
TextView mtv;
public UpdatePB(Activity context,ProgressBar pb,TextView tv) {
this.mcontext = context;
this.mpb = pb;
this.mtv = tv;
}
protected override string RunInBackground(params int[] #params)
{
// TODO Auto-generated method stub
for (int i = 1; i <= 4; i++)
{
try
{
Thread.Sleep(3000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
Android.Util.Log.Error("lv",e.Message);
}
mpb.IncrementProgressBy(25);
PublishProgress(i * 25);
}
return "finish";
}
protected override void OnProgressUpdate(params int[] values)
{
mtv.Text = String.ValueOf(values[0]);
Android.Util.Log.Error("lv==", values[0] + "");
}
protected override void OnPostExecute(string result)
{
mcontext.Title = result;
}
}
}
This might be a helpful link:
https://developer.android.com/reference/android/widget/ProgressBar.html
Code:
<ProgressBar
android:id="#+id/determinateBar"
style="#android:style/Widget.ProgressBar.Horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:progress="25"/>
... and then you just change the progress; 25, 50, 75, 100?
After running into the same problem, I found another solution that got it working. I was reluctant to define a new class (like AsyncTask) to fix this, so looked into async await and threading. I found that after defining an Android.Widget.ProgressBar in an .axml layout file like so:
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="0"
style="?android:attr/progressBarStyleHorizontal" />
I could get it to update if I put the updating tasks in a System.Threading.Tasks.Task.Run and passing in an action that does the updates with RunOnUiThread call like:
btnDoStuff.Click += (sender, e) =>
{
Task.Run(() =>
{
RunOnUiThread(() =>
{
progressBar.Max = 100;
progressBar.Progress = 0;
progressBar.Visibility = ViewStates.Visible;
});
DoSomeWork1(arguments);
RunOnUiThread(() => progressBar.Progress += 25);
DoSomeWork2(arguments);
RunOnUiThread(() => progressBar.Progress += 25);
DoSomeWork3(arguments);
RunOnUiThread(() => progressBar.Progress += 25);
DoSomeWork4(arguments);
RunOnUiThread(() => progressBar.Progress += 25);
});
}
But even then I've had some inconsistent behavior - there may be an element of timing to it as well...
I'm trying to create a list view with a set of remote images and while they are loading to display a placeholder image.
var imageSource = require("image-source");
var imageCache = require("ui/image-cache");
var cache = new imageCache.Cache();
var defaultImageSource = imageSource.fromResource("img-loading”);
cache.enableDownload();
cache.placeholder = defaultImageSource;
cache.maxRequests = 5;
Tried with fromFile as well instead of fromResource.
Any toughts?
I'm not sure how are you using the ImageCache but you cannot directly set an ImageCache object as source of an Image. In order to achieve what you want you must use something like this (taken from http://pstaev.blogspot.com/2016/04/using-nativescripts-imagecache-to-cache.html):
main-page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
navigatingTo="navigatingTo">
<ListView items="{{ images }}">
<ListView.itemTemplate>
<GridLayout>
<Image src="{{ imageSrc }}" stretch="aspectFill" height="100"/>
</GridLayout>
</ListView.itemTemplate>
</ListView>
</Page>
main-page.ts
import observableArray = require("data/observable-array");
import observable = require("data/observable");
import imageItem = require("./image-item");
import pages = require("ui/page");
export function navigatingTo(args: pages.NavigatedData)
{
var page = <pages.Page>args.object;
var model = new observable.Observable();
var images = new observableArray.ObservableArray<imageItem.ImageItem>();
images.push(new imageItem.ImageItem("http://foo.com/bar1.jpg"));
images.push(new imageItem.ImageItem("http://foo.com/bar2.jpg"));
// ...
images.push(new imageItem.ImageItem("http://foo.com/bar100.jpg"));
model.set("images", images);
page.bindingContext = model;
}
image-item.ts
import observable = require("data/observable");
import imageCache = require("ui/image-cache");
import imageSource = require("image-source");
var cache = new imageCache.Cache();
cache.maxRequests = 10;
cache.placeholder = imageSource.fromResource("img-loading")
export class ImageItem extends observable.Observable
{
private _imageSrc: string
get imageSrc(): imageSource.ImageSource
{
var image = cache.get(this._imageSrc);
if (image)
{
return image;
}
cache.push(
{
key: this._imageSrc
, url: this._imageSrc
, completed:
(image) =>
{
this.notify(
{
object: this
, eventName: observable.Observable.propertyChangeEvent
, propertyName: "imageSrc"
, value: image
});
}
});
return cache.placeholder;
}
constructor(imageSrc : string)
{
super();
this._imageSrc = imageSrc;
}
}
I'm following Joe Ward's Pixel Bender basics for Flex and AIR tutorial and updating it for Flex 4.5 Flashplayer 11.
While working on the grainBlend section It Works great, if I have an "Alert" message pop up. Otherwise the shader does not refresh/update when the HSlider is changed.
In other word: The script runs IF I have an active Alert message. If I remove the Alert Message, the blendShader only works once, and never updates afterwards.
ScriptFlow:
Init() OK
create shader OK
detect HSlider Change OK
updateFilter() OK
update Shader's turbulace value OK
update image "noise" shader and redraw NOT WORKING
I believe the following excerpt from the tutorial may be the issue. "...Because a shader object is cloned when you set the blendShader property of a display object, you cannot simply change the parameter of the original Shader object. You must also reassign the updated Shade object to the blendShader property...."
shader.data.turbulence.value = [turbulence.value];
noise.blendMode = BlendMode.SHADER;
noise.blendShader = shader;
Flex code
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
contentCreationComplete="init()"
backgroundColor="0x666666">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.SliderEvent;
//Embed the Pixel Bender kernel in the output SWF
[Embed(source="kernels/grainblend.pbj", mimeType="application/octet-stream")]
private var GrainBlendKernel:Class;
//Create the Shader object
[Bindable]
private var shader:Shader = new Shader( new GrainBlendKernel() );
private function init():void
{
//Set the slider values based on the parameter metadata
turbulence.minimum = shader.data.turbulence.minValue;
turbulence.maximum = shader.data.turbulence.maxValue;
turbulence.value = shader.data.turbulence.defaultValue;
turbulence.addEventListener( SliderEvent.CHANGE, updateFilter );
//Apply the blend
noise.blendShader = shader;
}
private function updateFilter( event:Event ):void
{
trace(turbulence.value);//print slider
//Alert.show("Hit");
shader.data.turbulence.value = [turbulence.value];
trace("shader's value: "+shader.data.turbulence.value);
noise.blendMode = BlendMode.SHADER;
noise.blendShader = shader;
}
]]>
</fx:Script>
<s:VGroup width="100%">
<s:HGroup width="100%" height="100%" horizontalAlign="center" verticalAlign="top">
<s:VGroup>
<mx:Canvas width="195" height="194" backgroundColor="#663300"/>
<s:Label text="Background" textAlign="center" width="196"/>
</s:VGroup>
<s:VGroup>
<s:Image source="img/noise.jpg" width="195" height="194"/>
<s:Label text="Perlin noise" width="196" textAlign="center"/>
</s:VGroup>
<s:VGroup>
<mx:Canvas width="195" height="194" backgroundColor="#663300">
<s:Image source="img/noise.jpg" id="noise" width="195" height="194"/>
</mx:Canvas>
<s:Label text="Grain blend" width="196" textAlign="center"/>
</s:VGroup>
</s:HGroup>
<s:HGroup width="100%" horizontalAlign="center" verticalAlign="top">
<s:Label text="{turbulence.value}"/>
<s:HSlider id="turbulence" width="200"/>
</s:HGroup>
</s:VGroup>
Pixel Bender Kernel
<languageVersion: 1.0;>
kernel GrainBlend
< namespace : "com.adobe.example";
vendor : "Adobe Systems Inc.";
version : 1;
description : "Creates a wood grain or marbleing effect"; >
{
input image4 background;
input image4 noise;
output pixel4 dst;
parameter float turbulence
<
maxValue : 500.0;
minValue : 0.0;
defaultValue : 150.0;
>;
void evaluatePixel()
{
pixel4 a = sampleNearest(background, outCoord());
pixel4 b = sampleNearest(noise, outCoord());
float alpha = a.a; //save the original alpha
if( (b.a > 0.0) && (a.a > 0.0)){
float seed = outCoord().x + (((b.r + b.g + b.b)/3.0) * turbulence);
float grain = (0.7 * sin(seed) + 0.3 * sin(2.0 * seed + 0.3) + 0.2 * sin(3.0 * seed + 0.2));
dst = sampleNearest(background, outCoord()) * (grain + 0.5);
dst.a = alpha; //restore the original alpha
}
else {
//Just copy the background pixel outside the area of the noise image
dst = sampleNearest(background, outCoord());
}
}
}
I gave up on blendShader. I recreated it using filters. It works now. Also, I dynamically created the brown background and the perlin noise.
See below!
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
backgroundColor="0x666666">
<fx:Script>
<![CDATA[
import mx.events.SliderEvent;
import spark.filters.ShaderFilter;
//Embed the Pixel Bender kernel in the output SWF
[Embed(source="kernels/grainblend.pbj", mimeType="application/octet-stream")]
private var GrainBlendKernel:Class;
//Create the Shader object
private var shader:Shader = new Shader( new GrainBlendKernel() );
private var shaderFilter:ShaderFilter = new ShaderFilter(shader);
private var myBrown:BitmapData;
private var myPerlin:BitmapData;
private function init():void
{
myPerlin = new BitmapData(200, 200, false, 0x00CCCCCC);
myBrown = new BitmapData(200, 200, false, 0x00663300);
//Set the slider values based on the parameter metadata
turbulence.minimum = shader.data.turbulence.minValue;
turbulence.maximum = shader.data.turbulence.maxValue;
turbulence.value = shader.data.turbulence.defaultValue;
turbulence.addEventListener( SliderEvent.CHANGE, updateFilter );
myPerlin.perlinNoise(100, 80, 6, Math.floor(Math.random() * 10), false, true, 7, true, null);
//Set the displayed images to the perlinNoise
perlinNoise.source = myGrain.source =myPerlin;
//Set the background image to Brown
backGround.source = myBrown;
shader.data.background.input = myBrown;
myGrain.filters = [shaderFilter];
}
private function updateFilter( event:Event ):void
{
shader.data.turbulence.value = [turbulence.value];
myGrain.filters = [shaderFilter];
}
]]>
</fx:Script>
<s:VGroup width="100%">
<s:HGroup width="100%" height="100%" horizontalAlign="center" verticalAlign="top">
<s:VGroup>
<s:BitmapImage id="backGround" width="200" height="200"/>
<s:Label text="Background" textAlign="center" width="200"/>
</s:VGroup>
<s:VGroup>
<s:BitmapImage id="perlinNoise" width="200" height="200"/>
<s:Label text="Perlin noise" width="200" textAlign="center"/>
</s:VGroup>
<s:VGroup>
<s:BitmapImage id="myGrain" width="200" height="200" />
<s:Label text="Grain blend" width="200" textAlign="center"/>
</s:VGroup>
</s:HGroup>
<s:HGroup width="100%" horizontalAlign="center" verticalAlign="top">
<s:Label text="{turbulence.value}"/>
<s:HSlider id="turbulence" width="200"/>
</s:HGroup>
</s:VGroup>
</s:Application>`
I'm using code for the open source MP3 player Howler and trying to port it to a Spark MobileApplication type. I'm getting a null pointer exception and I have no idea what's causing it. I've tried debugging extensively with breakpoints at what I think is causing the error, and set breakpoints in the untouched Howler project but all the variables in scope seem to be identical between my non-working project, and the Howler project. The only thing I can think of is that Howler uses MX components and I am using spark. I've pasted all my code below (which is very long) but I've bolded the lines that are throwing the error. The error occurs immediately after I choose a folder in the browse folder dialog.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Home"
xmlns:Comp="components.*" xmlns:display="flash.display.*">
<s:Button id="browse" x="546" y="43" label="Open Directory" click="browseForFolder()"/>
<s:DataGrid id="dgPlaylist" width="82" height="141" itemRenderer="components.DurationFormatter">
</s:DataGrid>
<s:Button id="btnForward" x="187" y="126" label="Forward"/>
<s:Button id="btnPause" x="90" y="39" label="Pause"/>
<s:Button id="btnBack" x="55" y="166" label="Back" click="changeSoundIndex(-1)"/>
<s:Button id="btnPlay" x="336" y="199" label="Button"/>
<s:Button id="btnStop" x="366" y="89" label="Stop"/>
<s:VScrollBar id="sldrPosition" x="280" y="43" mouseDown="thumbTimer.stop()"
mouseUp="thumbTimer.start()"
/>
<s:VScrollBar id="sldrVolume" x="265" y="234" change="ChangeSoundTransform()"
/>
<s:RichText id="txtID3" x="236" y="41" width="99">
</s:RichText>
<fx:Declarations>
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.ericcarlisle.PlayList;
import com.ericcarlisle.PlayModes;
import com.ericcarlisle.Utility;
import flash.desktop.NativeDragManager;
import flash.media.Sound;
import mx.core.UIComponent;
import mx.events.CloseEvent;
import mx.events.DragEvent;
import org.osmf.traits.PlayTrait;
import spark.components.DataGrid;
// Player properties
private var playMode:String = "STOP";
private var volume:uint;
private const panning:int = 0;
private var selectedFileCount:uint;
private var loadedFileCount:uint;
private var soundIndex:uint;
// Player objects
private var SoundObj:Sound;
private var Channel:SoundChannel;
private var Transform:SoundTransform;
private var thumbTimer:Timer;
private var PlayList:com.ericcarlisle.PlayList;
private var SoundFilter:FileFilter = new FileFilter("Sounds", "*.mp3;*.wav");
//private var PlaylistFilter:FileFilter = new FileFilter("Sounds", "*.pls;*.m3u");
// Visualization objects.
private var Spectrum:ByteArray;
private const VISUALIZER_HEIGHT:Number = 50;
private const VISUALIZER_COLOR:Number = 0x336699;
// ID3 and other metadata
private var ID3:ID3Info;
private var Duration:int;
/*---------- PLAYER INITIALIZER ----------*/
// Initialization function used to add event handlers and set initial settings.
private function init():void
{
// Set player initial settings.
playMode = PlayModes.STOP;
selectedFileCount = 0;
loadedFileCount = 0;
soundIndex = 0;
// Set initial application height.
//this.height= cvsControlBar.height + cvsPlayer.height;
// Set volume.
volume = sldrVolume.value;
// Instantiate sound objects.
Channel = new SoundChannel();
Transform = new SoundTransform(volume/100, panning);
PlayList = new com.ericcarlisle.PlayList();
// Bind playlist data to datagrid.
dgPlaylist.dataProvider = PlayList.Sounds;
// Create a timer to control the song position hslider.
thumbTimer = new Timer(500);
thumbTimer.addEventListener(TimerEvent.TIMER, onTimerTick);
// Create event handlers for application.
this.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onPlayerDragInto);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onPlayerDropInto);
this.addEventListener(InvokeEvent.INVOKE, onInvoke);
}
/*---------- DRAG/DROP & FILE MANAGEMENT ----------*/
private function onInvoke(event:InvokeEvent):void
{
if (event.arguments.length > 0)
{
var file:File;
var files:Array = new Array();
for (var i:int = 0; i < event.arguments.length; i++)
{
file = new File(event.arguments[i]);
files.push(file);
}
if (PlayList.Sounds.length > 0) removeAllSounds();
loadFiles(files);
}
}
// Handles file selection event dispatched by browse dialog.
private function onFileSelect(event:FileListEvent):void
{
loadFiles(event.files);
}
// Handles folder selection event dispatched by browse dialog.
private function onDirectorySelect(event:Event):void
{
var directory:File = event.target as File;
**loadFiles(directory.getDirectoryListing());**
}
// Loads a batch of files into the playlist.
private function loadFiles(files:Array):void
{
var file:File;
// Count the number of files selected. Only accept files with .mp3 extension.
selectedFileCount = 0;
for (var i:uint = 0; i < files.length; i++)
{
file = files[i];
if (file.extension == "mp3") selectedFileCount++;
}
// Reset the count on files currently loaded.
loadedFileCount = 0;
**// Set the player mode so that loaded files are played automatically.
if (PlayList.Sounds.length == 0) playMode = PlayModes.LOADTOPLAY;**
// Load files as sound objects.
for(var j:uint = 0; j < files.length; j++)
{
file = files[j];
if (file.extension == "mp3" || file.extension == "wav")
{
var sound:Sound = new Sound(new URLRequest(file.url));
sound.addEventListener(Event.ID3,onID3);
}
}
}
// Presents file browse (multiple file) dialog.
private function browseForFiles():void
{
var SoundFile:File = new File();
SoundFile.browseForOpenMultiple("Open", [SoundFilter]);//, PlaylistFilter]);
SoundFile.addEventListener(FileListEvent.SELECT_MULTIPLE, onFileSelect);
}
// Presents file browse (folder) dialog.
private function browseForFolder():void
{
var directory:File = File.documentsDirectory;
directory.browseForDirectory("Select Directory");
directory.addEventListener(Event.SELECT, onDirectorySelect);
}
// Accept files dragged into player.
private function onPlayerDragInto(event:Event):void
{
NativeDragManager.acceptDragDrop(this);
}
// Manages files dropped into player.
private function onPlayerDropInto(event:NativeDragEvent):void
{
// Accept only files.
if (event.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
{
// Parse dragged contents into array of files.
var dragFiles:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
// Load the files.
loadFiles(dragFiles);
}
}
/*---------- SOUND MANAGEMENT ----------*/
private function loadSound():void
{
SoundObj = new Sound();
SoundObj.load(new URLRequest(PlayList.Sounds[soundIndex]["url"]));
SoundObj.addEventListener(Event.COMPLETE, onSoundLoaded);
}
private function onSoundLoaded(event:Event):void
{
// Retrieve data for current sound from playlist.
var soundData:Object = PlayList.Sounds[soundIndex];
// Place ID3 information into the readout panel.
//txtID3.htmlText = Utility.ReadoutHTML(soundData["title"], soundData["track"], soundData["album"], soundData["artist"], soundData["year"], soundData["duration"]);
// Configure the holizontal slider to act as a playhead.
sldrPosition.maximum = soundData["duration"];
// Set the selected row in the playlist display.
dgPlaylist.selectedIndex = soundIndex;
// Start the player if the mode is correct.
if (playMode == PlayModes.LOADTOPLAY)
{
playSound();
}
else
{
playMode = PlayModes.LOADED;
}
}
// Plays the current sound.
public function playSound():void
{
// Load sound into channel.
Channel.stop();
Channel = SoundObj.play(sldrPosition.value,0,Transform);
playMode = PlayModes.PLAY;
// Start position timer.
thumbTimer.start();
// Configure UI controls.
btnPlay.visible = false;
btnPause.visible = true;
sldrPosition.enabled = true;
btnPlay.enabled = true;
btnStop.enabled = true;
setBackForwardButtons();
Channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
}
private function setBackForwardButtons():void
{
if (soundIndex == PlayList.Sounds.length-1 || PlayList.Sounds.length == 0)
{
btnForward.enabled = false;
}
else
{
btnForward.enabled = true;
}
if (soundIndex == 0 || PlayList.Sounds.length == 0)
{
btnBack.enabled = false;
}
else
{
btnBack.enabled = true;
}
}
// Stops the current sound.
public function stopSound():void
{
Channel.stop();
thumbTimer.stop();
sldrPosition.value = 0;
playMode = PlayModes.STOP;
//Visualizer.graphics.clear();
btnPlay.visible = true;
btnPause.visible = false;
}
// Pause a playing sound.
private function pauseSound():void
{
Channel.stop();
thumbTimer.stop();
btnPlay.visible = true;
btnPause.visible = false;
}
// Change the sound index
private function changeSoundIndex(delta:int):void
{
stopSound();
playMode = PlayModes.LOADTOPLAY;
soundIndex = soundIndex + delta;
loadSound();
}
// Change the volume and panning via the sound transform object.
private function ChangeSoundTransform():void
{
volume = Math.round(sldrVolume.value);
Channel.soundTransform = new SoundTransform(volume/100, panning);
}
// Handles event for sound completing.
private function onSoundComplete(event:Event):void
{
stopSound();
soundIndex++;
if (soundIndex < PlayList.Sounds.length)
{
playMode = PlayModes.LOADTOPLAY;
loadSound();
}
}
// Load ID3 information into local variables.
// Update the readout panel.
// Configure position slider.
private function onID3(event:Event):void
{
var sound:Sound = Sound(event.target);
ID3 = ID3Info(sound.id3);
Duration = Math.floor(sound.length);
// Load sound id3 data into the playlist.
PlayList.AddSound(ID3.songName, ID3.album, ID3.artist, ID3.track, ID3.year, ID3.genre, Duration, sound.url);
// Increment the loaded file count.
loadedFileCount++;
if (loadedFileCount == selectedFileCount * 2)
{
// Refresh the playlist so that new results will be visually displayed.
PlayList.Sounds.refresh();
// Set the count properties.
selectedFileCount = 0;
loadedFileCount = 0;
soundIndex = 0;
if (playMode == PlayModes.LOADTOPLAY) loadSound();
}
}
/*---------- VISUALIZATION ----------*/
private function UpdateVisualizer():void
{
// Instantiate a new byte array to contain spectrum data.
Spectrum = new ByteArray();
// Clear the visualizer graphics.
//Visualizer.graphics.clear();
// Dump the spectrum data into the byte array.
SoundMixer.computeSpectrum(Spectrum,false,0);
var f:Number;
var i:int;
var ave:int;
//Visualizer.graphics.lineStyle(1, VISUALIZER_COLOR,1);
//Visualizer.graphics.beginFill(VISUALIZER_COLOR, 0.75);
for (i = 0; i < 512; i=i+10)
{
f = Spectrum.readFloat();
//Visualizer.drawRoundRect(Math.floor(i*0.7) + 7, cvsReadout.height - 10, 4, -Math.abs(f) * (cvsReadout.height-10));
}
//Visualizer.graphics.endFill();
}
// Updates the position of the hslider thumb.
private function onTimerTick(event:TimerEvent):void
{
sldrPosition.value = Math.round(Channel.position);
}
// Update the wave visualizer if the sound is playing.
private function onEnterFrame(event:Event):void
{
if (playMode == PlayModes.PLAY)
{
UpdateVisualizer();
}
}
// Show application information.
private function showHowlerInfo():void
{
//cvsAbout.visible = true;
}
private function togglePlayList():void
{
}
private function onItemDoubleClick(event:Event):void
{
this.playMode = PlayModes.LOADTOPLAY;
thumbTimer.stop();
sldrPosition.value = 0;
loadSound();
}
/*---------- ERROR HANDLING ----------*/
// Handles IO errors.
private function onIOError(event:IOErrorEvent):void
{
//Alert.show("File load error: " + event.text);
}
private function startMove():void
{
stage.nativeWindow.startMove();
}
private function unloadSound():void
{
stopSound();
txtID3.text = "";
btnPlay.visible = true;
btnPause.visible = false;
btnPlay.enabled = false;
btnStop.enabled = false;
}
private function removeSound():void
{
var index:int = dgPlaylist.selectedIndex;
if (index >= 0)
{
if (index == soundIndex)
{
unloadSound();
}
PlayList.RemoveSoundAt(index);
PlayList.Sounds.refresh();
setBackForwardButtons();
}
}
private function removeAllSounds():void
{
unloadSound();
PlayList.Sounds.removeAll();
PlayList.Sounds.refresh();
setBackForwardButtons();
}
private function onKeyDown(event:KeyboardEvent):void
{
if (event.charCode.toString() == "8" || event.charCode.toString() == "127")
{
removeSound();
}
}
]]>
</fx:Script>
</s:View>
I'm new to Flex so I don't know if this is causing the problem or not, but the Howler app uses an MX:DataGrid defined like this:
<mx:DataGrid x="6"
y="7"
width="388"
height="310"
id="dgPlaylist"
keyDown="onKeyDown(event)"
dragMoveEnabled="true"
doubleClickEnabled="true"
dragEnabled="true"
dropEnabled="true"
dragComplete="onPlaylistDragDrop(event)"
itemDoubleClick="onItemDoubleClick(event)">
<mx:columns>
<mx:DataGridColumn width="168" headerText="Title" dataField="title" />
<mx:DataGridColumn width="160" headerText="Album" dataField="album"/>
<mx:DataGridColumn width="60" headerText="Duration" dataField="duration" textAlign="right" itemRenderer="components.DurationFormatter"/>
</mx:columns>
</mx:DataGrid>
It has additional columns that I'm not using. Could this be the cause?
We are going to need the error message. Generally what you are experiencing is called a null pointer exception ( just like the error said ). Which basically means you tried to access a property of an object that is null. For example, running the following code will cause the same problem
var arrayCollection:ArrayCollection;
arrayCollection.addItem( new Object() );
To fix your error, you should find the object that the error is referring to in your code and make sure it isn't null when your program executes whatever line it is complaining about, or add an if-loop in the event it is null.
I would like to allow the user of my XULRunner based app to be able to do copy/paste via a context menu.
Keyboard shortcuts Ctrl-C and Ctrl-V are already working fine
Here it is without flash. The getInputSelection function is from here: Is there an Internet Explorer approved substitute for selectionStart and selectionEnd?.
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="mywin" title="my app"
width="800" height="600" persist="screenX screenY width height sizemode"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<popupset>
<menupopup id="clipmenu">
<menuitem label="Copy" oncommand="copy()"/>
<menuitem label="Paste" oncommand="paste();"/>
</menupopup>
</popupset>
<browser
type="content-primary"
src="http://127.0.0.1/"
flex="1"
disablehistory="true"
id="browserId"
context="clipmenu"
/>
<script>
<![CDATA[
function copy()
{
var tabBrowser = document.getElementById("browserId");
var selectedTagName = tabBrowser.contentWindow.document.activeElement.tagName;
var windowObj;
if(selectedTagName == "IFRAME")
{
windowObj = tabBrowser.contentWindow.frames[tabBrowser.contentWindow.document.activeElement.name];
}
else
{
windowObj = document.getElementById("browserId").contentWindow;
}
var selectedText = windowObj.getSelection();
if(!selectedText || selectedText == "")
{
var focused = windowObj.document.activeElement;
if(focused && focused.value)
{
selectedText = getSelectionFromInput(focused);
}
}
//alert(selectedText + "---");
const clipHelper = Components.classes["#mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
clipHelper.copyString(selectedText);
}
function getSelectionFromInput(focused)
{
var focusedValue = focused.value;
var sel = getInputSelection(focused);
var selectedText = "";
if(focusedValue.length == (sel.end))
{
selectedText = focusedValue.substring(sel.start);
}
else
{
selectedText = focusedValue.substring(sel.start, (sel.end));
}
return selectedText;
}
function paste()
{
var clip = Components.classes["#mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
var trans = Components.classes["#mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
trans.addDataFlavor("text/unicode");
clip.getData(trans, clip.kGlobalClipboard);
var str = new Object();
var len = new Object();
trans.getTransferData("text/unicode",str,len);
str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
str = str.data.substring(0, len.value / 2);
var focused = document.commandDispatcher.focusedElement;
var focusedValue = focused.value;
var sel = getInputSelection(focused);
focused.value = focusedValue.substring(0,sel.start) + str + focusedValue.substring(sel.end);
}
function getInputSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
]]>
</script>
</window>
Updated to support iframes.
Create menu using the code below.
<popupset>
<menupopup id="clipmenu">
<menuitem label="Copy" oncommand="copy();"/>
<menuseparator/>
<menuitem label="paste" oncommand="paste();"/>
</menupopup>
</popupset>
<browser type="content" src="chrome://myapp/content/theme1/index.html" flex="1" context="clipmenu"/>
Connect it to whatever you want, here am making it the context menu of the browser element by giving the id of menu in the context attribute of the browser element.
Then for each menu you can give the command to execute in oncommand event. We have given the function copy and paste.
Then write the code to copy text from whatever element you want or if you want to copy the selected data only.
See the below link for copying command.
http://www.deluxeblogtips.com/2010/06/javascript-copy-to-clipboard.html
One more example for "Copy" context menu in XULRunner under SWT Browser (code was implemented for Eclipse v3.5.1, XULRunner v1.8.1.3):
Browser browser = new Browser(parent, style | SWT.MOZILLA);
Menu menu = new Menu(browser);
MenuItem item = new MenuItem(menu, SWT.NONE);
item.setText("Copy");
item.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent e) {
nsIWebBrowser webBrowser = (nsIWebBrowser) browser.getWebBrowser();
nsIInterfaceRequestor req = (nsIInterfaceRequestor) webBrowser.queryInterface(nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID);
nsIDocShell docShell = (nsIDocShell) req.getInterface(nsIDocShell.NS_IDOCSHELL_IID);
nsIClipboardCommands cmds = (nsIClipboardCommands) docShell.queryInterface(nsIClipboardCommands.NS_ICLIPBOARDCOMMANDS_IID);
cmds.copySelection();
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
browser.setMenu(menu);