Shoutbox

[?] Detecting when a Plus! Window is closed - Printable Version

-Shoutbox (https://shoutbox.menthix.net)
+-- Forum: MsgHelp Archive (/forumdisplay.php?fid=58)
+--- Forum: Messenger Plus! for Live Messenger (/forumdisplay.php?fid=4)
+---- Forum: Scripting (/forumdisplay.php?fid=39)
+----- Thread: [?] Detecting when a Plus! Window is closed (/showthread.php?tid=92567)

[?] Detecting when a Plus! Window is closed by ryxdp on 10-13-2009 at 08:36 AM

Basically, it's all in the title, but with a twist: I can't (?) use OnWindowidEvent_Destroyed as I need to get the WindowId from a ComboBox containing a list of other windows; this is fine and I can get that easily. What I need to do is somehow get the WindowId from that list and check whether it has been closed based on that WindowId. So, is it possible?


RE: [?] Detecting when a Plus! Window is closed by matty on 10-13-2009 at 01:17 PM

Store the WindowIds in an object and on Destroy delete them and when you need to check if they are open iterate through the object and see if they exist.

Javascript code:
var objWindows = {};
 
function OpenWindow(Name, XML, ShowCode){
    if (typeof objWindows[Name] === 'object' && typeof objWindows[Name].Handle === 'number' && Interop.Call('user32', 'IsWindow', objWindows[Name].Handle) {
        Interop.Call('user32', 'SetForegroundWindow', objWindows[Name].Handle);
        return objWindows[Name];
    }
    if (typeof(ShowCode) === 'undefined') ShowCode = 0;
   
    objWindows[Name] = MsgPlus.CreateWnd(XML, Name, ShowCode);
    return objWindows[Name];
}
 
function CheckIfWindowExists ( Name ) {
    for ( objWindow in objWindows ) {
        if ( objWindow === Name ) return true;
    }
    return false;
}
 
function OnWindowIdEvent_Destroyed(pPlusWnd) {
    delete objWindows[pPlusWnd.WindowId];
}


Something along those lines ;)
RE: [?] Detecting when a Plus! Window is closed by ryxdp on 10-14-2009 at 03:10 AM

Doesn't appear to be working; _debug is undefined and I've never heard of getfuncname anyway. I can easily replace the _win32 calls with the usual Interop.Call, but I've got no idea what the _debug one is for.

Pretty much what I need is a way of getting an OnWindowidEvent_Destroyed without knowing what the WindowId actually is, as it can't be hardcoded into the function name.


RE: [?] Detecting when a Plus! Window is closed by Matti on 10-14-2009 at 12:08 PM

It seems that matty copied that code snippet from Screenshot Sender. The line with _debug can simply be removed, and the _win32 can be replaced by Interop.Call.

However the code could still use some optimization. There's no need for a for-loop to check whether the window exists - you can use "[key] in [enumerable]" as an expression as well:

Javascript code:
var objWindows = {};
 
function OpenWindow(Name, XML, ShowCode){
    if (typeof(objWindows[Name]) === 'object' && typeof(objWindows[Name].Handle) === 'number'
         && Interop.Call('user32', 'IsWindow', objWindows[Name].Handle)) {
        Interop.Call('user32', 'SetForegroundWindow', objWindows[Name].Handle);
        return false;
    }
    if (typeof(ShowCode) === 'undefined') ShowCode = 0;
 
    objWindows[Name] = MsgPlus.CreateWnd(XML, Name, ShowCode);
    return objWindows[Name];
}
 
function CheckIfWindowExists ( Name ) {
    return ( Name in objWindows && typeof(objWindows[Name]) === 'object');
}
 
function OnWindowIdEvent_Destroyed(pPlusWnd) {
    delete objWindows[pPlusWnd.WindowId];
}


quote:
Originally posted by ryxdp
Pretty much what I need is a way of calling OnWindowidEvent_Destroyed without knowing what the WindowId actually is, as it can't be hardcoded into the function name.
Do you want to destroy the window and thus triggering the Destroyed event or do you only want to call the OnWindowIdEvent_Destroyed function without actually destroying the window?

In the first case, you can simply check whether it already exists (using CheckIfWindowExists above) and then use PlusWnd.Close().

In the second case, I'd suggest you to move the contents of the event function to some other place. Calling a function in the global scope by its function name in JScript can only be done using eval() and as you know, eval is evil and should be avoided as much as possible. Here's a possible solution: store the events in a global object indexed by the window's name and the event name, then retrieve the right function from the object and call it whenever needed.
Javascript code:
// Store the event handlers here
var objEvents = {
    'WndMain' : {
        'Destroyed' : function(PlusWnd, ExitCode) {
            // Code goes here
        }
    },
    'WndAbout' : {
        'Destroyed' : function(PlusWnd, ExitCode) {
            // More code here
        }
    }
};
 
// Define the "real" event handlers
function OnWndMainEvent_Destroyed(PlusWnd, ExitCode) {
    return objEvents.WndMain.Destroyed(PlusWnd, ExitCode);
}
 
function OnWndAboutEvent_Destroyed(PlusWnd, ExitCode) {
    return objEvents.WndAbout.Destroyed(PlusWnd, ExitCode);
}
 
// Now you can call the event handlers from anywhere using:
var sWindow = PlusWnd.WindowId;
objEvents[sWindow].Destroyed(PlusWnd, 0);
// You can even get the event name itself dynamically!
var sWindow = PlusWnd.WindowId;
var sEvent = ( foo === bar ? "CtrlClicked" : "Destroyed" );
objEvents[sWindow][sEvent](PlusWnd, 0);

It would be much easier if Plus! were be using a more flexible, possibly OOP way of assigning and triggering event handlers though...
RE: RE: [?] Detecting when a Plus! Window is closed by ryxdp on 10-14-2009 at 10:46 PM

quote:
Originally posted by Matti
Do you want to destroy the window and thus triggering the Destroyed event or do you only want to call the OnWindowIdEvent_Destroyed function without actually destroying the window?

It seems to be getting a bit too confusing even for me, so I'll make an example :P

Say we have a window. This window has, among other things, a button and a combo box. When the button is pressed, it creates another window using a specified XML file and the WindowId from the combo box. I want to know when that window is closed by use of the OnWindowidEvent_Destroyed event, but obviously I can't unless it's possible to do something similar to this:

JScript code:
function On+GetComboText()+Event_Destroyed(oPlusWnd){
//Change some text and stuff here
}


RE: [?] Detecting when a Plus! Window is closed by Matti on 10-16-2009 at 03:33 PM

Well, the easiest thing would be to create all possible Destroyed events and those can then check whether they have been created by that main window and then do something.

Here's an idea:

Javascript code:
// Stores the created window
var oCreatedWnd;
// Array of window IDs in the combo box
var oWndIds = [ "WndOne", "WndTwo", "WndThree" ];
 
// Creates the new window, pass the main window as parameter
function CreateTheWindow(PlusWnd) {
    var nWndId = PlusWnd.Combo_Gombo_GetCurSel("CmbWindow");
    if( nWndId < 0 ) return;
    var sWndId = oWndIds[nWndId];
 
    oCreatedWnd = MsgPlus.CreateWnd("Interfaces.xml", sWndId);
}
 
// Destroyed event for WndOne
function OnWndOneEvent_Destroyed(PlusWnd, ExitCode) {
    if( PlusWnd === oCreatedWnd ) {
        // Change some text and stuff here
    }
}
 
// Repeat for WndTwo, WndThree,...


RE: RE: [?] Detecting when a Plus! Window is closed by CookieRevised on 10-16-2009 at 05:47 PM

quote:
Originally posted by Matti

...or do you only want to call the OnWindowIdEvent_Destroyed function without actually destroying the window?

...I'd suggest you to move the contents of the event function to some other place. Calling a function in the global scope by its function name in JScript can only be done using eval() and as you know, eval is evil and should be avoided as much as possible. Here's a possible solution:

[CODE SNIPPED]
In this situation, what is the benefit of your code opposed to a simple:
JScript code:
function OnWndMainEvent_Destroyed(PlusWnd, ExitCode) {
    // some code
}
 
function RandomFunction() {
    // some code
    // Call the OnWndMainEvent_Destroyed() event in code.
    OnWndMainEvent_Destroyed(pPlusWnd, 0)
    // some code
}

???

According to your explaination you can't do the above. But I don't see why you can't, it works perfectly and is done all the time in many scripts (and doesn't use eval() at all).

-----------------------------------------------------------------------------------------------


quote:
Originally posted by Matti
Well, the easiest thing would be to create all possible Destroyed events and those can then check whether they have been created by that main window and then do something.
I think his point is that he does not know the Window IDs at all at the time of writing the code. Aka: the Window IDs can be anything at any point. He only knows the Windows IDs when the combobox is created and populated. So he obvisouly can not hard code a gazzilion Destroyed() events.

What would solve everything is a OnWnd_Destroyed(PlusWnd, ExitCode) which will trigger when _any_ plus window is destroyed.

Otherwise I'm afraid you need to look into subclassing in one way or the other.

On the other hand, if you only want to check if the windows still exist in a certain known event (like on the press of a button), you could simply also store the Window Handles of those windows (thus together with the Plus! Window IDs to show to the user) and use a Windows API to check if those windows still exist by passing the window handle. So the question also becomes: When do you want to check if those windows are still there? What's the purpose of checking if they still exist?
RE: RE: RE: [?] Detecting when a Plus! Window is closed by ryxdp on 10-16-2009 at 11:13 PM

quote:
Originally posted by CookieRevised
What would solve everything is a OnWnd_Destroyed(PlusWnd, ExitCode) which will trigger when _any_ plus window is destroyed.

Yeah, this is pretty much exactly what I would have liked. If that scripting wishlist is still going this should be added to it, if it's not already up there :P

Anyway, I was thinking about using RegisterMessageNotification, which would hook the WM_CLOSE message into the created window. Would this be plausible in any way?
RE: [?] Detecting when a Plus! Window is closed by CookieRevised on 10-16-2009 at 11:50 PM

quote:
Originally posted by ryxdp
Anyway, I was thinking about using RegisterMessageNotification, which would hook the WM_CLOSE message into the created window. Would this be plausible in any way?
Same problem, you would need to hard code the Window ID since the format is:

OnWindowidEvent_MessageNotification(
    [object] PlusWnd,
    [number] Message,
    [number] wParam,
    [number] lParam
);

RE: RE: [?] Detecting when a Plus! Window is closed by ryxdp on 10-17-2009 at 12:06 AM

quote:
Originally posted by CookieRevised
Same problem, you would need to hard code the Window ID since the format is:

OnWindowidEvent_MessageNotification(
    [object] PlusWnd,
    [number] Message,
    [number] wParam,
    [number] lParam
);


Oh yeah, I hadn't looked at the documentation yet :P
RE: [?] Detecting when a Plus! Window is closed by Matti on 10-17-2009 at 08:55 AM

Well, there is a way to create a new event handler function in run-time, but it's really nasty coding. I've been experimenting with this some time ago, but it's just plain ugly. Nevertheless, if it can help you or others, here it is:

Javascript code:
function CreateTheWindow( PlusWnd ) {
    var sWndId;
    // Code to retrieve the selected window ID goes here
 
    // Assign a new event in the global scope
    // Unfortunately, eval() is needed for this
    eval( "On"+sWndId+"Event_Destroyed = OnCreatedWindowEvent_Destroyed" );
    // Now the registered events have to be reloaded
    // We'll be loading an empty JScript file to trigger the reloading
    MsgPlus.LoadScriptFile("blank.js");
    // Now that everything is set, create the window
    var oWnd = MsgPlus.CreateWnd( "Interfaces.xml", sWndId );
}
 
// We'll make the new event handlers call this function
function OnCreatedWindowEvent_Destroyed( PlusWnd, ExitCode ) {
    // Code goes here
}

I've tried many things already, but this is the best that I could get.
  • In a JScript environment, global variables cannot be assigned by a variable name through normal code. In JavaScript on the contrary, there is the window object which controls all globally defined variables, allowing developers to use window['name'] to define a new global. The same thing goes for many other languages such as PHP, where you can use $$varname to create variables by a variable name. Unfortunately all of this can't be done in Plus! scripting without eval()...
  • The created global function won't be registered as event handler right away either. In order for Plus! to re-register the global functions, another script file has to be loaded into the script using MsgPlus::LoadScriptFile.
So yes, in order to achieve this in a better way we'll need some changes in Plus! scripting itself. Here are my suggestions:
  • Event handlers for all script windows (see CookieRevised's reply to [?] Detecting when a Plus! Window is closed).
  • An OOP approach for event handlers:
    Javascript code:
    // DEMONSTRATION CODE
    // This does not actually work at the time of writing!
    var fEvent = function( PlusWnd, ExitCode ) {
        // Code goes here
    }
    PlusWnd.AddEvent( 'Destroyed', fEvent );

    This would not only allow new events to be created in run-time, but also add more than one handler to a single event. This would make it so much easier to manage the code: it would make it possible to put all code for one feature in separate script files instead of having to mix all code for the handlers in one event.
What do you think? :P