Menus Enable/Disable (Built-in Menus)

Participant ,
Mar 07, 2018 Mar 07, 2018

Copy link to clipboard

Copied

Hi,

     We can invoke the menus using MenuActions.  How can i disable/enable InDesign's built-in menus using indesign javascript?

TOPICS
Scripting

Views

3.6K

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Guide , Mar 19, 2018 Mar 19, 2018

(…) Can we disable (stop) the process of only export PDF from Export menu?

I think so. The ImportExportEvent object has a format property that should be useful to filter file format.

In the callback function (disableProcess), you could add a condition of the form:

if( ev.properties.format == … ) etc

@+

Marc

Likes

Translate

Translate
Community Expert ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Hi Sudha,

just ask yourself how you would accomplish that with a user interface action?

That's under Edit/Menus… where you can restrict the view on a menu.

Never tried to access this by scripting.

Another way is to add an event listener to the menu that listens at the beforeDisplay event and do something—maybe invoking something else?—to prevent that menu from executing. Just a vague idea…

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

I'd use Laubender's suggestion with the event listener. I have a startup script that prevents a user from being able to print or export a PDF unless the preflight panel uses a specific preflight profile and has zero issues. The script listens for an Export or Print request and then calls the checkJob function to perform the rest of the script:

var  beforeExport = app.menuActions.itemByName ( "$ID/Export..." ).addEventListener ( "beforeInvoke", checkJob ); 

var  beforePrint = app.menuActions.itemByName ( "$ID/Print..." ).addEventListener ( "beforeInvoke", checkJob );            

The checkJob function runs just like any other function, but note the "preventDefault()" and "stopPropagation" portions:

function checkJob(myEvent){   

//other stuff goes here to make the script do what I want
myEvent.preventDefault(); 

myEvent.stopPropagation(); 

exit(0);}

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Hi Colin,

yes. And there is another thing what can be done with menus. One could use merhod remove() on them.
But then the problem is to add them to the application again without doing a new install of InDesign.
I would never try that on purpose… Just wanted to add this option to the repertoire of harsh actions 😉

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

G'day Uwe

I agree, I would not use the remove() method either... that's a rabbit-hole that is NOT worth exploring! I like the event listener option because it's easy to remove a startup script if it's causing too much grief, whereas restoring removed menu items (as I understand it) would be done through either a reinstall or trashing preferences, two things I dislike doing.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Hi,

     Thank you for your replies...

     I want to do disable Export Menu. Using event listener we can do some process. but how to disable that menu like Export and print?

     Can u pls suggest any links or documents to learn startup scripts..

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Sudha K

Perhaps have a look at my video that features the startup script in question: Episode 19: Preflight overview and "enforcer" scripts - YouTube

If you want the startup script, please contact me via my website that is linked in the Youtube video. The script is free but I'm trying to gauge interest in the script at this stage.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Hi,

     Ok... I could not able to look that video due to connectivity.

     Can u pls suggest any links or discussion...

     I can get the code for invoke and do further coding but don't know how to disable specific menus..

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 08, 2018 Mar 08, 2018

Copy link to clipboard

Copied

Hi,

     I am trying the startup script using the below code.  But its throwing the attached warning.

error.png

#target indesign

#targetengine 'onOpen'

(function() {

  app.eventListeners.add("afterOpen", afterOpen);

  function afterOpen(myEvent) {   

        var doc = app.activeDocument;

        app.menuActions.itemByName ( "$ID/Export..." ).enabled = false;

        alert(doc.name);       

   }

}())

     Is it possible to disable indesign's menu item "Export" and "Print"?

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 09, 2018 Mar 09, 2018

Copy link to clipboard

Copied

Hi Sudha,

you forgot one function: If you want to disable "Export" you have also to disable the Packaging feature where an export to IDML and PDF is possible.

A solution:

You can add an event listener to the beforeExport and beforePrint events.

Search the forum, there are some examples waiting for you.

Regarding packaging see into this threads here:

Get Packaged Folder Path [JS]

Stop Packaging based on file path [JS]

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 09, 2018 Mar 09, 2018

Copy link to clipboard

Copied

Hi,

     Thank you so much....

     I have used the below code... Place the script into startup script folder. If i click the menu Export and Package, nothing is happen. So i think its working. My doubt is..

1. The below code is stop the process of "Export" functionality. Its not showing the menu as like disabled. How to show the menu as disabled.

2.  When we need to disable the menu "Export", why should we disable the menu "Package".

      Can you pls clarify...

#target indesign

app.menuActions.itemByID (39938).addEventListener ("beforeInvoke", disableProcess);      // Package Menu

app.menuActions.itemByID (113411).addEventListener ("beforeInvoke", disableProcess);     // Export Menu

function disableProcess(evt)

{

    evt.stopPropagation();

    evt.preventDefault();

    return;

}

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 09, 2018 Mar 09, 2018

Copy link to clipboard

Copied

Hello there again.

in relation to your questions in your last post (in order):

1: Have a look at the conversation that Uwe and I were having earlier in this post and you can see that we agree that adding "destructive" elements to the startup script (such as remove() are not a good idea and that neither of us would implement them). It's not impossible to do, but it's information that we think is not a good idea to have in the public domain for other readers to misinterpret or misuse.

2: Uwe has highlighted a workaround that someone could use if they realise they can't export a PDF using either the file menu, keyboard shortcut or quick apply menu. If using the package feature, a PDF can successfully be made, despite the event listener looking for the "export" function. Similarly, if someone makes the ID file a data merge file and then chooses to export to PDF through the Data Merge palette, this is another workaround that won't trigger the event listener.

While testing my preflight enforcer scripts to confirm the last two paragraphs, I've indeed discovered faults with them that I will put down to the latest version of ID (I haven't tested the scripts in a while, there's been no demand for it).

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 10, 2018 Mar 10, 2018

Copy link to clipboard

Copied

https://forums.adobe.com/people/Colin+Flashman  wrote

… 1: Have a look at the conversation that Uwe and I were having earlier in this post and you can see that we agree that adding "destructive" elements to the startup script (such as remove() are not a good idea and that neither of us would implement them). It's not impossible to do, but it's information that we think is not a good idea to have in the public domain for other readers to misinterpret or misuse.

Hi Colin,

using remove() on a menu would not show it at all.
It would not show it disabled ( grayed out ).

I don't think one could show menus like Export… and Package… as disabled.

At least not the original InDesign ones.

One way could be to remove them at all and add new menus with the same name and show them as disabled perhaps.

But then you have the problem to add the original menus again if you want to. That would require a reinstall of InDesign and trashing preferences and/or cleaning the cache. Perhaps only cleaning the cache ( never tried that ).

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 11, 2018 Mar 11, 2018

Copy link to clipboard

Copied

Hi,

     I don't want to remove and all... Its enough to stop the process alone.

     Thank you for ur patient reply... Thank you..

     Any error in my code.. Its not working now...  I dont know the reason...

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 11, 2018 Mar 11, 2018

Copy link to clipboard

Copied

Hi,

     I have commented below  line. so its not working...

     #targetengine 'onOpen'

     What is the uses of "target" and "targetengine" and when and how to use it??

     I am new to startup script... is there is any basic tutorial or documents to learn startup script?

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 12, 2018 Mar 12, 2018

Copy link to clipboard

Copied

Hi,

you could test your script without being a start-up script first.

Best would be to include a "toggle" functionality:

Start the script: The event listeners will be added.

Start the script a second time: The event listeners will be removed.

Include alert messages.

How would that work? Identify your event listeners by name. For example:

( function()

{

#targetengine "bestChooseUniqueNameHere"

var myEventListenerName = "givenName";

var myEventListener = app.menuActions.itemByID(39938).eventListeners.itemByName( myEventListenerName );

if( myEventListener.isValid ){ myEventListener.remove(); alert("Listener removed."); return } // End script here!

myEventListener = app.menuActions.itemByID(39938).addEventListener("beforeInvoke", disableProcess);

myEventListener.name = myEventListenerName ;

alert("Listener added!");

// …

}() )

So you see, you can give event listeners names.

Look into this: Adobe InDesign CS6 (8.0) Object Model JS: EventListener

Otherwise you always have to restart InDesign if something goes wrong.
Or you need a second script to remove it…

app.menuActions.itemByID(39938).eventListeners.everyItem().remove();

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 12, 2018 Mar 12, 2018

Copy link to clipboard

Copied

HI,

     I run the code given above.  It gives the alert for "Listener added" but did not give any alert for "Listener removed". I quit the application and run the script.

     My doubt is, startup script start to work when indesign is on.  When application is quit, event listener wont get removed??

Screen Shot 2018-03-13 at 7.21.53 AM.png

     Scripts all will load when app is on.. Then how to call it as second script?

     Can I use like this...

#target indesign

#targetengine 'onOpen'

var elPackageName = "PackageEventListner";

var elPackage = app.menuActions.itemByID(39938).eventListeners.itemByName( elPackageName );

if(elPackage.isValid)

{

    elPackage.remove();

}

var elExportName = "ExportEventListner";

var elExport = app.menuActions.itemByID(39938).eventListeners.itemByName( elExportName );

if(elExport.isValid)

{

    elExport.remove();

}

var elExportToPDFName = "PackageEventListner";

var elExportToPDF = app.menuActions.itemByID(39938).eventListeners.itemByName( elExportToPDFName );

if(elExportToPDF.isValid)

{

    elExportToPDF.remove();

}

var elPackage = app.menuActions.itemByID (39938).addEventListener ("beforeInvoke", disableProcess);      // Package

var elExport =  app.menuActions.itemByID (113411).addEventListener ("beforeInvoke", disableProcess);     // Export...

var elExportToPDF =  app.menuActions.itemByID (108046).addEventListener ("beforeInvoke", disableProcess);     // Export To PDF

function disableProcess(evt)

    evt.stopPropagation();

    evt.preventDefault();

    return;

}

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 13, 2018 Mar 13, 2018

Copy link to clipboard

Copied

https://forums.adobe.com/people/Sudha+K  wrote

HI,

     I run the code given above.  It gives the alert for "Listener added" but did not give any alert for "Listener removed". I quit the application and run the script. …

Hi,

if you run my script snippet above ( you have to add the handler function disableProcess ) from the ESTK or with a double-click from InDesign's Scripts Panel the listener will be added. An alert is showing this. Now to remove the listener you have to run it a second time. That's the thought behind the "toggle" functionality of the snippet. I tested this and it should work as I described it.

To answer your second question: Yes, the event listener is removed if you quit InDesign.
Therefore: For a permanent solution you have to run the script as startup-script.

I think it's tedious if one is in a testing/debugging phase to permanently quit and restart InDesign to get a clean environment if one want to change code and try things out. So I suggested the toggle functionality…

That's all to it.

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 13, 2018 Mar 13, 2018

Copy link to clipboard

Copied

Hi Sudha,

[It seems to me that most of the points discussed above have already been touched by my fellows in sparse existing threads. Anyway the subject remains specially difficult to beginners and it's probably worthwhile to summarize the picture, so let's try!]

The general issue in dealing with menu items (including associated actions, event listeners, etc.) is to get a clear understanding of object lifecycles in InDesign scripting model. Most of us have gradually grasped these subtleties, although there are still many secrets to discover.

In short, what you MUST know before going any further is:

0. Menus and Submenus own MenuItems which are connected to InDesign processes through MenuActions (and ScriptMenuActions, but I'll leave that aside.) On the other hand, Events may be connected to custom callback functions through EventListeners. Most—if not all—InDesign entities can be the target of some event, and this includes Menus (cf beforeDisplay) and MenuActions (cf beforeInvoke), but also PageItems or even the app itself. In your example, though, MenuActions are the actual center of interest.

Lifecycles:

1. As objects, native InDesign MenuActions are permanent. That is, you can't remove or change them, ever.

2. As objects, Menus, Submenus and MenuItems are application-persistent. That is, if you remove or change something in that space, these changes will persist beyond the ID session. That's a crucial and not very intuitive fact.

3. As objects, EventListeners (and ScriptMenuActions, but ok, let's really forget them) are always session-persistent. That is, changes in that space are intended to persist throughout the whole ID session, until you quit the app.

4. Finally, your event handlers—that is, the callback functions contained in your JSX code and attached to particular events via EventListeners—are at best engine-persistent (if a #targetengine directive is active) or simply “script-persistent”, in other words purely volatile, if you run in the default main engine.

Note: There are in fact more persistent event handlers, those which are attached as JSX File objects.

See also: Indiscripts :: How to Create your Own InDesign Menus

Now you can clearly see that attaching an event handler to a MenuAction (invoked from a MenuItem) via a freshly created EventListener, may cause some issues if not carefully designed. For example, you may have an EventListener still alive—in the session—whose callback function is yet dead. In other threads we also discussed the problem of having a custom Menu and its MenuItem children (all application-persistent) waiting for an appropriate EventListener to wake up—which can be achieved with startup scripts only.

So, the very first step is to clearly have in mind how these respective lifecycles have to be dealt with and connected in any project that interacts with such objects. The gold rule is, you need at least two distinct stages:

(A) The Install Stage. This is the part of your code that initializes the persistent objects to be connected and used throughout the scope (usually, the engine) so that things are guaranteed to exist when you'll need them. That's typically the place to declare event handlers (but not to call them!), and to manage EventListeners to be either created or destroyed, not to mention menu structure, etc. In most projects the Install Stage is intended to run once per session, but since it could be manually executed again and again by an inquiring user, we must prevent troubles with object duplication.

(B) The Script-In-Action Stage. Ideally, This Should Be an Empty Zone! Indeed, when installing event handlers, what you want is just to process outside events (that may or will happen later), hence the script itself should have nothing more to do than installing (once.) I think it important to stress this point because some scripters have trouble capturing it well: the script "in action" does basically nothing once installed. The real process is delayed to event handlers that wait patiently to take action.

However, there are cases where (B) can have a job, namely, the job of reversing (A). This is what seems to emerge from reading the previous posts. The script, apart from the fact that it sets up listeners and event handlers, may be thought as a switch that toggles the whole event processing. So we create, within the Install area, a slightly enhanced function—say toggleListeners—designed to address this special feature.

Now, better is to provide a (generously commented) code to illustrate the approach I recommend:

//==========================================================================

// We definitely need a session-persistent engine to keep our event

// handler(s) alive, so we use the #targetengine directive.

//==========================================================================

#targetengine 'ExportAndPackageSpy'

//==========================================================================

// Since we have a session-persistent engine, executing the

// present script shouldn't cause re-declaration of existing

// data. The following conditional block takes this into

// account and provides the 'installation' brick.

//==========================================================================

if( !$.global.hasOwnProperty('DATA') )

{

    // Builds a set of native MenuActions of interest.

    //----------------------------------

    $.global.DATA = (function(  o,r)

        {

            // Register package&export action specifiers, and other hosts if needed.

            // ---

            o = app.menuActions;

            r = {

                Package:   { host:o.itemByName("$ID/Package...").toSpecifier(),    etp:'beforeInvoke' },

                Export:    { host:o.itemByName("$ID/Export...").toSpecifier(),     etp:'beforeInvoke' },

                PdfExport: { host:o.itemByName("$ID/Export To PDF").toSpecifier(), etp:'beforeInvoke' },

                };

            // In addition you'd like to catch PDF 'sub-actions' which 'Export To

            // PDF' does not cover: [High Quality Print]..., [PDF/X-1a:2001]...,

            // etc. Unfortunately, the listeners of those menu actions do not work!

            // As a fallback mechanism you can globally spy the beforeExport event.

            // It occurs *after* menu actions but can still be used to prevent

            // the user from exporting the document by unexpected means.

            // ---

            r['AnyExport'] = { host:app.toSpecifier(), etp:'beforeExport' };

            // Debug.

            // alert( r.toSource() );

            return r;

        })();

    // Defines the *actual function to be executed* by the

    // present script. In this particular case a 'toggle' feature

    // is wanted in order to turn listening ON/OFF. This process

    // is slighlty different from a pure menu installer but most

    // implementations use very similar tricks.

    //----------------------------------

    $.global.toggleListeners = function(/*fct*/callback,  loading,k,o,a,t)

    {

        // Even if a default callback is provided below, making it

        // visible as an input argument improves the modularity of

        // the code. (Technically, we could attach other event

        // handlers as well.)

        // ---

        'function' == typeof callback || (callback=$.global.disableProcess);

       

        // Paranoid checkpoint.

        // ---

        if( 'function' != typeof callback) throw "No function callback supplied!"

        // Here we use the function itself (callee) as a 'flag keeper,'

        // taking advantage of its persistence. If loading is 1,

        // listeners are to be added.

        // ---

        loading = 1 - (callee.LOADED||0);

        // Loop in the DATA.

        // ---

        for( k in DATA )

        {

            if( !DATA.hasOwnProperty(k) ) continue;

            // Collection of event listeners registered for this

            // menu action.

            // ---

            o = resolve(DATA.host).eventListeners;

            // *In any case*, remove any existing listener that matches

            // callback. This prevents debug or non-well-formed code from

            // duplicating listeners throughout the current session

            // (should #targetengine be missing or other bug occur...)

            // ---

            a = o.length ? o.everyItem().getElements() : [];

            while( t=a.pop() ) t.handler===callback && t.remove();

            if( !loading ) continue;

            // If loading is required, add the listener.

            // ---

            o.add(DATA.etp, callback);

        }

        // Upate the internal flag.

        // ---

        callee.LOADED = loading;

        // Debug.

        alert( loading ? "Listeners LOADED." : "Listeners UNLOADED.");

    };

    // Now comes the particular callback being invoked when any of the

    // spied events occurs. A critical property of this function is to

    // remain in memory as long as the event listeners may exist. Since

    // they are by nature session-persistent, we need a session-persistent

    // function as well (or a File, in other implementations.)

    // ---

    // This part of the code entirely depends on your specific goal. In

    // your example the actions have to be inhibited (likely under some

    // additional conditions?) Here we simply cancel the event, using

    // the fact that both BEFORE_INVOKE & BEFORE_EXPORT are cancelable.

    //----------------------------------

    $.global.disableProcess = function(/*Event*/ev)

    {

        // Make sure no other hypothetical target

        // would catch that event (from now.)

        // ---

        ev.stopPropagation();

        // Cancels the event. (In some versions, InDesign may prompt

        // a message telling the user that a native action has been

        // cancelled.)

        ev.preventDefault();

        // Debug.

        alert( "Cancelled event:\r\r" + {}.toSource.call(ev.properties) );

    };

}

//==========================================================================

// From this point the installation process is ready (data and functions

// are known to the engine), but no event listener has been either added

// or removed. The below instructions represent the actual finality of

// executing the present script, that is, toggling event listeners. This

// could be performed from a *startup script*, with the effect of turning

// listeners ON (since no flag is attached to the function yet.)

//==========================================================================

toggleListeners();

Hope that helps.

Marc

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 14, 2018 Mar 14, 2018

Copy link to clipboard

Copied

https://forums.adobe.com/people/Marc+Autret  wrote

Lifecycles:

1. As objects, native InDesign MenuActions are permanent. That is, you can't remove or change them, ever.

Hi Marc,

thank you very much for that summary!

We briefly discussed the case that native menus could be removed, but shouldn't. It happened to me once accidently and I had really trouble to get the removed native menu back. In such a case I suppose—I will not test that !—that the menuActions coming along with that native menu are gone as well.

You did mention:

B) The Script-In-Action Stage. Ideally, This Should Be an Empty Zone! Indeed, when installing event handlers, what you want is just to process outside events (that may or will happen later), hence the script itself should have nothing more to do than installing (once.) I think it important to stress this point because some scripters have trouble capturing it well: the script "in action" does basically nothing once installed. The real process is delayed to event handlers that wait patiently to take action.

I think, we should never expect that an event handler is there. From the perspective of any #targetengine running at the same time—various startup scripts are running concurrently plus some scripts the user starts from the Scripts Panel—attached listeners are available to all scripts indepenently of the #targetengine they were added from. A script from a different developer could remove them by a careless menu.eventListeners.everyItem().remove() . So the snippet I gave in answer #17 is really bad practice. I only added it as a potential tool in the debugging and development phase of a script.

What can we do about that potential threat?

Running an eventListener on an idleTask that is checking if all is ok?

That all asked in the light of:

… the script "in action" does basically nothing once installed. The real process is delayed to event handlers that wait patiently to take action. …

Thanks again,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 14, 2018 Mar 14, 2018

Copy link to clipboard

Copied

Hi Uwe,

We briefly discussed the case that native menus could be removed, but shouldn't. It happened to me once accidently and I had really trouble to get the removed native menu back. In such a case I suppose—I will not test that !—that the menuActions coming along with that native menu are gone as well.

(…)

I don't think so, but better would be to ask Adobe team to have a 100% certainty. To my knowledge, you can remove a MenuItem but you cannot remove a native MenuAction, even if it becomes invisible in GUI terms. For example, suppose you remove the File > Open… item [please, readers, don't do that!], my guess is that the underlying menu action still permanently exists.

. . .

OK, let's play guinea pigs! In my CS4 installation (French-localized), I have a menu item named "Programme d'amélioration des produits Adobe..." in the Help (=Aide) submenu. It will never have any function again, since CS4 support is abandoned. So this is a good candidate for the experiment:

var mi = app.menus.itemByName('$ID/Main')

           .submenus.itemByName('$ID/&Help')

           .menuItems.itemByName("Programme d'amélioration des produits Adobe...");

function showActionId(/*MenuItem*/mi)

//----------------------------------

// => `0x164ED`

{

    alert( '0x'+mi.associatedMenuAction.id.toString(16).toUpperCase() )

}

if( mi.isValid )

{

    showActionId(mi); // Save it in a const!!!

    mi.remove();      // the menu is gone

}

From now the menu item is gone:

RemovingMenuItem.png

But you know what? I can still invoke the associated menu action:

const AID = 0x164ED;

var ma = app.menuActions.itemByID(AID);

ma.isValid && ma.invoke();

Result:

ImproveScreen.png

@+

Marc

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 14, 2018 Mar 14, 2018

Copy link to clipboard

Copied

Ah! Thank you for testing that, Marc!

Regards,
Uwe

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 14, 2018 Mar 14, 2018

Copy link to clipboard

Copied

Laubender  wrote

I think, we should never expect that an event handler is there. From the perspective of any #targetengine running at the same time—various startup scripts are running concurrently plus some scripts the user starts from the Scripts Panel—attached listeners are available to all scripts independently of the #targetengine they were added from. A script from a different developer could remove them by a careless menu.eventListeners.everyItem().remove() . (…)

What can we do about that potential threat?

Running an eventListener on an idleTask that is checking if all is ok?

You're absolutely right about the potential threat. I seem to remember we touched that topic before.

A script should NEVER use instructions of the form …eventListeners.everyItem().remove()—unless you are in the process of fixing a critical bug that totally breaks ID!

The reason is, as I wrote above, EventListeners are session-persistent, their lifespan exceeds that of your particular script (which runs in its own engine.) In other words, your script is not the omniscient owner of every event listener currently running. Indeed, parallel scripts (in particular, startup scripts) may have their own stuff installed, based on dedicated listeners. Scripters should keep in mind, in addition, that the same event can be addressed by multiple listeners!

“Running an eventListener on an idleTask that is checking if all is ok?”

That's an option, assuming your enemies won't kill idle tasks as well!

To me the best solution is, scripting ethics & good practices.

@+

Marc

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 14, 2018 Mar 14, 2018

Copy link to clipboard

Copied

Uwe,

One more word.

In the cleanup part of the code provided at #20, you may notice the condition under which I actually remove a listener:

<listener>.handler===callback && <listener>.remove()

Other implementations may rely on id or name property (name is not CS4-compliant, btw). The advantage of using a strict === check on the handler itself, IMO, it that you are absolutely sure that the handler belongs to your own script—since you delared it in your own engine. We could paraphrase the test as follows: “If I AM THE ONE that is called by an event listener, then I can remove that event listener."

@+

Marc

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 19, 2018 Mar 19, 2018

Copy link to clipboard

Copied

Hi,

     Thanks for your help...

     I need one more clarification, we can disable the menus functionality using event listener.  Can we disable (stop) the process of only export PDF from Export menu?

     Export menu have multiple options (html, idml, pdf etc..). Can we stop the process of specific file format of export??

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines