Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Show PopupMenu with programs to launch


  • Please log in to reply
12 replies to this topic
mbirth
  • Members
  • 40 posts
  • Last active: Jul 31 2008 11:12 AM
  • Joined: 03 Oct 2005
I made this to extend the functionality of the buttonbar in Total Commander or the Windows QuickLaunch toolbar. It shows a popup menu with a list of programs and executes the selected command upon click. Nothing more. This way you can have one button doing several things. Configuration is read from an INI-file.

#NoTrayIcon
;#ErrorStdOut

SplitPath A_ScriptFullPath, null, IniDir, null, IniFile, null
IniFile = %IniDir%\%IniFile%.ini

IfNotExist %IniFile%
{
  MsgBox Could not find %IniFile%.
  ExitApp
}

ProcessMenu("Menu")
Menu Menu, Show

Exit

SplitFirst(ByRef OutLeft, ByRef OutRight, InpText, InpSep)
{
  StringGetPos SepPos, InpText, %InpSep%
  If (SepPos >= 0)
  {
    StringLeft OutLeft, InpText, %SepPos%
    RemChars := StrLen(InpText)-SepPos-1
    StringRight OutRight, InpText, %RemChars%
  }
  Else
  {
    OutLeft  := InpText
    OutRight := ""
  }
}

ProcessMenu(Menu)
{
  global IniFile
  IniRead MenuTitle, %IniFile%, %Menu%, Title
  If (MenuTitle <> "ERROR")
  {
    Menu %Menu%, Add, %MenuTitle%, AboutBox
    Menu %Menu%, Default, %MenuTitle%
;    Menu %Menu%, Disable, %MenuTitle%
    Menu %Menu%, Add
  }
  
  IniRead Count, %IniFile%, %Menu%, MaxItem, 0
  Loop %Count%
  {
    IniRead, CurItem, %IniFile%, %Menu%, %A_Index%
    If (CurItem = "-")
      Menu %Menu%, Add
    Else If (CurItem <> "ERROR")
    {
      SplitFirst(CurID, CurTitle, CurItem, "|")
      If (CurTitle = "")
        CurTitle := CurID
      
      IniRead IsSub, %IniFile%, %CurID%, MaxItem, 0
      If (IsSub = 0)
      {
        Menu %Menu%, Add, %CurTitle%, ProcessEvent
        IniRead CurCmd, %IniFile%, %CurID%, Cmd
        If (CurCmd = "ERROR")
          Menu %Menu%, Disable, %CurTitle%
      }
      else
      {
        ProcessMenu(CurID)
        Menu %Menu%, Add, %CurTitle%, :%CurID%
      }
      IniRead IsChecked, %IniFile%, %CurID%, Checked, 0
      If (IsChecked = 1)
        Menu %Menu%, Check, %CurTitle%
    }
  }
  Return
}

ProcessEvent:
  IniRead Count, %IniFile%, %A_ThisMenu%, MaxItem, 0
  Loop %Count%
  {
    IniRead, CurItem, %IniFile%, %A_ThisMenu%, %A_Index%
    If (CurItem <> "ERROR")
    {
      SplitFirst(CurID, CurTitle, CurItem, "|")
      If (CurTitle = "")
        CurTitle := CurID
          
      If (CurTitle = A_ThisMenuItem)
      {
        IniRead Cmd, %IniFile%, %CurID%, Cmd
        If (Cmd <> "ERROR")
        {
          empty := ""
          IniRead TargDir, %IniFile%, %CurID%, StartPath, %empty%
          IniRead StartMode, %IniFile%, %CurID%, StartMode, %empty%
          Loop 9
          {
            CurPar := %A_Index%
            StringReplace Cmd, Cmd, `%%A_Index%, %CurPar%
          }
          Run %Cmd%, %TargDir%, %StartMode%, PID
        }
      }
    }
  }
  
Return

AboutBox:
  MsgBox 4160, About ClickMenu..., ClickMenu shows a popup menu with entries read from an INI file and launches the selected program upon click.`n`nMade in 2005 by Markus Birth <mbirth at webwriters.de>
Return

a sample .ini file looks like this:

[Menu]
;Maximum number to parse, starting at 1
;Empty values are ignored.
MaxItem=10
Title=EXE compression
; Entries are like:
; <no>=<section>|<menu title>
; <no>=-    <=== separator
1=UPX|UPX 1.25w
2=UPXB|UPX 1.93 BETA
3=-
9=MEW|MEW11 SE 1.1 (Compress only)

[UPX]
MaxItem=2
; (set following to 1 to show a checkmark in front of menu entry)
Checked=0
1=UPXC|Compress
2=UPXD|Decompress

[UPXB]
MaxItem=2
; (set following to 1 to show a checkmark in front of menu entry)
Checked=0
1=UPXBC|Compress
2=UPXBD|Decompress

[UPXC]
Cmd="c:\program files\upx.exe" --best --crp-ms=524288 %1
; (set following to 1 to show a checkmark in front of menu entry)
Checked=0
; (omit or leave empty following to start in current path)
StartPath=
; StartMode=(Max|Min|Hide)
; (omit or leave empty following to start normally)
StartMode=

[UPXD]
Cmd="c:\program files\upx.exe" -d %1

[UPXBC]
Cmd="c:\program files\upx_beta.exe" --best --crp-ms=524288 %1

[UPXBD]
Cmd="c:\program files\upx_beta.exe" -d %1

[MEW]
Cmd="C:\Program Files\mew11.exe" %1 -backup -lzma_special

(%1 in those Cmd=lines is replaced by the first parameter of the script. This works up to %9 (for the ninth parameter) for now.)

NOTE: The main menu displayed is what is specified in the [Menu]-section. All other sections can have any name.

Cheers,
-mARKUS

(2005-10-04 Added AboutBox when clicking on a menu heading.)

Rabiator
  • Members
  • 292 posts
  • Last active: Aug 29 2016 09:29 PM
  • Joined: 17 Apr 2005
Marvellous! Especially the interesting way of building menues recursively! :idea:

tomzi
  • Guests
  • Last active:
  • Joined: --
Hi. First I must say that a new in AHK and at programming ... well ... I suck :oops:

I found this script very usefull. I modified .INI file so that not an .EXE would be executed, but a script that copies a selected word from MS Word to dictionary. But ... nothing happened. What did I do wrong? Here is part of .INI file - main program I left as it was:


[English]           

Cmd=                          ; Without this line all menu items are disabled - but I don't know the meaning 
Clipboard =                   ; Clears Clipboard
Send ^c                       ; Copies to Clipboard

IfWinExist, English dictionary
{
	WinActivate
	Send ^v                    ; Paste from Clipboard
}
else
{
	Run, C:\DICT\DICT.EXE      ; Runs dictionary
	WinWait, English dictionary
	WinActivate
	Send ^v                    ; Paste from Clipboard 
}

Return

This is only one item in the menu, but I would like to have more of them - they would all look simmilar (actually - I would copy to different dictionaries in same fashion).

BoBo
  • Guests
  • Last active:
  • Joined: --
This dict didn't offer an import option ? What's the format of the dict file (which keeps the entries you c&p) ?

tomzi
  • Guests
  • Last active:
  • Joined: --

This dict didn't offer an import option ? What's the format of the dict file (which keeps the entries you c&p) ?

Sorry if I wasn't clear. :oops:

Well - "Dict.exe" (just a "dummy" name) is only executable of some dictionary program. Immediately after it is oppened - has user option to input a word, and program at the same time displays explanation - without pressing Enter. Focus is set on input filed immediately after program is ran.
This is why I can simply copy a word -> switch to (or run) dictionary -> and paste to input field. I have more dictionaries (english, german ..) which are all from same vendor and all operate in the same way.

This all works perfect if I use simple hotkey - let's say "F2::", "F3::" etc. So nothing is wrong with:
Clipboard = 
Send ^c
IfWinExist, English dictionary
{
   WinActivate
   Send ^v 
}
else
{
   Run, C:\DICT\DICT.EXE 
   WinWait, English dictionary
   WinActivate
   Send ^v
}
Return

I just don't know how to implement this into .INI file. Or maybe - how to modify it to work instead of pure run command such as:

Cmd="c:\program files\upx.exe" -d %1 
, which is in mbirth's example.

tomzi
  • Members
  • 4 posts
  • Last active: Jun 10 2011 04:42 AM
  • Joined: 25 Jan 2006
Has no one any idea? :(

mbirth
  • Members
  • 40 posts
  • Last active: Jul 31 2008 11:12 AM
  • Joined: 03 Oct 2005
The thing is: The INI-file just contains configuration information. It was never meant to hold AHK-sourcecode.

To realize your idea, you would have to introduce three new options in the INI file:

[dict]
Cmd=dict.exe
WindowName=English dictionary
SendBefore=^c
SendAfter=^v

And the script should be changed like this around the Run-command:

IniRead TargDir, %IniFile%, %CurID%, StartPath, %empty% 
          IniRead StartMode, %IniFile%, %CurID%, StartMode, %empty% 
          IniRead WindowName, %IniFile%, %CurID%, WindowName, %empty% 
          IniRead SendBefore, %IniFile%, %CurID%, SendBefore, %empty% 
          IniRead SendAfter, %IniFile%, %CurID%, SendAfter, %empty% 
          Loop 9 
          { 
            CurPar := %A_Index% 
            StringReplace Cmd, Cmd, `%%A_Index%, %CurPar% 
          } 
          Send %SendBefore%
          If (WindowName <> "" and WinExist(WindowName) <> 0)
          {
            WinActivate %WindowName%
          }
          else
          {
            Run %Cmd%, %TargDir%, %StartMode%, PID
          }
          Send %SendAfter%
        } 
      }

This code is untested, but you should get the idea.

Cheers,
-mARKUS

tomzi
  • Members
  • 4 posts
  • Last active: Jun 10 2011 04:42 AM
  • Joined: 25 Jan 2006
Eureka! :lol:

I found different popup menus on this forum - one is from Rajat - but mbirth - yours is really neat. It can be easily modified. And it is the only one that looks like normal Windows menu & also each line is highlighted when mouse cursor is over it - nice.
I used exactly the same INI structure as you suggested, but I made little modification in Run command section:

          IniRead TargDir, %IniFile%, %CurID%, StartPath, %empty%
          IniRead StartMode, %IniFile%, %CurID%, StartMode, %empty%
          IniRead WindowName, %IniFile%, %CurID%, WindowName, %empty%
          IniRead SendBefore, %IniFile%, %CurID%, SendBefore, %empty%
          IniRead SendAfter, %IniFile%, %CurID%, SendAfter, %empty%
          Loop 9
          {
            CurPar := %A_Index%
            StringReplace Cmd, Cmd, `%%A_Index%, %CurPar%
          }
          Send %SendBefore%
          If (WindowName <> "" and WinExist(WindowName) <> 0)
          {
            WinActivate %WindowName%
          }
          else
          {
          Run %Cmd%, %TargDir%, %StartMode%, PID
 ->       WinWait %WindowName%       ;waits for program window
 ->       WinActivate %WindowName%   ;program window is activated
          }
          Send %SendAfter%
        }
      }
    }
That's all.

Thank you. And:
Dein post hat mir richtig viel geholfen. Ich verstehe jetzt besser wie AHK code sieht aus. (English: Your post helped me a lot. I understand now better the AHK code structure)

jchristl
  • Members
  • 42 posts
  • Last active: Apr 03 2016 10:27 PM
  • Joined: 13 Jan 2006
This script is awesome.

Just one question: Why is it slower when clicked on with the mouse, whereas if called by hitting enter, or calling it with a hotkey, it runs faster?

mbirth
  • Members
  • 40 posts
  • Last active: Jul 31 2008 11:12 AM
  • Joined: 03 Oct 2005

Just one question: Why is it slower when clicked on with the mouse, whereas if called by hitting enter, or calling it with a hotkey, it runs faster?


I don't know. Maybe it's a strange thing with Windows or so....

Cheers,
-mARKUS

horst
  • Guests
  • Last active:
  • Joined: --
I would like to define the cmd to execute with enviroment variables.
For example:
cmd=%programfiles%\myprog\myprog.exe

This way the ini file would be portable between different windows versions.
I have English and German Windows and use Total Commander from an USB Stick.

Eidan
  • Members
  • 1 posts
  • Last active: Jul 11 2012 01:32 PM
  • Joined: 11 Jul 2012
Hello there guys!
First of all thank you so much for this script. It's awesome!!!

I got a problem
First: I have a ini like this:

[Menu]
;Iniziare il menù con 1
;I valori vuoti saranno ignorati.
MaxItem=99
Title=CGM UTILITIES
1=Chiama Numero Fisso
2=Chiama Numero Cellulare
3=Chiama Numero Fisso ASL
4=Chiama Numero Cellulare ASL
5=Pausa Phonebar
6=Parziale Mancato Contatto
7=Parziale Mancato Contatto AUTO
8=Chiama Andrea Melis
9=Wiki
10=Generazione Codice LogMeIn
11=Recall
12=Sblocco Smart Card

[Chiama Numero Fisso]
Cmd=Chiama Numero Fisso.ahk

[Chiama Numero Cellulare]
Cmd=Chiama Numero Cellulare.ahk

[Chiama Numero Fisso ASL]
Cmd=Chiama Numero Fisso ASL.ahk

[Chiama Numero Cellulare ASL]
Cmd=Chiama Numero Cellulare ASL.ahk

[Pausa Phonebar]
Cmd=Pausa Phonebar.ahk

[Parziale Mancato Contatto]
Cmd=Parziale Mancato Contatto.ahk

[Parziale Mancato Contatto AUTO]
Cmd=Parziale Mancato Contatto AUTO.ahk

[Wiki]
Cmd=Wiki.ahk

[Generazione Codice LogMeIn]
Cmd=Generazione Codice LogMeIn.ahk

[Chiama Andrea Melis]
Cmd=Chiama Andrea Melis.ahk

[Recall]
Cmd=Recall.ahk

[Sblocco Smart Card]
Cmd=Sblocco Smart Card.ahk

I set a shortcut in Autohotkey to execute your code, but everytime I use the shorcut it launches the menu and add a line after the menu...and it gets growing. If I use no title at all this does not happen! Where am I wrong?
If I remove this string at the bottom:

Menu %Menu%, Add, %MenuTitle%, AboutBox
Menu %Menu%, Default, %MenuTitle%
; Menu %Menu%, Disable, %MenuTitle%
Menu %Menu%, Add

I just got the title in bold with no line after it but it solves the issue i described above.
If I reload the script in Autohotkey the problem does not occur.

Thank you!

Zvonko
  • Members
  • 173 posts
  • Last active: Jan 24 2016 05:21 PM
  • Joined: 17 Oct 2014

A fantastic script, mbirth!

I can’t believe it, this is exactly what I am searching for since …

 

I am a newbie here and have not really ambitions to learn everything about AHK. But I am trying to learn all what is useful for my project (portable apps …), because I see that AHK is an incredibly mighty instrument.

 

Since mbirth’s script is exactly what I need, I tried to implement some changes for my project. However, I think that other newbies could benefit from these changes to. (See below.)

 

First of all:

Eidan:

 

I set a shortcut in Autohotkey to execute your code, but everytime I use the shorcut it launches the menu and add a line after the menu...and it gets growing.

 

I think the line marked with @@@! solves this problem.

 

Working on this problem, I discovered something amazing:

Instead of “-” for separator lines also text can be used. By doing this mbirth’s script can produce a kind of extended message boxes. Two examples:

 

...

Title=Which app should be used to open the file?
1=... your favorite one:
2=LibreOfficeWriter
3=... or maybe:
4=MSWord
5=... or simply:
6=Inedita 

[LibreOfficeWriter]
Cmd="E:\Butch!!!\PortableApps\LibreOfficePortable\LibreOfficeWriterPortable.exe"
[MSWord]
Cmd="E:\Butch!!!\PortableApps\Office97\MS Word 97.exe"
[Inedita]
Cmd="E:\Butch!!!\PortableApps\Inedita\Inedita.exe"

***

Title=Which app should be used to open the file? 
1=Your favorite one: 
2=... LibreOfficeWriter 
3=Or maybe: 
4=... MSWord 
5=Or simply: 
6=... Inedita

[... LibreOfficeWriter]
Cmd="E:\Butch!!!\PortableApps\LibreOfficePortable\LibreOfficeWriterPortable.exe"
[... MSWord]
Cmd="E:\Butch!!!\PortableApps\Office97\MS Word 97.exe"
[... Inedita]
Cmd="E:\Butch!!!\PortableApps\Inedita\Inedita.exe"
...
 

Try it, it looks very good! I really think that this simple but efficient way to design configurable message boxes (as alternative to MsgBox or other complicated solutions) deserves an enormous attention! 

 

Furthermore, I adapted the script to make the PopupMenu portable!

The relevant lines are marked with @@@P.

Now a path as defined in the ini with a specific drive, e.g.

            Cmd="E:\Butch!!!\PortableApps\Inedita\Inedita.exe"

is changed by the script as necessary for the portable use of the app, e.g.

            Cmd="X:\Butch!!!\PortableApps\Inedita\Inedita.exe"

 

I also adapted the positioning of the PopupMenu.

 

The adapted script is working perfectly in my case, however, as a newbie, I am not sure whether all AHK coding details are correct or not. I marked some lines with @@@?, hoping that an expert will review and edit this adaptation of mbirth’s cool script.

#NoEnv
#SingleInstance, force	

; =======================================================================
									; @@@P PORTABILITY ISSUE:
; domicileD = C								; This would be needed most probably for non-portable use.  
; domicileD = X								; Here you could define (manually) the drive letter of the
									; currently used drive for your portable apps, 
									; e.g. X if your USB stick currently has this letter.

									; @@@P However, I am using environmental variables.
EnvGet,domicileD,domicileD						; So domicileD can be imported automatically.
global domicileD := domicileD 						; Continued below at @@@P*
									; BUT, I still have problems with getting e.v.
									; because they became empty sometimes ...
						

; =======================================================================

#NoTrayIcon							


; Begin of the Hotkey section ===========================================

^+p::									; @@@ Hotkey Ctrl-Shift-P, as example.

SplitPath A_ScriptFullPath, null, IniDir, null, IniFile, null
IniFile = %IniDir%\%IniFile%.ini
IfNotExist %IniFile%
{
  MsgBox Could not find %IniFile%.
;  ExitApp								; @@@? Use in exe
}

ProcessMenu("Menu")

Menu Menu, Color, FFFFF0 		
	CoordMode, Menu, Screen						; @@@ To position the upper left corner of the menu ......
	WinGetPos,,,Xmax,Ymax,ahk_class Progman  ; get desktop size	
	Xcenter := Xmax/2        
	Ycenter := Ymax/2

Menu Menu, Show, %Xcenter%, %Ycenter%					; ...... in the center of the screen

Menu Menu, Delete							; @@@! NEEDED TO AVOID GROWING OF MENU IF NOT RESTARTED EACH TIME!!!

Exit

return									


; End of the Hotkey section =============================================


; Functions and Labels ==================================================


SplitFirst(ByRef OutLeft, ByRef OutRight, InpText, InpSep)
{
  StringGetPos SepPos, InpText, %InpSep%
  If (SepPos >= 0)
  {
    StringLeft OutLeft, InpText, %SepPos%
    RemChars := StrLen(InpText)-SepPos-1
    StringRight OutRight, InpText, %RemChars%
  }
  Else
  {
    OutLeft  := InpText
    OutRight := ""
  }
}


ProcessMenu(Menu)
{
  global IniFile
  IniRead MenuTitle, %IniFile%, %Menu%, Title
  If (MenuTitle <> "ERROR")
  {
    Menu %Menu%, Add, %MenuTitle%, AboutBox
    Menu %Menu%, Default, %MenuTitle%
    Menu %Menu%, Add
  }
  
  IniRead Count, %IniFile%, %Menu%, MaxItem, 0
  Loop %Count%
  {
    IniRead, CurItem, %IniFile%, %Menu%, %A_Index%
    If (CurItem = "-")
      Menu %Menu%, Add
    Else If (CurItem <> "ERROR")
    {
      SplitFirst(CurID, CurTitle, CurItem, "|")
      If (CurTitle = "")
        CurTitle := CurID
      
      IniRead IsSub, %IniFile%, %CurID%, MaxItem, 0
      If (IsSub = 0)
      {
        Menu %Menu%, Add, %CurTitle%, ProcessEvent
        IniRead CurCmd, %IniFile%, %CurID%, Cmd
        If (CurCmd = "ERROR")
          Menu %Menu%, Disable, %CurTitle%
      }
      else
      {
        ProcessMenu(CurID)
        Menu %Menu%, Add, %CurTitle%, :%CurID%
      }
      IniRead IsChecked, %IniFile%, %CurID%, Checked, 0
      If (IsChecked = 1)
        Menu %Menu%, Check, %CurTitle%
    }
  }
  Return
}									

ProcessEvent:
  IniRead Count, %IniFile%, %A_ThisMenu%, MaxItem, 0
  Loop %Count%
  {
    IniRead, CurItem, %IniFile%, %A_ThisMenu%, %A_Index%
    If (CurItem <> "ERROR")
    {
      SplitFirst(CurID, CurTitle, CurItem, "|")
      If (CurTitle = "")
        CurTitle := CurID
          
      If (CurTitle = A_ThisMenuItem)
      {
        IniRead Cmd, %IniFile%, %CurID%, Cmd
        If (Cmd <> "ERROR")
        {
          empty := ""
          IniRead TargDir, %IniFile%, %CurID%, StartPath, %empty%
          IniRead StartMode, %IniFile%, %CurID%, StartMode, %empty%
          Loop 9
          {
            CurPar := %A_Index%
            StringReplace Cmd, Cmd, `%%A_Index%, %CurPar%
          }

	  StringTrimLeft, Cmd, Cmd, 1					; @@@P* PORTABILITY ISSUE: Here the drive letter (occurring in
	  Cmd = %domicileD%%Cmd%					; the ini under Cmd and StartPath) is replaced by the letter
	  StringTrimLeft, TargDir, TargDir, 1				; of the currently used drive.
	  TargDir = %domicileD%%TargDir%	

          Run %Cmd%, %TargDir%, %StartMode%, PID

;	  WinWait %WindowName%       ;waits for program window		; @@@? Added by tomzi!!! Not good!!!(?) Seems to wait and wait and ...
          WinActivate %WindowName%   ;program window is activated	; @@@? Added by tomzi!!! Is it useful?
        }
      }
    }
  }
  
Return

AboutBox:
  MsgBox 4160, About ClickMenu..., ClickMenu shows a popup menu with entries read from an INI file and launches the selected program upon click.`n`nMade in 2005 by Markus Birth <mbirth at webwriters.de>
Return