Please help to port "PopFav" to v2 Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
ronkwan
Posts: 8
Joined: 04 Nov 2016, 03:41

Please help to port "PopFav" to v2

Post by ronkwan » 11 May 2024, 02:44

In the old forum, there was a post about a simple popup menu launcher called "Popfav". I've been using it for over 10 years but have yet to see anyone porting it to ahk v2. Modifying the script to v2 is totally out of my depth, could anyone be nice enough to help? Below is the original script, many thanks;

Code: Select all

; PopFav -- simple popup menu launcher inspired by PopSel

; Sample menus are given in the special commented section below between /* */.
; Two menus are created. Use hotkeys Ctrl-1 and Ctrl-2 to launch them.
; If you want to read menus from another file, change POPFAV_MENU_FILE below.
;
; First line of a menu definition contains a unique Menu Name which identifies
; the menu.  It must be the first instance of this Name in the file.
; Next line with Menu Name ends Menu definition.  Menu Names must conform to
; the rules of AHK variable naming: letters, numbers, and _ only, no spaces.
;
; Within each menu, one line corresponds to one menu item.
; Specify the name of the menu item first, followed by the == string, followed
; by the Target parameter for the Run command.
; See http://www.autohotkey.com/docs/commands/Run.htm for details.
; It's not possible to specify Run parameters other than Target.
;
; To create separator line, omit text after the == string.
; Lines without == are ignored, e.g., blank lines, comments.
;
; Use >> string to create submenu. Only one level sumbenus are possible, that
; is only the first >> string is treated as sumbenu separator.
;
; Once a menu name is defined, use it to create popup menu:
; 1. Call function
;   POPFAV_Create_Menu("MenuName", POPFAV_MENU_FILE)
; 2. Create hotkey
;   {Hotkey}::Menu, MenuName, Show
; See below for examples.

/*
=========== MENU DEFINITIONS =================================================

___________ POPFAV_DEMO_MENU1 ________________________________________________
/Documents and Settings    == C:\Documents and Settings
        folders can open faster if file manager is specified
/Program Files             == explorer.exe %ProgramFiles%
        create separator in main menu
--------==
/%A_Temp%                  == explorer.exe %A_Temp%

        create sumbenu Applications
Applications >> !Notepad.exe  == notepad.exe
Applications >> edit this script in notepad   == notepad.exe %A_ScriptFullPath%
Applications >> !Calc.exe     == Calc.exe
Applications >> !cmd.exe      ==  %comspec% /k cd %A_ScripDir%

        these are examples from AHK help for Run command
AHK help examples >> My Computer        == ::{20d04fe0-3aea-1069-a2d8-08002b30309d}
AHK help examples >> Recycle Bin        == ::{645ff040-5081-101b-9f08-00aa002f954e}
AHK help examples >> ------------------ ==
        Do not escape , in Target string.
AHK help examples >> Display Properties->Settings == rundll32.exe shell32.dll,Control_RunDLL desk.cpl,, 3

        create separator in main menu, this ends previous sumbenu
--------==

        Potential gotchas with deref. Don't use %% unless you have to.
Gotchas >> this menu name: %MenuName%        == %A_Space%
Gotchas >> edit menu definition file in notepad   == notepad.exe %MenuFile%
___________ POPFAV_DEMO_MENU1 ________________________________________________


___________ POPFAV_DEMO_MENU2 ________________________________________________
        links
autohotkey.com          == http://www.autohotkey.com
autohotkey.com/docs     == http://www.autohotkey.com/docs/
google                      == http://www.google.com/webhp?num=100
___________ POPFAV_DEMO_MENU2 ________________________________________________

========= END OF MENU DEFINITIONS ============================================
*/

;;;;; Specify file which contains PopFav menu definitions.
; Menus are defined at the start of this script file between /* */.
POPFAV_MENU_FILE = %A_ScriptFullPath%
; Menus are defined in external file in this script's dir.
; POPFAV_MENU_FILE = %A_ScriptDir%\PopFav.menus

;;;;; Create PopFav menus.  Use menu names defined in menu file.
POPFAV_Create_Menu("POPFAV_DEMO_MENU1", POPFAV_MENU_FILE)
POPFAV_Create_Menu("POPFAV_DEMO_MENU2", POPFAV_MENU_FILE)

;;;;; Create hotkeys which show PopFav menus.
^1::Menu, POPFAV_DEMO_MENU1, Show
^2::Menu, POPFAV_DEMO_MENU2, Show

;;;;; End of PopFav configuration. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
return
;====================== end of auto-execute section ==========================
;=============================================================================


POPFAV_Create_Menu(MenuName, MenuFile) {
; Parse menu definition MenuName from file MenuFile.
; Create menu MenuName and its submenus.
    global
    local pos
    local _left, _right ; parts before and after == separator
    local _left1, _left2 ; parts before and after >> separator
    local OutsideOfMenu = 1
    local SubLev = 0 ; 0 if in main menu, 1 if in submenu
    local SubName ; current submenu item; part before >> with whitespace stripped
    local MenuIdx = 1 ; main menu item index, also a submenu id: %MenuName%%MenuIdx%
    local SubIdx = 1 ; current sumbenu item index, increment after adding to sumbenu

    Loop, Read, %MenuFile%
    {
        ; Skip lines until the first line with MenuName.
        if OutsideOfMenu
        {
            IfInString, A_LoopReadLine, %MenuName%
                OutsideOfMenu = 0
            continue
        }
        ; Second line with MenuName marks the end of menu definition.
        ; Add last submenu, if any, and exit loop.
        IfInString, A_LoopReadLine, %MenuName%
        {
            if SubLev = 1
                Menu, %MenuName%, Add, %SubName%, :%MenuName%%MenuIdx%
            break
        }
        ; We are inside menu definition.
        ; Add menu item to (sub)menu and create global variable containing the
        ; command of this (sub)menu item. Variables are named:1
        ;      %MenuName%_cmd%MenuIdx%           --main menu
        ;      %MenuName%%MenuIdx%_cmd%SubIdx%   --submenu
        ; Add menu separator if there is nothing after the first == string.
        ; NOTE: increment (sub)menu item index after adding any item, including
        ; separator or submenu.
        ;
        ; get location of ==
        StringGetPos, pos, A_LoopReadLine, ==
        if pos < 0; ignore line without ==
            continue
        ; get part after ==
        StringTrimLeft, _right, A_LoopReadLine, pos+2
        _right = %_right%  ; strip whitespace
        ; get part before ==
        StringLeft, _left, A_LoopReadLine, pos
        ; resolve any references to variables (assumes that submenu separator cannot be created)
        Transform, _left, deref, %_left%
        _left = %_left%  ; strip whitespace
        ; check for submenu separator
        StringGetPos, pos, _left, >>
        ;;;;;;;;;; there is no submenu, add item to main menu
        if pos < 0
        {
            if SubLev = 1 ; previous submenu ended, add it
            {
                SubLev = 0
                Menu, %MenuName%, Add, %SubName%, :%MenuName%%MenuIdx%
                MenuIdx++
                SubIdx = 1
            }
            if _right =
            {
                Menu, %MenuName%, Add
                MenuIdx++
            }
            else
            {
                Transform, %MenuName%_cmd%MenuIdx%, deref, %_right%
                Menu, %MenuName%, Add, %_left%, POPFAV_MENU_HANDLER
                MenuIdx++
            }
        }
        ;;;;;;;;;; there is a submenu; add item to it
        else
        {
            StringLeft, _left1, _left, pos ; sumbenu name
            _left1 = %_left1%
            StringTrimLeft, _left2, _left, pos+2 ; sumbenu item name
            _left2 = %_left2%
            ;;;;; new submenu after main menu
            if SubLev = 0
            {
                SubLev = 1
                SubName = %_left1%
            }
            ;;;;; another item of previous submenu, nothing changed
            else if _left1 = %SubName%
            {
            }
            ;;;;; new submenu after previous submenu
            else
            {
                ; add previous submenu
                Menu, %MenuName%, Add, %SubName%, :%MenuName%%MenuIdx%
                MenuIdx++
                SubIdx = 1
                ; start building new submenu
                SubName = %_left1%
            }
            ;;;;; add item to sumbenu, add separator if there is nothing after ==
            if _right =
            {
                Menu, %MenuName%%MenuIdx%, Add
                SubIdx++
            }
            else
            {
                Transform, %MenuName%%MenuIdx%_cmd%SubIdx%, deref, %_right%
                Menu, %MenuName%%MenuIdx%, Add, %_left2%, POPFAV_MENU_HANDLER
                SubIdx++
            }
        }
    }
}


POPFAV_MENU_HANDLER:
; Handler for all PopFav menu items.
    ; Fetch variable containing action for selected menu item.
    ; _popfav_cmd contains last menu action: handy for debugging
    _popfav_cmd := %A_ThisMenu%_cmd%A_ThisMenuItemPos%
    Run, %_popfav_cmd%
return

niCode
Posts: 307
Joined: 17 Oct 2022, 22:09

Re: Please help to port "PopFav" to v2  Topic is solved

Post by niCode » 11 May 2024, 17:16

I don't have v1 so I can't test the original to try and recreate it 1:1 but here's my best shot.

You don't need external files for this. Just pass the lines as parameters to the function. The third menu is only to demonstrate how to add divider lines. I couldn't get the display properties->settings command in the example to completely work. I don't know if something changed in Windows 11 or if the author just chose a misleading name.

I feel like the script should be pretty self explanatory since it's similar to the old one but I might as well explain it anyway for onlookers.


So the way it works is it tries to separate the lines into three sections based on >> and ==.
  • If three sections are found, everything before >> is a sub-menu, everything to the right of == becomes the argument for the Run function, and the middle is the text on the sub-menu item.
  • If two sections are found, the left side is the text for the menu item on the main menu screen and the right side is the argument for the Run function. Because of this, you could technically use Menu Name >> notepad.exe instead of Menu Name == notepad.exe to create an item in the main menu. Normally you would think >> is the sub-menu name but it doesn't make sense to have a sub-menu name and only one other section so I don't see this being an issue.
  • If one section is found, it creates a sub-menu if it doesn't already exist and adds a divider line.
  • If none are found (empty argument), a divider is added to the main menu screen.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance


POPFAV_DEMO_MENU1 := POPFAV_Create_Menu(
    'Documents and Settings == ' A_MyDocuments,
    'Program Files == explorer.exe ' A_ProgramFiles,
    '%Temp% == explorer.exe ' A_Temp,
    'Applications >> !Notepad.exe == notepad.exe',
    'Applications >> !Calc.exe == Calc.exe',
    'Applications >> !cmd.exe == ' A_ComSpec ' /k cd ' A_ScriptDir,
    'Applications >> edit this script in notepad == notepad.exe ' A_ScriptFullPath,
    'AHK help examples >> My Computer == ::{20d04fe0-3aea-1069-a2d8-08002b30309d}',
    'AHK help examples >> Recycle Bin == ::{645ff040-5081-101b-9f08-00aa002f954e}',
    'AHK help examples >> Display Properties->Settings == rundll32.exe shell32.dll, Control_RunDLL desk.cpl,, 3',
    'AHK help examples >> Display Settings == control desk.cpl'
)

POPFAV_DEMO_MENU2 := POPFAV_Create_Menu(
    'autohotkey.com == http://www.autohotkey.com',
    'autohotkey.com/docs == http://www.autohotkey.com/docs/',
    'google == http://www.google.com/webhp?num=100'
)

; for divider line demonstration purposes
POPFAV_DEMO_MENU3 := POPFAV_Create_Menu(
    'Menu Name >> thing == notepad.exe',            ; create sub-menu called Menu Name
    'Menu Name >>',                                 ; add divider to sub-menu Menu Name
    'Menu Name >> other thing == notepad.exe',      ; create sub-menu item in Menu Name
    'Menu Name',                                    ; add divider to sub-menu Menu Name
    ,                                               ; add divider in the main menu
    'Other Menu',                                   ; create sub-menu and add divider
    ,                                               ; add divider in the main menu
    'item == Calc.exe'                              ; add item to main menu
)

; hotkeys to show menu
^1::POPFAV_DEMO_MENU1.Show()
^2::POPFAV_DEMO_MENU2.Show()
^3::POPFAV_DEMO_MENU3.Show()


POPFAV_Create_Menu(items*) {
    myMenu   := Menu()
    subMenus := Map()

    for index, line in items {
        sections := []                                          ; initialize array for current iteration

        if !IsSet(line) {                                       ; if line wasn't given
            myMenu.Add()                                        ; add divider line to main menu
            continue                                            ; go to next line in list
        }

        for section in StrSplit(line, ['>>', '=='], A_Space) {  ; split line based on >> and ==
            if section {
                sections.Push(section)                          ; add section to array
            }
        }

        switch sections.Length {                                ; check array length
            case 1: subMenuName  := sections[1]                 ; if one section found, assign sub-menu name
            case 2: menuItemName := sections[1]                 ; if two sections found, assign menu item name,
                    targetParam  := sections[2]                 ; assign target parameter
            case 3: subMenuName  := sections[1]                 ; if three sections found, assign sub-menu name,
                    menuItemName := sections[2]                 ; assign menu item name,
                    targetParam  := sections[3]                 ; assign target parameter
        }

        if sections.Length = 2 {
            myMenu.Add(menuItemName, RunCommand.Bind(targetParam))
        } else {
            if not subMenus.Has(subMenuName) {                  ; if sub-menu hasn't been created yet
                subMenus.Set(subMenuName, Menu())               ; create sub-menu and save it in map
                current_menu := subMenus[subMenuName]           ; get the sub-menu just created
                myMenu.Add(subMenuName, current_menu)           ; add sub-menu to main menu
            } else {
                current_menu := subMenus[subMenuName]           ; get the sub-menu just created
            }

            if sections.Length = 1 {                            ; if menu item name is blank
                current_menu.Add()                              ; add divider line
            } else {                                            ; else add sub-menu item
                current_menu.Add(menuItemName, RunCommand.Bind(targetParam))
            }
        }
    }

    return myMenu
    RunCommand(target, *) => Run(target)
}

Post Reply

Return to “Ask for Help (v2)”