UI selection?

Jim's Avatar

Jim

04 Feb, 2026 06:34 AM

Hi, Is it possible to detect the currently account in the UI and also selected transactions from the UI? From what I can tell it's not possible. The Account seems to be kind of available through parsing the current window.

  1. 1 Posted by Stuart Beesley ... on 04 Feb, 2026 09:09 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Yes. I’ll look and send later.

  2. 2 Posted by Stuart Beesley ... on 04 Feb, 2026 05:36 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    It's actually very simple.. Hooks were added for MD2024 to do this. In python:

    mdGUI = moneydance.getUI()
    mf = mdGUI.getFirstMainFrame()
    print mf.getSelectedAccount()
    print mdGUI.getSelectedTxns()
    

    (using getFirstMainFrame() is slightly hacky and is not a supported API call as such, but getSelectedTxns() is certainly there to be used and you can backtrack the account from the list of selected txns).

    There is also a way to hook into the right click selected txns within a register if you want to do that?

  3. 3 Posted by Jim on 05 Feb, 2026 02:08 AM

    Jim's Avatar

    Thanks! Yes, sure right-click selected may come in handy. Is there any documentation on what was added? I don't see this in the apidoc.

  4. 4 Posted by Stuart Beesley ... on 05 Feb, 2026 07:32 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Are you using java?

  5. 5 Posted by chiang.jim on 05 Feb, 2026 03:41 PM

    chiang.jim's Avatar

    I use both, but for this UI stuff, yes Java

  6. 6 Posted by Stuart Beesley ... on 05 Feb, 2026 03:46 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    I ask as the context menu hook method is totally different between Java and python extensions.

  7. 7 Posted by Stuart Beesley ... on 05 Feb, 2026 11:23 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    OK, here is the java hook.. It's easy - after you have gotten your head around it..

    Your extension's Main must be extending FeatureModule

    You will need the following, and you may have to compile against the live jar - see what works:

    com.moneydance.apps.md.controller.MDActionContext
    com.moneydance.apps.md.view.gui.MDAction.MDAction
    

    Now override: getActionsForContext(context:MDActionContext):List<Action>

    In conceptual terms, everytime a context menu is triggered, all extensions get their getActionsForContext method called. They must return a list of Actions. If not overridden, then the superclass method is called returning an empty list. This call must return quickly to not lag the UI.

    This method must quickly interrogate the contents of MDActionContext and determine what it can do. It then builds a list of Actions with names that appear on the context menu, and a callback that gets called if clicked... A kotlin example below:

      /**
       * Return a list of contextual actions (javax.swing.Action) that the extension can perform
       * on target objects which are given in the context parameter, along with references to the kind of context and the UI object.
       * The default implementation of this method returns an empty list. If you override it,
       * please ensure that it returns quickly so that the context menu appearance remains snappy.
       *
       * @param context Information about the context, including
       * @return a list of actions that the UI can perform on the
       *
       * @since Moneydance 2024 (build 5100)
       */
      override fun getActionsForContext(context:MDActionContext):List<Action> {
        val actions = mutableListOf<Action>()
        val accounts = context.accounts
        val items = context.items
        val type = context.type
        when (type) {
          ActionContextType.account_list, ActionContextType.sidebar, ActionContextType.register, ActionContextType.invest_register,
          ActionContextType.security_register, ActionContextType.loan_register -> {
            
            if (items.isEmpty()) {
              actions.add(MDAction()
                            .name("Empty items - do nothing ($type)")
                            .command("do_nothing")
                            .callback {});  // do something
            } else {
              actions.add(MDAction()
                            .name("Items contains ${items.size} objects ($type)")
                            .command("item_action")
                            .callback {
                              items.forEach { item ->
                                if (item is Account) AppDebug.ALL.log("My Extension:Context Menu($type):Processing account: $item")
                                if (item is AbstractTxn) AppDebug.ALL.log("My Extension:Context Menu:Processing txn dated: ${item.parentTxn.dateInt}")
                              }
                              mdGUI.showInfoMessage("your extension code can do something with this callback and ${items.size} items")
                            });
            }
            if (accounts.isEmpty()) {
              actions.add(MDAction()
                            .name("Empty accounts - do nothing ($type)")
                            .command("do_nothing")
                            .callback {});  // do something
            } else {
              actions.add(MDAction()
                            .name("Accounts contains ${accounts.size} objects ($type)")
                            .command("account_action")
                            .callback {
                              accounts.forEach { account ->
                                AppDebug.ALL.log("My Extension:Context Menu($type):Processing account: $account")
                              }
                              mdGUI.showInfoMessage("your extension code can do something with this callback and ${accounts.size} accounts")
                            });
            }
          }
          
          else -> {} // do nothing
        }
        return actions
      }
    

    No, it's not documented... Let me know how you get on?

  8. 8 Posted by Stuart Beesley ... on 07 Feb, 2026 10:34 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    ?

  9. 9 Posted by Jim on 07 Feb, 2026 06:18 PM

    Jim's Avatar

    Yup, it's working!
    Did a bit of digging around. Is there any reason not to use:
    txns = mdGui.getSelectedTxns();
    acc = mdGui.getCurrentAccount();

    for retreiving currently selected txns? I had been using the method in post 2 with polling, but this seems much clearner

  10. 10 Posted by Stuart Beesley ... on 07 Feb, 2026 06:32 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    txns = mdGui.getSelectedTxns(); is fine acc = mdGui.getCurrentAccount(); is NOT fine - this is not the selected account. This is the top level account book...

    For some reason mdGui.getSelectedAccount() was removed in the past so you have to use what I said....

  11. 11 Posted by Jim on 07 Feb, 2026 07:15 PM

    Jim's Avatar

    From my testing mdGui.getCurrentAccount() does return the current account (not the root book)...?

  12. 12 Posted by Stuart Beesley ... on 07 Feb, 2026 07:30 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    I misspoke, but I still maintain it is NOT returning the currently selected account (in the GUI sidebar)..

    What does this return in Developer console for you?

    a=moneydance.getUI().getCurrentAccount()
    print a, type(a), a.getAccountType()
    

    It returns the ROOT account (top) level of the account book.. So yes, it's an account, but it has nothing to do with the currently selected account in the GUI sidebar.

  13. 13 Posted by Jim on 07 Feb, 2026 10:28 PM

    Jim's Avatar

    I'm a bit confused....In Java, it seems to return the current sidebar account, but in python it seems to always return the root account. Does that make any sense?

    I noticed I can use getFirstMainFrame() and the md:account:select even together. Is there an event for txn selection?

  14. 14 Posted by Stuart Beesley ... on 07 Feb, 2026 10:34 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    No, these are the events:

    ONLINE_DOWNLOAD_STARTED = "md:app:onlinedownloadstarted"
    ONLINE_DOWNLOAD_FINISHED = "md:app:onlinedownloadfinished"
    BACKUP_STARTED = "md:file:backupstarted"
    BACKUP_FINISHED = "md:file:backupfinished"
    FILE_CLOSING = "md:file:closing"
    FILE_CLOSED = "md:file:closed"
    FILE_OPENING = "md:file:opening"
    FILE_OPENED = "md:file:opened"
    FILE_BEFORE_SAVE = "md:file:presave"
    FILE_AFTER_SAVE = "md:file:postsave"
    APP_EXITING = "md:app:exiting"
    ACCOUNT_SELECTED = "md:account:select"
    HOME_SELECTED = "md:account:root"
    GRAPH_REPORT_SELECTED = "md:graphreport"
    BUDGET_SELECTED = "md:viewbudget"
    REMINDERS_SELECTED = "md:viewreminders"
    LICENSE_UPDATED = "md:licenseupdated"
    
  15. 15 Posted by Stuart Beesley ... on 07 Feb, 2026 10:46 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Nope - what you are seeing makes no sense to me... Can you send me the actual code? How are you getting the ui? Is it something like this:

    MoneydanceGUI mdGUI = (MoneydanceGUI ((com.moneydance.apps.md.controller.Main) context).getUI();
    
  16. 16 Posted by Jim on 07 Feb, 2026 10:51 PM

    Jim's Avatar

    it's

    com.moneydance.apps.md.view.gui.MoneydanceGUI mdGui = (com.moneydance.apps.md.view.gui.MoneydanceGUI) mdUI;  
    Account acc = mdGui.getCurrentAccount();
    
    <pre><code></code>
    </pre>
    
  17. 17 Posted by Stuart Beesley ... on 07 Feb, 2026 11:11 PM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Well, there are only two definitions of getCurrentAccount() in MD, the one in UI points to the one in Main, and this one gets the root account.

    So you are saying that in java when you call mdGUI().getCurrentAccount().getAccountType() - you get something other than ROOT?

    Are you running in an IDE? Can you double-click to decompile and open getCurrentAccount()

  18. 18 Posted by Jim on 08 Feb, 2026 03:12 AM

    Jim's Avatar

    My apologies, I had some fallback code that was 'hiding' the output, so you're absolutely right, its always the root account.

  19. 19 Posted by Jim on 08 Feb, 2026 03:31 AM

    Jim's Avatar

    So at this point the getFirstMainFrame() method seems the most robust for me. I've noticed if I use the getSelectedTxns() and then backtrack the account from the selected txn, it may be incorrect or ambigious. If there is 1 selected txn and it happens to be a transfer, I may get the account on the other side of the transfer. If there are more than1 txn selected I may get multiple accounts (if there are transfers). Tthe getFirstMainFrame method seems to be the only reliable method.
    The other method I have is ((JFrame)window).getTitle() and then parsing the text which is pretty hacky but works.

  20. 20 Posted by Stuart Beesley ... on 08 Feb, 2026 08:25 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    I think your usage of the account on a txn may be incorrect.. Basically you can do this.

    iterate all selected txns, for each call txn.getAccount(). If all are the same account then you can safely use that account, and are probably in an account register. Otherwise, if you get mixed results, then you are probably on a Summary Screen quick search, and yes in this situation you might be looking at either side of the txn...

    Further, if you were to call such a (missing) getSelectedTxns() when you were on the Summary Screen, then getSelectedTxns() would return the root account anyway.

Reply to this discussion

Internal reply

Formatting help / Preview (switch to plain text) No formatting (switch to Markdown)

Attaching KB article:

»

Attached Files

You can attach files up to 10MB

If you don't have an account yet, we need to confirm you're human and not a machine trying to post spam.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac