Community
    • Login

    Handling Ctrl+C in a c++ plugin dialog

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    7 Posts 4 Posters 835 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • PeterJonesP
      PeterJones
      last edited by

      I know this borders on a generic Win32 coding question, so it’s borderline off-topic. But I’m trying to figure out how to handle Ctrl+C to copy elements from the (as I said I would here)

      I am using the main C++ plugintemplate coding style (so more c-like than c++, so not with a separate class for each dialog, just the old-fashioned callback dialog-proc).

      My searches have suggested either using TranslateAccelerator (with accelerators defined in the RC file), or just handling WM_KEYDOWN in my dialog procedure.

      TranslateAccelerator seems to need to be called from the main message loop (in the GetMessage/TranslateMessage/DispatchMessage) – but I think that’s controlled outside the plugin (at least, the plugintemplate doesn’t have that explicit message loop), so I don’t think that’s a viable option for a plugin (unless I’ve misunderstood, which is quite possible).

      So I thought WM_KEYDOWN was a better bet. But when I add that case WM_KEYDOWN: to my dialog-proc function, that message never gets received by my procedure, whether I type Ctrl+C or just a normal keyboard character. I am guessing I am missing something (like some flag in the RC file definition for the dialog and/or the listbox control), but if so, I cannot find it; or maybe it’s a fundamental concept or something else I need to do.

      If I cannot get past this, I will just add a “copy selected error” button. But Ctrl+C would be so much more elegant, so I was hoping a more-experienced plugin programmer could chime in with how to make it work, or at least give me some hints that my internet searches haven’t turned up yet.

      Thomas KnoefelT 1 Reply Last reply Reply Quote 0
      • Thomas KnoefelT
        Thomas Knoefel @PeterJones
        last edited by Thomas Knoefel

        @PeterJones
        The reason your DialogProc doesn’t see WM_KEYDOWN is keyboard focus. Windows sends keyboard messages directly to the control that is currently active—in this case, your list control. The parent dialog is never notified.

        Since your plugin can’t access the main Notepad++ message loop to use accelerators, the correct approach is to tap directly into the message stream of the list control itself. This is what subclassing is for. It lets you create a small, specialized message handler that inspects messages for your control before the default behavior kicks in. It’s a clean, safe, and officially sanctioned way to extend control functionality.

        The most robust implementation is a subclass procedure that is completely self-contained. It handles the specific key combination you’re interested in and even cleans itself up automatically when the control is destroyed. This makes your code more modular and easier to maintain.

        Here is what such a procedure looks like. It’s the only piece of complex code you need.

        static LRESULT CALLBACK ListBoxSubclassProc(
            HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
            UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
        {
            // Check for the universal Ctrl+C signal (works on all keyboard layouts)
            if (uMsg == WM_CHAR && wParam == 0x03) {
                CopySelectionFromControl(hWnd); // Your function to copy text
                return 0; // Event handled
            }
        
            // When the control is destroyed, it automatically removes this subclass
            if (uMsg == WM_NCDESTROY) {
                RemoveWindowSubclass(hWnd, ListBoxSubclassProc, uIdSubclass);
            }
        
            // For all other messages, just pass them to the default handler
            return DefSubclassProc(hWnd, uMsg, wParam, lParam);
        }
        

        This procedure is activated with a single line in your DialogProc’s WM_INITDIALOG case: SetWindowSubclass(hListBox, ListBoxSubclassProc, 0, 0);. That’s it. Because the subclass cleans itself up, you don’t even need to add code to WM_DESTROY.

        PeterJonesP 1 Reply Last reply Reply Quote 2
        • PeterJonesP
          PeterJones @Thomas Knoefel
          last edited by

          @Thomas-Knoefel ,

          Well, that got me farther. With some keystrokes, like a normal letter, or a key-combo that’s not assigned to anything in Notepad++ (like Shift+Ctrl+C), it will call that subclass function and see the uMsg==WM_CHAR. But Ctrl+C doesn’t trigger it; and if I try a shortcut that does something obviousl in N++, like Alt+C (for the Column Editor).

          But that reminded me of the recent conversation about shortcut mappings, and so I tried sending the appropriate NPPM_MODELESSDIALOG arguments during my plugin’s init & destroy, and now it can capture Ctrl+C (or any other shortcut).

          Thank you

          Fatih CoşkunF 1 Reply Last reply Reply Quote 3
          • Fatih CoşkunF
            Fatih Coşkun @PeterJones
            last edited by

            @PeterJones

            Hello,
            I have a related question:
            I want to change the behavior of two keyboard shortcuts for my plugin. I have been recommended to subclass main window for this. Am I allowed to do that? Can I subclass “plugin.nppData._nppHandle”?

            Thank you

            CoisesC 1 Reply Last reply Reply Quote 0
            • CoisesC
              Coises @Fatih Coşkun
              last edited by Coises

              @Fatih-Coşkun said in Handling Ctrl+C in a c++ plugin dialog:

              I want to change the behavior of two keyboard shortcuts for my plugin. I have been recommended to subclass main window for this. Am I allowed to do that? Can I subclass “plugin.nppData._nppHandle”?

              You can. For the purpose you describe, you probably shouldn’t.

              If you want to change the behavior of existing shortcut keys, the way to do that is to make sure the commands you want them to do are among the items you set on your plugin’s menu during initialization (getFuncsArray), so they will be available to Shortcut Mapper. Then explain to your users how to change the key assignments.

              It would be a bad idea to attempt to change the assignment of existing shortcut keys. That’s the user’s privilege.

              If what you really want to do is intercept and replace a command — regardless of whether it is initiated by menu, toolbar or keyboard — then the only way to do that is to subclass the main Notepad++ window. Be very careful, and try to think of another way to do it first, since the potential for interfering with other functions or plugins is high. Example here, where I needed to avoid an annoying error that could occur when pasting a column selection if my plugin’s Elastic tabstops feature was in use. There was no practical way to fix it but to intercept the Paste command and apply custom tabstops before the paste was allowed to happen.

              PeterJonesP 1 Reply Last reply Reply Quote 0
              • PeterJonesP
                PeterJones @Coises
                last edited by

                @Fatih-Coşkun ,

                Unfortunately, I don’t know how to do what you want. At least, not what I think you want.

                @Coises said in Handling Ctrl+C in a c++ plugin dialog:

                If you want to change the behavior of existing shortcut keys, the way to do that is to make sure the commands you want them to do are among the items you set on your plugin’s menu during initialization

                I interpreted @Fatih-Coşkun’s desire differently than you did, I think. The way you phrased that, you seem to think that the desire is for a keyboard shortcut for something action in the Plugins > MyPlugin > submenu hierarchy. But given the context of my original question, and the way that I read @Fatih-Coşkun’s post, I interpreted as wanting to have a keyboard shortcut specific to an action inside the dialog. I mean, I know in Notepad++'s native Find/Replace/Mark/… dialog family, Notepad++ does special processing, so the first Ctrl+F makes it go to the Find tab in the dialog, and the second Ctrl+F will re-center the dialog. I doubt @Fatih-Coşkun wants something quite that fancy, but maybe something more akin to, "in Notepad++, Ctrl+F opens the Find dialog; I want Ctrl+F in my dialog box to do some action specific to that dialog box (whether it to “find” something not in the document text, or whether it’s some other action where Ctrl+F would be a natural mnemonic). (And, especially if the dialog is modal, I don’t think you’d have to worry about interfering with other plugins, since the dialog has primary control at the moment.)

                It would very much surprise me if it weren’t possible for a plugin to do that (though I cannot think of any off the top of my head that do so). But whether “subclassing” or some other strategy is the right way to implement the shortcut capture is beyond me.

                @Fatih-Coşkun , is my “I want Ctrl+F in my dialog box to do some action specific to that dialog” the right idea (even if it’s not the right shortcut)? And is your dialog modal or not. (I don’t know whether modal is relevant, but if it’s not modal, there’s more of a chance that your interception might confuse things… thought I would think that if a non-modal dialog doesn’t have focus, your DlgProc wouldn’t be processing things, so it shouldn’t interfere with other plugins, even then).

                CoisesC 1 Reply Last reply Reply Quote 0
                • CoisesC
                  Coises @PeterJones
                  last edited by Coises

                  @PeterJones said in Handling Ctrl+C in a c++ plugin dialog:

                  I interpreted as wanting to have a keyboard shortcut specific to an action inside the dialog.
                  […]
                  It would very much surprise me if it weren’t possible for a plugin to do that (though I cannot think of any off the top of my head that do so). But whether “subclassing” or some other strategy is the right way to implement the shortcut capture is beyond me.

                  (Following based on theory/understanding. I have not actually tried to do this.)

                  Subclassing the NPP window would be horrific way to go about this (if it would even work at all). Making shortcuts work specifically in a plugin window is a bit of an awkward problem, because (as far as I can tell) there is only one accelerator table for an entire running program — and Notepad++ already “owns” that table.

                  In a modal dialog or a non-modal window that was registered with NPPM_MODELESSDIALOG it wouldn’t work at all, because messages for those don’t go through TranslateAccelerator. The message would get sent to the window/dialog. This is actually the right way to go, but subclassing the main NPP window would be irrelevant, because the message would never get to that window; it would be dispatched to the plugin window or one of its controls.

                  For a non-modal window without NPPM_MODELESSDIALOG, the shortcut key would still send whatever menu command was assigned to it. In that case, a subclass procedure could check whether the keyboard focus is in a window belonging to the plugin. If it is, process it; if it isn’t, pass it through normally. But note that you’d be catching the command, not the shortcut. Your command would always take over from the NPP command, regardless of whether it was by menu, by tool button, or by whatever shortcut key the user assigned to the NPP function.

                  For modeless dialogs that don’t register, NPP now works around this for cut, copy and paste by sending WM_CUT, WM_COPY and WM_PASTE to the window/control with keyboard focus when one of those commands is invoked and focus is not in an NPP-managed window or control. Note that if you change the shortcut for one of those commands, the dialog will respond to the new shortcut. (It will respond to the “normal” shortcut as well if and only if that shortcut isn’t assigned to a Notepad++ command. Scintilla assignments don’t count; that’s why the problem didn’t show up until the shortcuts were moved from Scintilla to Notepad++.)

                  So to get it right, you have to register the window if it is modeless, and you have to pick up the key combination in your plugin code. How to pick it up is a bit of a pain in the butt, because usually the main window (typically a dialog box) won’t have focus, so the keystrokes will go to whatever control does have focus — unless the dialog manager captures it and applies its own logic (such as it does for the tab key and for Alt+ accelerators).

                  I still haven’t found a clear and complete description of what the dialog manager does when the message loop calls IsDialogMessage. Part of it is bound up with WM_GETDLGCODE, which is sent to controls — not the dialog procedure — which further complicates things. Subclassing every control which could have focus when you want your key combination to work would probably be necessary. I suspect some key combinations cannot be captured, but I don’t know that for a fact.

                  It’s messy. Finding another way — like adding a button for your action and then underlining a letter so that Alt+letter will activate it — is likely to be a lot easier and less fragile. (If the action is control-based, rather than independent of which control in your plugin has focus, then of course the way you did it is correct.)

                  1 Reply Last reply Reply Quote 1
                  • First post
                    Last post
                  The Community of users of the Notepad++ text editor.
                  Powered by NodeBB | Contributors