Help - Search - Members - Calendar
Full Version: WSH Panel Mod
Hydrogenaudio Forums > Hosted Forums > foobar2000 > 3rd Party Plugins - (fb2k)
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9
T.P Wang
Current Version: 1.1.11

MAIN FEATURES
Scripting
Restrict "Language" to JScript and VBScript to prevent unexpected behavior.
Add popup menu, blur, timer and more operations to images.
Add support to run main menu and context menu command.

Editor Window
Add support to pseudo transparent, syntax highlighting for JScript and VBScript, Import/Outport scripts, customizable font style and miscellaneous UI improvements.

Screenshot:

Shortcuts:
  • Ctrl+F: Find
  • Ctrl+H: Replace
  • Ctrl+G: Goto
  • Ctrl+S: Apply
NOTES & HINTS
  1. If anything goes wrong, check the console first.
  2. gdi.Font() is fully changed: gdi.Font(name, pxSize, style);
  3. Don't call repaint functions such as window.Repaint() in callback function on_size() {}, especially in pseudo transparent mode.
  4. Don't try to assign parameter "gr" from function on_paint(gr) {} with other value, nor try to store "gr" in another variable.
  5. CollectGarbage() is useful in JScript when you want to free memory immediately. (See #12)
  6. It's better to retrieve window width and height in callback function on_size(), it's not necessary to retrieve them during startup.
  7. It's not recommended to create objects in callback function on_paint(), since it's called frenquently.
  8. Since v1.1.0, functions /methods which use IFbFileInfo Interface is replaced with IFbMetadbHandle Inferface.
  9. Do NOT update tags in global callbacks, see Callbacks.txt for details.
  10. jsbeautifier.org is a good site for formatting your JScript files.
  11. Don't forget that error handling (JScript, VBScript) helps to create more stable scripts.
  12. Consider using Dispose() method instead of CollectGarbage() function.
  13. DO NOT try to call playback control methods such as fb.Play(), fb.Next() and fb.RunMainMenuCommand("Stop"), or you will make foobar2000 crash.

CHANGELOG
CODE

v1.1.11
- ADD: window.SetCursor() method.
- CHG: Improvements for ApplyMask.
- CHG: Enhanced editor (autocompletion and calltips).
- FIX: Stack overflow in property editor.
- FIX: Fix crashes while trying to import invalid properties setting.
- FIX: Fix crashes due to GDI+ bug.

v1.1.10
- ADD: GdiAlphaBlend() method to IGdiGraphics interface, see Interfaces.txt for more details.
- ADD: Dispose() method to various classes, so now avoid using CollectGarbage(), see Interfaces.txt for more details.
- ADD: Reize() method to IGdiBitmap interface.
- ADD: ApplyMask() method to IGdiBitmap interface.
- ADD: utils.GetAlbumArtAsync() method and on_get_album_art_done() callback for getting album art asynchronously.
- ADD: window.ID() property, for now only used in utils.GetAlbumArtAsync().
- ADD: window.GetBackgroundImage() and on_refresh_background_done() callback to retrieve pseudo transparent bakground image.
- ADD: MetaRemoveField(), MetaAdd() and MetaInsertValue() methods for IFbFileInfo interface.
- ADD: on_item_played() callback.
- ADD: Edge style.
- CHG: UpdateFileInfoSimple() method of IFbMetadbHandle interface now supports multivalue field, see Interfaces.txt for more details.
- CHG: Set timeout to 0 now means disabling script hangs check.
- CHG: Updated Samples.
- FIX: Misc bugs and crashes fixed.

v1.1.9
- ADD: Re-added ability to check script hangs with customizable timeout.
- ADD: Add "safe mode" in order to prevent scripts using unsafe COM/ActiveX Objects.
- FIX: Possible crashes while editing scripts and other cleanups.

v1.1.8
- ADD: utils.PathWildcardMatch(pattern, str).
- ADD: IContextMenuManager.InitContext(metadb) method.
- ADD: New Callbacks: on_mouse_lbtn_dblclk, - ADD: New Callbacks: on_mouse_mbtn_down, on_mouse_mbtn_up, on_mouse_mbtn_dblclk, on_mouse_lbtn_dblclk and on_mouse_rbtn_dblclk.
- DEL: No check for script hangs any more.
- CHG: Change implementation of pseudo transparent background.
- CHG: Methods related to gdi+ objects creation won't throw errors anymore, only return NULL if failed.
- FIX: UpdateFileInfo() and UpdateFileInfoSimple() now throw an error if it's not executed in main thread.
- FIX: IMenuObject.ID property was broken.
- FIX: Crash when invalid index passed into InfoName()/InfoValue()/MetaName()/MetaValue().
- FIX: Rare crashes and minor cleanups.

v1.1.7
- ADD: Check for script hangs.
- ADD: window.GetProperty() and window.SetProperty().
- ADD: window.ShowConfigure() and window.ShowProperties();
- ADD: fb.IsMetadbInMediaLibrary() method.
- ADD: utils.IsKeyPressed() method.
- ADD: utils.GetAlbumArtEmbedded() method.
- CHG: utils.GetAlbumArt() now support artist image (See flags.txt).
- CHG: Gain default config value on panel creation, for panel stack splitter and dockable panels.
- FIX: Crash while passing a invalid value to IFbFileInfo.InfoValue() or IFbFileInfo.MetaValue().
- FIX: A rare crash when closing foobar2000 and other cleanups.

v1.1.6
- ADD: Add method SetMaxWidth(width) to IFbTooltip interface, note that this should be called at least once to enbale multiline or long string support.
- FIX: No length restriction for tooltip text now.
- FIX: Fix a crash when try to apply script in editor window whose corresponding panel is closed (Thanks Peter).

v1.1.5
- ADD: Add two new methods: MetaFind(name) and InfoFind(name) to IFbFileInfo interface.
- ADD: Add DrawEllipse(), FillEllipse(), DrawRoundRect() and FillRoundRect() to IGdiGraphics interface.
- DEL: Remove broken method Rotate() from IGdiBitmap interface.
- ADD: window.CreateTooltip().
- ADD: IFbTooltip interface, check Interfaces.txt for details.
- CHG: Change behavior of shortcuts in editor.

v1.1.4
- CHG: fb.trace() now accept variable number of arguments, and all arguments are converted to string automatically, e.g: fb.trace("Numbers:", 1, 2, 3) will output "Numbers 1 2 3".
- ADD: New "Time" property to "IFbProfiler" interface.
- ADD: Two new methods: utils.ReadINI() and utils.WriteINI()
- ADD: In the preferences window of editor properties, add serveral new styles customizable.
- FIX: Some UI glitches about syntax highlighting after editing styles.
- FIX: Some fixes to prevent potential crash.

v1.1.3
- ADD: New methods: window.WatchMetadb(metadb) and window.UnWatchMetadb(), in order to watch metadb changes.
- ADD: New callback function on_metadb_change(metadb, fromhook) is added in cooperation with two new methods above.
- ADD: New sample: "Watch Metadb Change (Base)".
- CHG: Names of editor properties are changes, and add serveral new styles customizable.
- ADD: Import / Export support for editor properties.
- ADD: New "Apply" button in Preferences, WSH Panel Mod, in order to apply editor properties immediately.
- FIX: Minor UI fixes/improvements to editor window.
- FIX: Misc fixes/changes in source code.

v1.1.2
- FIX: A crash while using utils.GetAlbumArt() if there are not album art present.
- CHG: IFbTitleFormat.Eval() is now IFbTitleFormat.Eval(force = false) (Backward compatible).
- ADD: fb.RunContextCommandWithMetadb(metadb);
- ADD: Much improvements about editor window, you now can set style (font, size, fore/bakground color, etc) in foobar2000 Preferences, WSH Panel Mod. Note these setting will take effect after reopening editor window.
- CHG: Content of popup error message is more friendly.

v1.1.1
- FIX: fb.ShowConsole() doesn't work due to wrong GUID in fb2k SDK.
- FIX: fb.RunContextCommand() is broken since v1.1.0.
- FIX: Memory leak in IGdiBitmap.CreateRawBitmap().
- ADD: New properties: fb.CursorFollowPlayback and fb.PlaybackFollowCursor.
- ADD: New "NotifyOthers" property to "window" interface.
- ADD: New callbacks: on_playlist_stop_after_current_changed(state), on_cursor_follow_playback_changed(state), on_playback_follow_cursor_changed(state) and on_notify_data(name, info).
- FIX: "on_item_focus_change" is suddenly wrong documented as "on_items_focus_change".

v1.1.0
- ADD: IFbMetadbHandle, refer to Interface.txt for more information.
- CHG: (ATTENTION) fb.GetNowPlaying(), fb.GetFocusItem() now return IFbMetadbHandle, instead of IFbFileInfo.
- CHG: (ATTENTION) on_playback_new_track(metadb) call back now use IFbMetadbHandle as parameter, instead of IFbFileInfo.
- ADD: Method MetaSet() to IFbFileInfo interface.
- ADD: Ability to update file info: IFbMetadbHandle.UpdateFileInfo() and IFbMetadbHandle.UpdateFileInfoSimple().
- ADD: New interface utils: utils.CheckComponent(), utils.CheckFont() and utils.GetAlbumArt().
- ADD: IFbTitleFormat.EvalWithMetadb().
- FIX: fb.RunMainMenuCommand() handles menu display string which contains '/' character correctly.
- DEL: Remove EvalPlaylist() from "fb" interface.
- CHG: Eval() will execute some base functions/variables even fb2k is not playing or playlist is empty.
- CHG: Callback "on_items_selection_change" is now "on_items_focus_change".
- ADD: New samples: "GetAlbumArt" and "SimpleFollowCursor".
- FIX: Some problems may cause memory leak.

v1.0.4
- ADD: Two new callbacks: on_mouse_rbtn_down(x, y, vkey) and on_mouse_rbtn_up(x, y, vkey).
- ADD: "ShowPopupMessage" method to "fb" interface.

v1.0.3
- FIX: Crash while using fb.RunMainMenuCommand().

v1.0.2
- ADD: "StopAfterCurrent" property to "fb" interface.
- FIX: Minor bug fixes to configuration window.

v1.0.1
- First public release.


DOWNLOAD
Binaries, Samples, References and Source:
LINK (GoogleCode)
Falstaff
Thanx for this T.P cool.gif
tedgo
Thank you very much smile.gif
Spirit_of_the_ocean
Sorry for asking: But I can't imagine what the changes do. Maybe some can explain or give examples ?
chiwou
works great biggrin.gif and a thank you button would do great in this forum ^^
tedgo
@Spirit of the ocean
The most conspicious changes are in usability.
For instance:
- Import and Export buttons instead the need of copy and paste codes
- Pseudo Transparent mode
- different colours in the code box for better overview

and of course the change in the gdi.Font() setting
instead of
var g_font = gdi.Font(-12, weight_normal, italic, uline_no, "Tahoma");

you now need to write
var g_font = gdi.Font("Tahoma",12,2);

(as "2" means "italic" according to the flags.txt included in the "Callbacks Flags and Interfaces" archive)
TomBarlow
Awesome! I had a go at modding WSH panel last summer (semi successfully), I trust your development skills far more than my own though, is there any chance you could add a stop after current property of the fb object? So you can get and set the stop after current state? Cursor follows playback/playback follows cursor would be awesome too.

Thanks!!
Andreasvb
I tried this version instead of the old one, this one works much better for me.

I use Curacao by Falstaff, but the WSH panel won't display the volume knob or the seek bar picture.
Couldn't find a way to change the code in the old, the configure-button is greyed and there's no where to change it, had to use Window Enabler for that.
This mod enables that button, thank you.

I had some minor problem with the code for the seek bar in this new version: txt = g_titlefmt.Eval();
It doesn't like that part somehow, removed it and it works great.

So now I can use it like it's supposed to be. =)
chiwou
// problem solved wink.gif
tedgo
Sure you can.
If you have a seekbar sample from the original WSH Panel you can use it in the mod too.
You only need to change the gdi.Font setting as described above.

All my old samples are working in this mod too.
chiwou
ahh to late, no that wasn't the prob, But Andreas workaround works great biggrin.gif
tedgo
Hm, the "txt = g_titlefmt.Eval();" part works great here once the gdi.Font setting is changed...
I tried it with the seekbar_sample provided with the original WSH Panel.
Andreasvb
Thanks, works now. smile.gif
T.P Wang
@TomBarlow:
I'll add StopAfterCurrent property later in the next build.

@Andreasvb:
If error occured, wsh panel mod will send error message to the console, so check the console first is a better choice.
Andreasvb
Thanks.
Good to know!
T.P Wang
1.0.2 Released, with source. smile.gif
TomBarlow
Thank you!! This is great. At last, a decent stop after current toggle! smile.gif Would it be possible to have a callback for it as well?

Another thing, I notice the PBOButtons example uses images that not everyone is going to have (I don't), which makes it look like it doesn't work (although the button still changes playback order). Could you make them text buttons?
grounder
when used "pseudo transparent".
small flashing when changing size panel and change track.

great work smile.gif

saivert
Here is my foopaint app updated for use with WSH Panel Mod:

CODE
//--------
var ForReading = 1, ForWriting = 2;

// Flags, used with GdiDrawText()
// For more information, see: http://msdn.microsoft.com/en-us/library/dd162498(VS.85).aspx
DT_TOP = 0x00000000;
DT_LEFT = 0x00000000;
DT_CENTER = 0x00000001;
DT_RIGHT = 0x00000002;
DT_VCENTER = 0x00000004;
DT_BOTTOM = 0x00000008;
DT_WORDBREAK = 0x00000010;
DT_SINGLELINE = 0x00000020;
DT_EXPANDTABS = 0x00000040;
DT_TABSTOP = 0x00000080;
DT_NOCLIP = 0x00000100;
DT_EXTERNALLEADING = 0x00000200;
DT_CALCRECT = 0x00000400;
DT_NOPREFIX = 0x00000800;
DT_INTERNAL = 0x00001000;
DT_EDITCONTROL = 0x00002000;
DT_PATH_ELLIPSIS = 0x00004000;
DT_END_ELLIPSIS = 0x00008000;
DT_MODIFYSTRING = 0x00010000;
DT_RTLREADING = 0x00020000;
DT_WORD_ELLIPSIS = 0x00040000;
DT_NOFULLWIDTHCHARBREAK = 0x00080000;
DT_HIDEPREFIX = 0x00100000;
DT_PREFIXONLY = 0x00200000;

// Flags, used by Menu
var MF_SEPARATOR = 0x00000800;
var MF_ENABLED = 0x00000000;
var MF_GRAYED = 0x00000001;
var MF_DISABLED = 0x00000002;
var MF_UNCHECKED = 0x00000000;
var MF_CHECKED = 0x00000008;
var MF_STRING = 0x00000000;
var MF_POPUP = 0x00000010;
var MF_RIGHTJUSTIFY = 0x00004000;

// This is helper function, used in DrawString()/MeasureString()
// args: h_align, v_align, trimming, flags
function StringFormat() {
var h_align = 0, v_align = 0, trimming = 0, flags = 0;
switch (arguments.length)
{
// fall-thru
case 4:
flags = arguments[3];
case 3:
trimming = arguments[2];
case 2:
v_align = arguments[1];
case 1:
h_align = arguments[0];
break;
default:
return 0;
}
return ((h_align << 28) | (v_align << 24) | (trimming << 20) | flags);
}

// h_align/v_align:
// http://msdn.microsoft.com/en-us/library/ms534177(VS.85).aspx
StringAlignment = {
Near: 0,
Center: 1,
Far: 2
};

// trimming:
// http://msdn.microsoft.com/en-us/library/ms534403(VS.85).aspx
StringTrimming = {
None: 0,
Character: 1,
Word: 2,
EllipsisCharacter: 3,
EllipsisWord: 4,
EllipsisPath: 5
};

// flags, can be combined of:
// http://msdn.microsoft.com/en-us/library/ms534181(VS.85).aspx
StringFormatFlags = {
DirectionRightToLeft: 0x00000001,
DirectionVertical: 0x00000002,
NoFitBlackBox: 0x00000004,
DisplayFormatControl: 0x00000020,
NoFontFallback: 0x00000400,
MeasureTrailingSpaces: 0x00000800,
NoWrap: 0x00001000,
LineLimit: 0x00002000,
NoClip: 0x00004000
};

//--------
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
function RGBA(r,g,b,a){ return ((a<<24)|(r<<16)|(g<<8)|(b)); }
//--------
function TimeFmt(t){
var zpad = function(n){
var str = n.toString();
return (str.length<2) ? "0"+str : str;
}
var h = Math.floor(t/3600); t-=h*3600;
var m = Math.floor(t/60); t-=m*60;
var s = Math.floor(t);
if(h>0) return h.toString()+":"+zpad(m)+":"+zpad(s);
return m.toString()+":"+zpad(s);
}
//----------------------------------------------------------------------------



var g_font = gdi.Font("Segoe UI", 13, 0);
//var g_titlefmt = fb.TitleFormat("%bitrate%");

var g_ctx = fb.CreateContextMenuManager();

var g_datafile = "c:\\users\\saivert\\documents\\fb2k_drawing_data.txt";
var g_drag = 0;
var g_pos = new Array();
var g_shift = {x:0, y:0};
var g_size = 5;
var g_key = null;
var g_increment = 10;
var g_bkgnd = 0;
var g_fgnd = 0;
var g_showall = false;
var g_fgnd_colors = new Array(RGB(255,10,0),
RGB(35,20,80),
RGB(90,100,10),
RGB(56,230,67)
);
function shadowtext(gr,txt,f,c,x,y,w,h,fmt){
gr.GdiDrawText(txt, f, RGB(10,10,10), x-1, y-1, w, h,fmt);
gr.GdiDrawText(txt, f, c, x, y, w, h,fmt);
}


function on_paint(gr){
var start = (new Date).getTime();

var ww = window.Width-1;
var wh = window.Height-1;
var ox = g_shift.x;
var oy = g_shift.y;

switch (g_bkgnd) {
case 0:
gr.FillSolidRect(0, 0, ww, wh, RGB(0,0,0));
break;
case 1:
gr.FillGradRect(0, 0, ww, wh, 45, RGB(0,0,0), RGB(255,255,255));
break;
case 2:
gr.FillGradRect(0, 0, ww, wh, 60, RGB(30,90,10), RGB(200,180,90));
break;

}

var img_to_blur = gdi.CreateImage(ww, wh);
var g = img_to_blur.GetGraphics();

var count = (g_showall) ? g_pos.length : fb.PlaybackTime * (g_pos.length / fb.PlaybackLength);

try {
for (var i = 1; i < count; i++) {
if (g_pos[i-1].size==0) {continue;}
g.DrawLine(ox+g_pos[i-1].x, oy+g_pos[i-1].y, ox+g_pos[i].x, oy+g_pos[i].y, g_pos[i].size, g_pos[i].color);
}
} catch(Err) {
fb.trace(Err.message);
};

img_to_blur.ReleaseGraphics(g);
// Make box blur, radius = 2, iteration = 2
img_to_blur.BoxBlur(2, 2);

img_to_blur && gr.DrawImage(img_to_blur, 0, 0, ww, wh, 0, 0, ww, wh);


var diff = (new Date).getTime() - start;

txt = "Number of points: " + g_pos.length + "; Size: " + g_size + "; Key=" + g_key + "; Time=" + diff;
if (g_showall) txt += " [showall]";
shadowtext(gr,txt, g_font, RGB(255,255,255), 2, 0, ww, wh, DT_SINGLELINE|DT_LEFT|DT_TOP|DT_NOCLIP);

gr.FillSolidRect(5, 18, g_size, 10, g_fgnd_colors[g_fgnd]);

var help_text = "Keys: s=save, r=load, k=change bkgnd, l=change pen color, scrollwheel=change pen size, ";
shadowtext(gr,help_text, g_font, RGB(255,255,255), 2, 0, ww, wh, DT_SINGLELINE|DT_LEFT|DT_BOTTOM|DT_NOCLIP);

gr.DrawRect(ww-21,1, 20, 20, 1.0, RGB(255,0,255));

gr.DrawRect(0,0, ww, wh, 1.0, RGB(150,150,150));
}

function showmenu(){
var menu = window.CreatePopupMenu();
var colormenu = window.CreatePopupMenu();
colormenu.AppendMenuItem(MF_STRING, 3, 'Reset canvas');
menu.AppendMenuItem(MF_STRING, 1, 'Reset canvas');
menu.AppendMenuItem(MF_STRING|MF_POPUP, colormenu.id, 'Color');
g_ctx.InitNowPlaying();
g_ctx.BuildMenu(colormenu, 3, -1);

if (arguments.length==2) {
var x = arguments[0];
var y = arguments[1];
} else {
var x = window.width - 10;
var y = 20;
}
var id = menu.TrackPopupMenu(x,y);
if (id==1) {
g_pos = []; g_shift = {x:0, y:0};
} else if (id>0) g_ctx.ExecuteByID(id-3);
}

function on_size(){
}
function on_focus(focused){
}
function on_key_down(key){
g_key = key;
switch (key) {
case 66: g_pos = []; g_shift = {x:0, y:0}; break;
case 37: g_shift.x-=g_increment; break;
case 39: g_shift.x+=g_increment; break;
case 38: g_shift.y-=g_increment; break;
case 40: g_shift.y+=g_increment; break;
case 75: if (g_bkgnd++ > 1) g_bkgnd = 0; break;
case 76: if (++g_fgnd > g_fgnd_colors.length-1) g_fgnd = 0; break;
case 83: savetofile(); break;
case 82: loadfromfile(); break;
case 8: while (g_pos.length>0 && g_pos.pop().size>0); break;
case 70: g_showall = !g_showall; break;
case 93: showmenu(); break;
}
window.Repaint();
}

function on_mouse_lbtn_down(x,y){
g_drag = 1;
}
function on_mouse_lbtn_up(x,y){
//on_mouse_move(x,y);
g_pos.push({x: x-g_shift.x, y: y-g_shift.y, size: 0});
if(g_drag) g_drag = 0;
if (x > window.Width-20 && y < 20) showmenu(x,y);
}
function on_mouse_move(xpos,ypos){
if(g_drag){
g_pos.push({x: xpos-g_shift.x, y: ypos-g_shift.y, size: g_size, color: g_fgnd_colors[g_fgnd]});
window.Repaint();
}
}
function on_mouse_wheel(delta){
g_size += delta;
if (g_size <=1) g_size = 1;
window.Repaint();
}

function on_playback_starting(cmd, paused){
}
function on_playback_new_track(info){
}
function on_playback_stop(){
}
function on_playback_seek(time){
}
function on_playback_pause(state){
}
function on_playback_edited(){
}
function on_playback_dynamic_info(){
}
function on_playback_dynamic_info_track(){
}
function on_playback_time(time){
window.Repaint();
}
function on_volume_change(val){
}

function savetofile() {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var a = fso.CreateTextFile(g_datafile, true);
a.WriteLine(JSON.stringify(g_pos));
a.Close();
}

function loadfromfile() {
var fso = new ActiveXObject("Scripting.FileSystemObject");
try {
var a = fso.OpenTextFile(g_datafile, ForReading);
g_pos = JSON.parse(a.ReadAll());
a.Close();
}
catch(Err)
{
};
}

if(!this.JSON){JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}})();


loadfromfile();


I'm currently triggering this on a left button click in a special area.
Where is a event for right button clicks? or even to replace the standard context menu (with the Configure... item).
T.P Wang
1.0.4 Released

@saivert:
You can use 1.0.4, the "on_mouse_rbtn_up(x, y, vkey)" callback function is what you need, and don't forget to return true in that function if you want to override the standard context menu. happy.gif
Hed1n
It would be very nice if you add support for older versions of foobar2000, if possible!
I'm using version 0.9.4.5 + Columns UI and I can not make it work with foo_uie_wsh_panel...
Thanks!
d0ng
I was wondering if you are able to release a version with out the SSE2, would that be possible?
T.P Wang
@d0ng:
No SSE2 in this release.
NEMO7538
How can I apply a title formatting to the focused item (not the currently playing item) ?
[Note that it could be feasible if there was a way to retrieve the index in the playlist of the currently selected item]
Thanks
NEMO7538
Nevertheless i've made those two toolbars to replace foobar's standard ones :
Volume :
CODE

function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var g_font = gdi.Font("Tahoma", 12, 0);
var g_drag = 0;
var ww = 100;
var hofset = 5;
var wh = 12;
function on_paint(gr){
var vofset = (window.Height-wh)/2;
var grad = Math.pow((100+fb.Volume)/100,2);
var i = 0;
while (i < ww+1) {
var col = (grad<=0) ? RGB(168,168,168) : (i<=grad*ww) ? RGB(32+128*i/ww,164*(1-i/ww),32*(1-i/ww)) : RGB(168,168,168);
gr.FillSolidRect(hofset+1+i,vofset,4,wh, col);
i = i +5;
}
gr.DrawRect(hofset,vofset, i, wh, 1.0, RGB(192,192,192));
}
function on_mouse_lbtn_down(x,y){
g_drag = 1;
}
function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag = 0;
}
function on_mouse_move(x,y){
if(g_drag){
var v = (x-hofset)/ww;
v = (v<0) ? 0 : (v<1) ? v : 1;
v = 100 * (Math.pow(v,1/2) - 1);
fb.Volume = v;
}
}
function on_mouse_wheel(delta){
if(delta>0)
fb.VolumeUp();
else
fb.VolumeDown();
}
function on_volume_change(val){
window.Repaint();
}
function on_playback_time(time){
window.Repaint();
}
//EOF



Seekbar:
CODE

function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var g_font = gdi.Font("Tahoma", 12, 0);
var g_drag = 0;
var ww = 270;
var hofset = 10;
var wh = 12;
function on_paint(gr){
var vofset = (window.Height-wh)/2;
var length = fb.PlaybackLength;
var grad = 0;
if (length > 0) {
grad = fb.PlaybackTime/fb.PlaybackLength;}

var i = 0;
while (i < ww+1) {

var col = (grad<=0) ? RGB(168,168,168) : (i<=grad*ww) ? RGB(0,32+48*(1-i/ww),96+64*(1-i/ww)) : RGB(128+48*(1-i/ww),128+48*(1-i/ww),128+48*(1-i/ww));
gr.FillSolidRect(hofset+1+i,vofset,4,wh, col);
i = i +5;
}
gr.DrawRect(hofset,vofset, i, wh, 1.0, RGB(192,192,192));
}
function on_mouse_lbtn_down(x,y){
g_drag = 1;
}
function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag = 0;
}
function on_mouse_move(x,y){
if(g_drag){
var v = (x-hofset)/ww;
v = (v<0) ? 0 : (v<1) ? v : 1;
fb.PlaybackTime = fb.PlaybackLength * v;



}

}
function on_mouse_wheel(delta){
if(delta>0)
fb.PlaybackTime = fb.PlaybackTime + delta;
else
fb.PlaybackTime = fb.PlaybackTime + delta;
}
function on_playback_new_track(info){
window.Repaint();
}
function on_playback_stop(){
window.Repaint();
}
function on_playback_seek(time){
window.Repaint();
}
function on_playback_time(time){
window.Repaint();
}

chiwou
@NEMO: thanks for the templates, they are working pretty good biggrin.gif

//edit
actually there are not biggrin.gif every time I close/start foobar I've to re-apply them, because they don't show up on start
but I use the panel width with window.Width
weird biggrin.gif
T.P Wang
Ver 1.1.0 Released, with some major changes.
Now implementing a "follow cursor" file info is now possible.

NOTICE
If you are using any of fb.GetNowPlaying(), fb.GetFocusItem(), on_playback_new_track() and on_items_selection_change(), please pay more attention to the ChangeLog.txt, Interfaces.txt and Callbacks.txt, because some features are not back backwards compatible.
NEMO7538
Thanks for the new version.
However, it seems that a callback would be needed: on_stopaftercurrent_changed (state) {} so that we can use the property properly.
TomBarlow
Is it possible to run a service using foo_run with this? I tried it but couldn't get it to work. Could it be possible in the future?

The only other thing missing for me is a fixed width font in the editor! pinch.gif and maybe on_stopaftercurrent_changed (state) {}.
T.P Wang
@NEMO7538:
Thanks for your advice, I'll add them in the next version.

@TomBarlow:
The editor window use "Courier New" font (which is monospaced font), It should be installed with XP by default...
T.P Wang
1.1.1 released

@TomBarlow:
fb.RunContextCommand() is fixed, so you can run them now...
TomBarlow
Fantastic!! This version is great! I still can't get the run services things to run, is there a particular syntax? I'm using e.g. fb.RunContextCommand('Run services//Last.fm') , although I don't really know if that's right. For some reason they're not working as a regular CUI toolbar button either, maybe the problem with with foo_run?

And I definitely have Courier New installed, I can't think why it's not showing up. It's not too much of a problem.
T.P Wang
@TomBarlow:
I tried it and it works, however, it will not be executed if there are no now playing content.
for exmaple:
fb.RunContextCommand("Run service/Google Artist");

And please contact me, I'll send you a debug version to find out why your font not work.
EDIT: I find out the problem, I'll fix that later.
NEMO7538
fb.RunContextCommand() only works with the nowplaying track. Is there a possibility to have it applied on the getfocusitem ?
Thanks
T.P Wang
1.1.2 Released smile.gif

@TomBarlow:
You now can change font is foobar2000 Preferences, WSH Panel Mod

@NEMO7538
You can use fb.RunContextCommandWithMetadb(metadb) from now on.
T.P Wang
For setting in Preferences->Display->WSH Panel Mod, Please read the following description.
This message is quoted from SciTE Doc
QUOTE
The value of each setting is a set of ',' separated fields, some of which have a subvalue after a ':'.
The fields are font, size, fore, back, italics, notitalics, bold, notbold, eolfilled, noteolfilled, underlined, notunderlined, and case. The font field has a subvalue which is the name of the font, the fore and back have colour subvalues, the size field has a numeric size subvalue, the case field has a subvalue of 'm', 'u', or 'l' for mixed, upper or lower case, and the bold, italics and eolfilled fields have no subvalue. The value "fore:#FF0000,font:Courier,size:14" represents 14 point, red Courier text.


Note that all fields is case sensitive.
Black_Over_Bills_Mothers
I've never programmed this panel before so please excuse my ignorance.

I've seen several examples of how this panel can be programmed to be many things but could I use it to contain other panels. My thinking is a few buttons used to select which panel to display. I know I could PSS for this but that is a big step from 'normal' CUI.
TomBarlow
Thanks for the new version, fonts work well now. Could you explain what RunContextCommandWithMetadb does exactly, and how to use it, and how it's different from RunContextCommand? Thanks.
T.P Wang
@Black_Over_Bills_Mothers:
Sorry, you cannot embed any other panels in WSH Panel.

@TomBarlow:
For example,
CODE
// Get current focus item handle
metadb = fb.GetFocusItem();
// Applying RunContextCommand to this handle
fb.RunContextCommandWithMetadb(metadb);


and something more:
fb.RunContextCommandWithMetadb(fb.GetNowPlaying()) is equivalent to fb.RunContextCommand(), since fb.RunContextCommand() apply on now playing item handle.
NEMO7538
Thank you TP Wang for the new version.

For those who are interested, I made a little toolbar to handle the rating :
CODE
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }


var g_drag = 0;
var bool;
var g_metadb;
var imgname;
var rating;
var nrating;
var lrating;
var img;
var hofset=20;
var imgw=20;
var g_tfo = fb.TitleFormat("%rating%");

on_item_focus_change();

function on_paint(gr){
// var info = g_metadb.GetFileInfo();
if (g_metadb) {
for (i = 1; i < 6; i++) {
img = gdi.image(fb.FoobarPath + "Images\\"
+ ((i > (g_drag ? lrating : rating)) ? "NoStar" : "Star" + (g_drag ? "-hover" : "")) + ".png");
// + ((i > (g_drag ? lrating : rating)) ? "No" : "") +"Star" + (g_drag ? ((rating != lrating) ? "-hover" : "") : "") + ".png");
// + ((i > (g_drag ? lrating : rating)) ? "No" : (g_drag ? ((rating != lrating) ? "Red" : "") : "")) +"Star.png");
// + (g_drag ? ((rating != lrating) ? "Blue" : "") : "") + ((i > (g_drag ? lrating : rating)) ? "No" : "") +"Star.png");
gr.DrawImage(img, hofset+imgw*(i-1), 4, 20, 16, 0, 0, 20, 16);
}
}
}

function on_mouse_wheel(delta){}


function on_mouse_lbtn_up(x,y){
// fb.trace("button up");
if (lrating !=rating) {if (g_metadb) {
bool = fb.RunContextCommandWithMetadb("Rating/"+((lrating==0) ? "<not set>" : lrating),g_metadb);
}}
}

function on_mouse_move(x, y) {
if (g_metadb) {
g_drag = 1;
nrating = Math.ceil((x-hofset)/imgw);
if (nrating > 5) nrating = 5;
if (nrating != lrating) {
lrating = nrating;
window.Repaint();
}
}
}
function on_mouse_leave() {
on_metadb_changed()
}

function on_item_focus_change() {
// fb.trace("itm focus changed");
if (g_metadb) {window.UnwatchMetadb();}
g_metadb = fb.GetFocusItem();
if (g_metadb) {
on_metadb_changed();
window.WatchMetadb(g_metadb);
}

}
function on_metadb_changed() {
g_drag = 0;
// fb.trace("changed");
rating = g_tfo.EvalWithMetadb(g_metadb);
if (rating == "?") {rating = 0;}
lrating = rating;
window.Repaint();
}

function on_playback_new_track(metadb) {on_item_focus_change();}
//EOF



For it to work:
  • you need the playback statistics standard component, with "Rating"as an entry in your context menu
  • you need to place "Star.png", "NoStar.png" and "Star-hover.png" in your foobar/Images directory. Sample:

@T.P. Wang: Since we have a metadb handle, it would be nice (if possible) to have a callback when metadb data is changed. Thanks.
tedgo
@NEMO7538
Thanks for this sample! smile.gif
I'm on replacing most settings of PSS in my config with WSH Panel mod, but i'm unfortunately too dumb to do it right... biggrin.gif
Now i finally have a sample i can work with.
chiwou
@NEMO: could you please change your seekbar/volume config so that it use the window.Width/Height of the wsh panel *please*

and thanks for the rating buttons smile.gif
NEMO7538
This is another one for "Stop after current"
CODE
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var g_drag = 0;
var imgname;
var img;
function on_init(bool) {
imgname =bool ? "sac.png" : "playing.png";
img = gdi.image(fb.FoobarPath + "Images\\" + imgname);
window.Repaint();
};

on_init(fb.StopAfterCurrent);

function on_paint(gr){

gr.DrawImage(img, 0, 4, 20, 16, 0, 0, 20, 16);
}

function on_mouse_move(x, y) {

if (g_drag==0) {

on_init(!fb.StopAfterCurrent);
g_drag = 1;
}

}
function on_mouse_leave() {
if (g_drag==1) {

on_init(fb.StopAfterCurrent);
g_drag = 0;
}
}
function on_mouse_lbtn_up(x,y){

fb.StopAfterCurrent = !fb.StopAfterCurrent;
window.Repaint();

// g_drag = 0;
}


function on_playlist_stop_after_current_changed(state) {on_init(fb.StopAfterCurrent);}

//EOF
Where you need to use those two images: (In Foobar/Images directory)

.. and yet another one for the playback order :
CODE
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var PlaybackOrder = {
Default: 0,
RepeatPlaylist: 1,
RepeatTrack: 2,
Random: 3,
ShuffleTracks: 4,
ShuffleAlbums: 5,
ShuffleFolders: 6
}


var PlaybackOrderText = new Array(
"Default", // = 0
"Repeat (Playlist)",
"Repeat (Track)",
"Random",
"Shuffle (tracks)",
"Shuffle (albums)",
"Shuffle (folders)") ;

var g_font = gdi.Font("Arial", 11, 1);
var g_drag = 0;
var txt = "";
var bool;
function on_init() {
on_playback_order_changed(fb.PlaybackOrder);
};

on_init();
function on_paint(gr){
gr.SetTextRenderingHint(0);
gr.SetSmoothingMode(2);
// gr.FillGradRect( 3, 3, 0, window.Height-3, 90, RGB(240,240,240), RGB(100,230,100));
gr.DrawRect(15, 1, window.Width-36, 20, 1, RGB(64,64,64));
gr.FillGradRect(16, 2, window.Width-38, 18, 70, RGB(240,240,240), RGB(190,190,190));
gr.DrawString(txt, g_font, RGB(124,128,164), 5, 0, window.Width-15, 22, 0x11005000);

}

function on_mouse_wheel(delta){
if(delta>0) {if (fb.PlaybackOrder==6) fb.PlaybackOrder=0;
else fb.PlaybackOrder= fb.PlaybackOrder+1;}

else
{if (fb.PlaybackOrder==0) fb.PlaybackOrder=6;
else fb.PlaybackOrder= fb.PlaybackOrder-1;}
}
function on_mouse_lbtn_up(x,y){

if (fb.PlaybackOrder==0) fb.PlaybackOrder=4;
else fb.PlaybackOrder= 0;

// fb.trace("order="+fb.PlaybackOrder)

g_drag = 0;
}
function on_playback_order_changed(new_order_index) {
txt = PlaybackOrderText[fb.PlaybackOrder];

window.Repaint();
}
//EOF
Without images ... just click or use the mouse wheel.
T.P Wang
@NEMO7538:
Thanks for your advice, I now considering to include that feature in the next version.
However, I think l should restrict it to watch only one metadb handle at a time.
NEMO7538
QUOTE (chiwou @ Apr 4 2009, 18:29) *
@NEMO: could you please change your seekbar/volume config so that it use the window.Width/Height of the wsh panel

Here it is :
Volume bar:
CODE
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var g_font = gdi.Font("Tahoma", 12, 0);
var g_drag = 0;
var ww;
var hofset = 10;
var wh = 12;
var vofset;
var grad;

function on_paint(gr){
vofset = (window.Height-wh)/2;
grad = Math.pow((100+fb.Volume)/100,2);
ww = 5*Math.floor((window.Width - 2*hofset)/5);
var i = 0;
while (i < ww) {
var col = (grad<=0) ? RGB(168,168,168) : (i<=grad*ww) ? RGB(32+128*i/ww,164*(1-i/ww),32*(1-i/ww)) : RGB(168,168,168);
gr.FillSolidRect(hofset+1+i,vofset,4,wh, col);
i = i +5;
}
gr.DrawRect(hofset,vofset, i, wh, 1.0, RGB(192,192,192));
}
function on_mouse_lbtn_down(x,y){
g_drag = 1;
}
function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag = 0;
}
function on_mouse_move(x,y){
if(g_drag){
var v = (x-hofset)/ww;
v = (v<0) ? 0 : (v<1) ? v : 1;
v = 100 * (Math.pow(v,1/2) - 1);
fb.Volume = v;
}
}
function on_mouse_wheel(delta){
if(delta>0)
fb.VolumeUp();
else
fb.VolumeDown();
}
function on_volume_change(val){
window.Repaint();
}
function on_playback_time(time){
window.Repaint();
}
//EOF

Seekbar
CODE
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
var g_font = gdi.Font("Tahoma", 12, 0);
var g_drag = 0;
var hofset = 10;
var wh = 13;
var vofset;
var length;
var grad;


function on_paint(gr){
vofset = (window.Height-wh)/2;
ww = 5*Math.floor((window.Width - 2*hofset)/5);
// fb.trace("ww="+ww);
length = fb.PlaybackLength;
grad = 0;
if (length > 0) {
grad = fb.PlaybackTime/fb.PlaybackLength;}

var i = 0;
while (i < ww) {

var col = (grad<=0) ? RGB(168,168,168) : (i<=grad*ww) ? RGB(0,32+48*(1-i/ww),96+64*(1-i/ww)) : RGB(128+48*(1-i/ww),128+48*(1-i/ww),128+48*(1-i/ww));
gr.FillSolidRect(hofset+1+i,vofset,4,wh, col);
i = i +5;
}
gr.DrawRect(hofset,vofset, i, wh, 1.0, RGB(192,192,192));
}
function on_mouse_lbtn_down(x,y){
g_drag = 1;
}
function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag = 0;
}
function on_mouse_move(x,y){
if(g_drag){
var v = (x-hofset)/ww;
v = (v<0) ? 0 : (v<1) ? v : 1;
fb.PlaybackTime = fb.PlaybackLength * v;



}

}
function on_mouse_wheel(delta){
if(delta>0)
fb.PlaybackTime = fb.PlaybackTime + delta;
else
fb.PlaybackTime = fb.PlaybackTime + delta;
}
function on_playback_new_track(info){
window.Repaint();
}
function on_playback_stop(){
window.Repaint();
}
function on_playback_seek(time){
window.Repaint();
}
function on_playback_time(time){
window.Repaint();
}

You need to adjust wh manually, however;
chiwou
yeah I know biggrin.gif but especially for the seekbar it would be nice if it use the window.width (for resizing etc...)

but thanks for the answer ^^
NEMO7538
QUOTE (T.P Wang @ Apr 4 2009, 18:43) *
I now considering to include that feature in the next version.
However, I think l should restrict it to watch only one metadb handle at a time.
Yes, I understand it may cause performance issues. My request was for the rating panel to be refreshed when rating is set by another mean (the real context menu item, for instance).
T.P Wang
v1.1.3 Released.

Notes:
  • Previous editor properties will not transfer to this version, so make a backup of your editor properties, and edit them again in the Preferences->Display->WSH Panel Mod.
  • Some important notes is added, please check Callbacks.txt for details.

@NEMO7538:
WSH Panel now can get notified when metadb is changed wink.gif
NEMO7538
Thanks ... it works well now. I have updated the rating toolbar coded above. There is still a little annoyance with the fact that we don't get notified when the playlist has changed, so the stars does not exactly match the track which is selected in the new playlist ... but thanks anyway.

I have posted the toolbars and images here just in case someone wants to get all of them.
Please note that images are to be placed in Foobar/Images directory.
fbuser
QUOTE (chiwou @ Apr 4 2009, 18:53) *
yeah I know biggrin.gif but especially for the seekbar it would be nice if it use the window.width (for resizing etc...)

but thanks for the answer ^^
I didn't test it for this specific Seekbar, but it works for my Seekbar which had the same problem. You need to add the following code to make it dependent of the window.width:
CODE
on_size=function() {
  ww=window.Width;
}

This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2009 Invision Power Services, Inc.