Detect a right-click on StringGadget

Just starting out? Need help? Post your questions and find answers here.
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Detect a right-click on StringGadget

Post by Shardik »

HarrysLad wrote:Your code does everything I need for one StringGadget but, on my particular application, I have 10 StringGadgets. Four of them require a popup-menu to be attached but all of them need the system popup to be suppressed.
Please take a look into the following advanced example which allows to subclass multiple StringGadgets on MacOS. It identifies the number of the StringGadget in the RightMouseDownCallback and displays the number of the right-clicked StringGadget. And it further demonstrates how to display a customized popup menu when right-clicking into the 2nd StringGadget.

Code: Select all

EnableExplicit

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define i.I
Define SubclassedStringGadget.I

Dim StringGadgetID(2) ; Number of StringGadgets - 1

Procedure SubclassGadget(GadgetID.I, NewClassName.S)
  Protected GadgetClass.I = CocoaMessage(0, GadgetID(GadgetID), "class")
  Protected NewGadgetClass.I

  NewGadgetClass = objc_allocateClassPair_(GadgetClass, NewClassName, 0)
  objc_registerClassPair_(NewGadgetClass)
  object_setClass_(GadgetID(GadgetID), NewGadgetClass)

  ProcedureReturn NewGadgetClass
EndProcedure

ProcedureC ContextMenuCallback(Object.I, Selector.I, TextView.I, Menu.I,
  Event.I, Index.I)
  ; ----- Suppress context menu
  ProcedureReturn 0
EndProcedure

ProcedureC RightMouseDownCallback(Object.I, Selector.I, Event.I)
  Shared StringGadgetID.I()

  Protected i.I

  For i = 0 To ArraySize(StringGadgetID())
    If Object = StringGadgetID(i)
      If i = 1
        DisplayPopupMenu(0, WindowID(0))
      Else
        Debug "Right mouse button down on StringGadget " + Str(i + 1)
        Break
      EndIf
    EndIf
  Next i
EndProcedure

OpenWindow(0, 270, 100, 350, 130, "Detect right mouse button down")

; ----- Prepare popup menu for StringGadget 2
If CreatePopupMenu(0)
  MenuItem(0, "Menu item 1")
  MenuItem(1, "Menu item 2")
EndIf

For i = 0 To ArraySize(StringGadgetID())
  StringGadgetID(i) = StringGadget(i, 80, 25 + i * 30, 170, 20, "")
  SubclassedStringGadget = SubclassGadget(i, "SubclassedStringGadget" + Str(i))
  class_addMethod_(SubclassedStringGadget,
    sel_registerName_("rightMouseDown:"),
    @RightMouseDownCallback(), "v@:@")
  class_addMethod_(SubclassedStringGadget,
    sel_registerName_("textView:menu:forEvent:atIndex:"),
    @ContextMenuCallback(), "v@:@@@@")
Next i

CocoaMessage(0, StringGadgetID(0), "setDelegate:", AppDelegate)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Menu
      Debug "Menu item " + Str(EventMenu() + 1) + " was selected"
  EndSelect
ForEver
I have also programmed a true cross-platform example (for simplicity's sake for only one StringGadget) which demonstrates how to detect a right click and suppress the context menu.

I have tested this cross-platform example successfully on these operating systems with PB 5.43:
- Linux Mint 18 x86 "Sarah" with Cinnamon (GTK 2 and GTK 3)
- MacOS X 10.9.5 (Mavericks)
- MacOS X 10.11.6 (El Capitan)
- Windows XP SP3
- Windows 8.1 x64

Unfortunately it currently doesn't work on MacOS X 10.6.8 (Snow Leopard)!

Code: Select all

EnableExplicit

#Event_StringGadget_RightClick = #PB_Event_FirstCustomValue
#StringGadget = 0

OpenWindow(0, 270, 100, 350, 75,
  "Detect right click and suppress context menu")
StringGadget(0, 80, 25, 170, 25, "")
SetActiveGadget(0)

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Linux ; -------------------------------------------------
    ProcedureC ButtonPressCallback(*Widget.GtkWidget,
      *EventButton.GdkEventButton, UserData.I)
      If *EventButton\button = 3
        PostEvent(#Event_StringGadget_RightClick)
        ProcedureReturn #True
      EndIf
    EndProcedure

    g_signal_connect_(GadgetID(#StringGadget), "button-press-event",
      @ButtonPressCallback(), 0)
  CompilerCase #PB_OS_MacOS ; -------------------------------------------------
    Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
      "NSApplication sharedApplication"), "delegate")
    Define SubclassedStringGadget.I

    ProcedureC ContextMenuCallback(Object.I, Selector.I, TextView.I, Menu.I,
      Event.I, Index.I)
      ProcedureReturn 0
    EndProcedure

    ProcedureC RightMouseDownCallback(Object.I, Selector.I, Event.I)
      PostEvent(#Event_StringGadget_RightClick)
    EndProcedure

    SubclassedStringGadget = objc_allocateClassPair_(CocoaMessage(0,
      GadgetID(#StringGadget), "class"), "SubclassedStringGadget", 0)
    objc_registerClassPair_(SubclassedStringGadget)
    object_setClass_(GadgetID(#StringGadget), SubclassedStringGadget)
    class_addMethod_(SubclassedStringGadget,
      sel_registerName_("rightMouseDown:"),
      @RightMouseDownCallback(), "v@:@")
    class_addMethod_(SubclassedStringGadget,
      sel_registerName_("textView:menu:forEvent:atIndex:"),
      @ContextMenuCallback(), "v@:@@@@")
    CocoaMessage(0, GadgetID(#StringGadget), "setDelegate:", AppDelegate)
  CompilerCase #PB_OS_Windows ; -----------------------------------------------
    Define DefaultStringGadgetCallback.I

    Procedure CustomStringGadgetCallback(WindowHandle.I, Msg.I, WParam.I,
      LParam.I)
      Shared DefaultStringGadgetCallback.I

      Protected Result.I

      If Msg <> #WM_CONTEXTMENU
        Result = CallWindowProc_(DefaultStringGadgetCallback, WindowHandle, Msg,
          WParam, LParam)
      Else
        PostEvent(#Event_StringGadget_RightClick)
      EndIf

      ProcedureReturn Result
    EndProcedure

    DefaultStringGadgetCallback = SetWindowLongPtr_(GadgetID(#StringGadget),
      #GWL_WNDPROC, @CustomStringGadgetCallback())
CompilerEndSelect ; -----------------------------------------------------------

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #Event_StringGadget_RightClick
      Debug "Right click into StringGadget detected!"
  EndSelect
ForEver
HarrysLad
User
User
Posts: 57
Joined: Fri Jun 04, 2010 9:29 pm
Location: Sunny Spain

Re: Detect a right-click on StringGadget

Post by HarrysLad »

Shardik - Thanks again for the code and all of your work.

Unfortunately it gets me no further forward since the first example works for numerous StringGadgets but only under OSX whereas the second example is cross-platform but operates on only one StringGadget. You write that the second example is limited to one StringGadget 'for simplicity’s sake' but I’ve no idea as to how it could be extended to multiple StringGadgets …

Surely this stuff should be in the compiler, not bloating individual apps.
Frankly, I’m beginning to despair of the whole affair.

Dave
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Detect a right-click on StringGadget

Post by Shardik »

I have programmed an advanced example (supporting multiple StringGadgets) which works on MacOS and Windows:

Code: Select all

EnableExplicit

#NumStringGadgets = 3

Enumeration #PB_Event_FirstCustomValue
  #Event_StringGadget_RightClick
  #Event_StringGadget_RightClick_PopupMenu
EndEnumeration

Define i.I

Dim StringGadgetID.I(#NumStringGadgets - 1)

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_MacOS ; -------------------------------------------------
    Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
      "NSApplication sharedApplication"), "delegate")

    ProcedureC ContextMenuCallback(Object.I, Selector.I, TextView.I, Menu.I,
      Event.I, Index.I)
      ; ----- Suppress context menu
      ProcedureReturn 0
    EndProcedure
    
    ProcedureC RightMouseDownCallback(Object.I, Selector.I, Event.I)
      Shared StringGadgetID.I()

      Protected i.I

      For i = 0 To #NumStringGadgets - 1
        If Object = StringGadgetID(i)
          If i = 1
            PostEvent(#Event_StringGadget_RightClick_PopupMenu, 0, i)
          Else
            PostEvent(#Event_StringGadget_RightClick, 0, i)
          EndIf

          Break
        EndIf
      Next i
    EndProcedure

    Procedure SubclassGadget(GadgetID.I, NewClassName.S)
      Protected GadgetClass.I = CocoaMessage(0, GadgetID(GadgetID), "class")
      Protected NewGadgetClass.I

      NewGadgetClass = objc_allocateClassPair_(GadgetClass, NewClassName, 0)
      objc_registerClassPair_(NewGadgetClass)
      object_setClass_(GadgetID(GadgetID), NewGadgetClass)
      
      class_addMethod_(NewGadgetClass,
        sel_registerName_("rightMouseDown:"),
        @RightMouseDownCallback(), "v@:@")
      class_addMethod_(NewGadgetClass,
        sel_registerName_("textView:menu:forEvent:atIndex:"),
        @ContextMenuCallback(), "v@:@@@@")
    EndProcedure
  CompilerCase #PB_OS_Windows ; -----------------------------------------------
    Define  DefaultStringGadgetCallback.I

    Procedure CustomStringGadgetCallback(WindowHandle.I, Msg.I, WParam.I,
      LParam.I)
      Shared DefaultStringGadgetCallback.I
      Shared StringGadgetID.I()

      Protected i.I
      Protected Result.I

      If Msg = #WM_CONTEXTMENU
        For i = 0 To #NumStringGadgets - 1
          If WParam = StringGadgetID(i)
            If i = 1
              PostEvent(#Event_StringGadget_RightClick_PopupMenu, 0, i)
            Else
              PostEvent(#Event_StringGadget_RightClick, 0, i)
            EndIf
            
            Break
          EndIf
        Next i
      Else
        Result = CallWindowProc_(DefaultStringGadgetCallback, WindowHandle,
          Msg, WParam, LParam)
      EndIf

      ProcedureReturn Result
    EndProcedure
CompilerEndSelect ; -----------------------------------------------------------

OpenWindow(0, 270, 100, 340, 130, "Detect right mouse button down")

; ----- Prepare popup menu for StringGadget 2
If CreatePopupMenu(0)
  MenuItem(0, "Menu item 1")
  MenuItem(1, "Menu item 2")
EndIf

For i = 0 To #NumStringGadgets - 1
  StringGadgetID(i) = StringGadget(i, 80, 25 + i * 30, 170, 20, "")

  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_MacOS ; -----------------------------------------------
      SubclassGadget(i, "SubclassedStringGadget" + Str(i))
    CompilerCase #PB_OS_Windows ; ---------------------------------------------
      DefaultStringGadgetCallback = SetWindowLongPtr_(StringGadgetID(i),
        #GWL_WNDPROC, @CustomStringGadgetCallback())
  CompilerEndSelect ; ---------------------------------------------------------
Next i

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  CocoaMessage(0, StringGadgetID(0), "setDelegate:", AppDelegate)
CompilerEndIf

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Menu
      Debug "Menu item " + Str(EventMenu() + 1) + " was selected"
    Case #Event_StringGadget_RightClick
      Debug "Right mouse button down on StringGadget " + Str(EventGadget() + 1)
    Case #Event_StringGadget_RightClick_PopupMenu
      DisplayPopupMenu(0, WindowID(0))
  EndSelect
ForEver
HarrysLad
User
User
Posts: 57
Joined: Fri Jun 04, 2010 9:29 pm
Location: Sunny Spain

Re: Detect a right-click on StringGadget

Post by HarrysLad »

Thanks Shardik for your hard work - that works perfectly. :D
It took a bit of time and code re-arranging to implement it but all’s well, etc.
This should definitely go in tip the 'tips and tricks' section.

Thanks again. I owe you a pint or two.

Dave
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Detect a right-click on StringGadget

Post by Shardik »

I am glad that I was able to help you out and that it's now working for you... :D
Post Reply