1
votes

Here is part of my script. It runs a program by a hotkey if it's not running or shows/hides its window in other case.

ConsolePath := "cmd.exe"
ConsoleWndClass := "ConsoleWindowClass"
CalculatorPath := "calc.exe"
CalculatorWndClass := "CalcFrame"

#s::
  RunOrToggleActive(ConsolePath, ConsoleWndClass)
return

#c:: 
  RunOrToggleActive(CalculatorPath, CalculatorWndClass)
return

RunOrToggleActive(path, wndClass) {
  SplitPath, path, process
  Process, Exist, %process%
  If !ErrorLevel {
    Run, %path%
  }
  else {
    ToggleActive(wndClass)
  }
}

ToggleActive(wndClass)
{
  IfWinNotActive, % "ahk_class " wndClass 
  {
    WinActivate, % "ahk_class " wndClass
  }
  else 
  {
    WinMinimize, % "ahk_class " wndClass
  }
}

Works fine, but the problem with such an approach is that adding new programs and hotkeys is very laborious. Have to add 2 variables, copy hotkey handler code, replace hotkey, replace variables. If I would like to add new hotkey function for each program (like !#s and !#c to run another instance of program even if it's running already) I will have to repeat new code again. My actual script has 7 programs and is very hard to edit.

I'd like it to work like this (half pseudocode):

appDescs := Object()
appDescs.Insert(new ProgramDesc("cmd.exe", "ConsoleWindowClass", "s"))
appDescs.Insert(new ProgramDesc("calc.exe", "CalcFrame", "c"))


#"some key"{
  find key in appDescs array and RunOrToggleActive(for correspondig program)
}

!#"some key"{
  find key in appDescs array and RunNewInstance(for correspondig program)
}

RunOrToggleActive(programDesc) {
  ...
}

RunNewInstance(programDesc) {
  ...
}

Class ProgramDesc {
  __New(path, wndClass, key) {
      this.path := path, this.wndClass := wndClass, this.key := key
   }
}

I don't know how to implement #"some key"{ behavior. If someone would rewrite the code according to the pattern above (or suggest a better one) I'd be very grateful.

3

3 Answers

1
votes

You can use the Hotkey command http://ahkscript.org/docs/commands/Hotkey.htm and use it to have all hotkeys jump to a label or call a function. There you can use the built-in variable A_ThisHotkey to find out which hotkey was pressed and find the other data you need.

With labels:

appDescs := Object()
appDescs.Insert(new ProgramDesc("cmd.exe", "ConsoleWindowClass", "s"))
appDescs.Insert(new ProgramDesc("calc.exe", "CalcFrame", "c"))

for k, v in appDescs {
     Hotkey, % "#" v.key, RunOrToggleActive
     Hotkey, % "!#" v.key, RunNewInstance
    }

RunOrToggleActive:
; ....
Return

RunNewInstance:
; ....
Return

With functions

appDescs := Object()
appDescs.Insert(new ProgramDesc("cmd.exe", "ConsoleWindowClass", "s"))
appDescs.Insert(new ProgramDesc("calc.exe", "CalcFrame", "c"))

fn1 := Func("RunOrToggleActive")
fn2 := Func("RunNewInstance")

for k, v in appDescs {
     Hotkey, % "#" v.key, % fn1, on
     Hotkey, % "!#" v.key, % fn2, on
    }

RunOrToggleActive() {
; ....
}

RunNewInstance() {
; ....
}

Edit: if you prefer the functions version this would be the complete script.

#SingleInstance, force

appDescs := Object()
appDescs.Insert(new ProgramDesc("cmd.exe", "ConsoleWindowClass", "s"))
appDescs.Insert(new ProgramDesc("calc.exe", "CalcFrame", "c"))

fn1 := Func("RunOrToggleActive").Bind(appDescs)
fn2 := Func("RunNewInstance").Bind(appDescs)

for k, v in appDescs {
     Hotkey, % "#" v.key, % fn1, on
     Hotkey, % "!#" v.key, % fn2, on
    }

RunOrToggleActive(o) {
    for k, v in o
        If ("#" v.key = A_ThisHotkey)
            path:=v.path,wndClass:=v.wndClass

    SplitPath, path, process
    Process, Exist, %process%
    If !ErrorLevel {
        Run, %path%
    }
    else {
        ToggleActive(wndClass)
    }
 }

RunNewInstance(o) {
    for k, v in o
        If ("!#" v.key = A_ThisHotkey)
            Run % v.path
}

ToggleActive(wndClass)
{
  IfWinNotActive, % "ahk_class " wndClass 
  {
    WinActivate, % "ahk_class " wndClass
  }
  else 
  {
    WinMinimize, % "ahk_class " wndClass
  }
}

Class ProgramDesc {
  __New(path, wndClass, key) {
      this.path := path, this.wndClass := wndClass, this.key := key
   }
}
1
votes

Damn it, lintalist was faster. :D
Well, here is what I came up with:

AddWinToggle("#s","cmd.exe","ConsoleWindowClass") ;toggle hotkey
AddRunHotkey("#c","calc.exe") ;hotkey that starts a new instance each time
;RemoveHotkey("#s") ;remove a hoktey

AddWinToggle(hk, path, wndClass) {
    fn := func("RunOrToggleActive").Bind(path,wndClass)
    Hotkey, % hk, % fn
    If !ErrorLevel
        Return True
}    
AddRunHotkey(hk, path) {
    fn := func("RunNew").Bind(path,wndClass)
    Hotkey, % hk, % fn
    If !ErrorLevel
        Return True
}
RemoveHotkey(hotkey) {
    Hotkey, %hotkey%, Off
    If !ErrorLevel
        Return True
}

RunNew(path) {
    Run, % path
}
RunOrToggleActive(path, wndClass) {
    SplitPath, path, process
    Process, Exist, %process%
    If !ErrorLevel 
        Run, %path%
    Else
        ToggleActive(wndClass)
}
ToggleActive(wndClass) {
    IfWinNotActive, % "ahk_class " wndClass
        WinActivate, % "ahk_class " wndClass
    Else
        WinMinimize, % "ahk_class " wndClass
}
0
votes

Here is my solution though I like lintalist's one better.

global appDescs := Object()
appDescs.Insert(new AppDesc("n", "Notepad.exe", "Notepad"))
appDescs.Insert(new AppDesc("s", "cmd.exe", "ConsoleWindowClass"))
appDescs.Insert(new AppDesc("c", "calc.exe", "CalcFrame"))

#n::
#s::
#c::
  stringsplit, splitted_, A_ThisHotkey
  appDesc := AppDescByKey(splitted_2)
  RunOrToggleApp(appDesc)
return

RunOrToggleApp(appDesc) {
  DetectHiddenWindows, on
  path := appDesc.path
  SplitPath, path, process
  Process, Exist, %process%
  If !ErrorLevel {
    Run, %path%
  }
  else {
    ToggleActive(appDesc.wndClass)
  }
}

ToggleActive(wndClass)
{
  IfWinNotActive, % "ahk_class " wndClass 
  {
    WinActivate, % "ahk_class " wndClass
  }
  else
  {
    WinMinimize, % "ahk_class " wndClass
  }
}

AppDescByKey(key) {
  for i, appDesc in appDescs {
    if (key = appDesc.key) {
      return appDesc
    }
  }
}

Class AppDesc {
  __New(key, path, wndClass) {
    this.key := key, this.path := path, this.wndClass := wndClass
  }
}