Advertisement
aikikode

iswitchw - Incrementally switch between windows using substr

Sep 21st, 2012
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ;
  2. ; iswitchw - Incrementally switch between windows using substrings
  3. ;
  4. ; [MODIFIED by ezuk, 3 July 2008, changes noted below. Cosmetics only.]
  5. ;
  6. ; Required AutoHotkey version: 1.0.25+
  7. ;
  8. ; When this script is triggered via its hotkey the list of titles of
  9. ; all visible windows appears. The list can be narrowed quickly to a
  10. ; particular window by typing a substring of a window title.
  11. ;
  12. ; When the list is narrowed the desired window can be selected using
  13. ; the cursor keys and Enter. If the substring matches exactly one
  14. ; window that window is activated immediately (configurable, see the
  15. ; "autoactivateifonlyone" variable).
  16. ;
  17. ; The window selection can be cancelled with Esc.
  18. ;
  19. ; The switcher window can be moved horizontally with the left/right
  20. ; arrow keys if it blocks the view of windows under it.
  21. ;
  22. ; The switcher can also be operated with the mouse, although it is
  23. ; meant to be used from the keyboard. A mouse click activates the
  24. ; currently selected window. Mouse users may want to change the
  25. ; activation key to one of the mouse keys.
  26. ;
  27. ; If enabled possible completions are offered when the same unique
  28. ; substring is found in the title of more than one window.
  29. ;
  30. ; For example, the user typed the string "co" and the list is
  31. ; narrowed to two windows: "Windows Commander" and "Command Prompt".
  32. ; In this case the "command" substring can be completed automatically,
  33. ; so the script offers this completion in square brackets which the
  34. ; user can accept with the TAB key:
  35. ;
  36. ;     co[mmand]
  37. ;
  38. ; This feature can be confusing for novice users, so it is disabled
  39. ; by default.
  40. ;
  41. ;
  42. ; For the idea of this script the credit goes to the creators of the
  43. ; iswitchb package for the Emacs editor
  44. ;
  45. ;
  46. ;----------------------------------------------------------------------
  47. ;
  48. ; User configuration
  49. ;
  50.  
  51. ; set this to yes if you want to select the only matching window
  52. ; automatically
  53. autoactivateifonlyone =
  54.  
  55. ; set this to yes if you want to enable tab completion (see above)
  56. ; it has no effect if firstlettermatch (see below) is enabled
  57. tabcompletion = yes
  58.  
  59. ; set this to yes to enable digit shortcuts when there are ten or
  60. ; less items in the list
  61. digitshortcuts =
  62.  
  63. ; set this to yes to enable first letter match mode where the typed
  64. ; search string must match the first letter of words in the
  65. ; window title (only alphanumeric characters are taken into account)
  66. ;
  67. ; For example, the search string "ad" matches both of these titles:
  68. ;
  69. ;  AutoHotkey - Documentation
  70. ;  Anne's Diary
  71. ;
  72. firstlettermatch =
  73.  
  74. ; set this to yes to enable activating the currently selected
  75. ; window in the background
  76. activateselectioninbg =
  77.  
  78. ; number of milliseconds to wait for the user become idle, before
  79. ; activating the currently selected window in the background
  80. ;
  81. ; it has no effect if activateselectioninbg is off
  82. ;
  83. ; if set to blank the current selection is activated immediately
  84. ; without delay
  85. bgactivationdelay = 300
  86.  
  87. ; show process name before window title.
  88. showprocessname = yes
  89.  
  90. ; Close switcher window if the user activates an other window.
  91. ; It does not work well if activateselectioninbg is enabled, so
  92. ; currently they cannot be enabled together.
  93. closeifinactivated =
  94.  
  95. if activateselectioninbg <>
  96.     if closeifinactivated <>
  97.     {
  98.         msgbox, activateselectioninbg and closeifinactivated cannot be enabled together
  99.         exitapp
  100.     }
  101.  
  102. ; List of subtsrings separated with pipe (|) characters (e.g. carpe|diem).
  103. ; Window titles containing any of the listed substrings are filtered out
  104. ; from the list of windows.
  105. filterlist = asticky|blackbox
  106.  
  107. ; Set this yes to update the list of windows every time the contents of the
  108. ; listbox is updated. This is usually not necessary and it is an overhead which
  109. ; slows down the update of the listbox, so this feature is disabled by default.
  110. dynamicwindowlist =
  111.  
  112. ; path to sound file played when the user types a substring which
  113. ; does not match any of the windows
  114. ;
  115. ; set this to blank if you don't want a sound
  116. ;
  117. ;nomatchsound = %windir%\Media\ding.wav
  118. nomatchsound =
  119.  
  120. if nomatchsound <>
  121.     ifnotexist, %nomatchsound%
  122.         msgbox, Sound file %nomatchsound% not found. No sound will be played.
  123.  
  124. ;----------------------------------------------------------------------
  125. ;
  126. ; Global variables
  127. ;
  128. ;     numallwin      - the number of windows on the desktop
  129. ;     allwinarray    - array containing the titles of windows on the desktop
  130. ;                      dynamicwindowlist is disabled
  131. ;     allwinidarray  - window ids corresponding to the titles in allwinarray
  132. ;     numwin         - the number of windows in the listbox
  133. ;     idarray        - array containing window ids for the listbox items
  134. ;     orig_active_id - the window ID of the originally active window
  135. ;                      (when the switcher is activated)
  136. ;     prev_active_id - the window ID of the last window activated in the
  137. ;                      background (only if activateselectioninbg is enabled)
  138. ;     switcher_id    - the window ID of the switcher window
  139. ;     filters        - array of filters for filtering out titles
  140. ;                      from the window list
  141. ;
  142. ;----------------------------------------------------------------------
  143.  
  144. AutoTrim, off
  145.  
  146. ;this section modified by ezuk, 03 July 2008
  147. Gui, +LastFound +AlwaysOnTop -Caption  
  148. Gui, Color, black,black
  149. WinSet, Transparent, 180
  150. Gui,Font,s15 cYellow bold,Calibri
  151. Gui, Add, ListBox, vindex gListBoxClick x-2 y-2 w810 h602 AltSubmit -VScroll
  152. ;end of modifications by ezuk
  153.  
  154. if filterlist <>
  155. {
  156.     loop, parse, filterlist, |
  157.     {
  158.         filters%a_index% = %A_LoopField%
  159.     }
  160. }
  161.  
  162. ;----------------------------------------------------------------------
  163. ;
  164. ; I never use the CapsLock key, that's why I chose it.
  165. ;
  166. CapsLock::
  167.  
  168. search =
  169. numallwin = 0
  170. GuiControl,, Edit1
  171. GoSub, RefreshWindowList
  172.  
  173. WinGet, orig_active_id, ID, A
  174. prev_active_id = %orig_active_id%
  175.  
  176. Gui, Show, Center h600 w800, Window Switcher
  177. ; If we determine the ID of the switcher window here then
  178. ; why doesn't it appear in the window list when the script is
  179. ; run the first time? (Note that RefreshWindowList has already
  180. ; been called above).
  181. ; Answer: Because when this code runs first the switcher window
  182. ; does not exist yet when RefreshWindowList is called.
  183. WinGet, switcher_id, ID, A
  184. WinSet, AlwaysOnTop, On, ahk_id %switcher_id%
  185.  
  186. Loop
  187. {
  188.     if closeifinactivated <>
  189.         settimer, CloseIfInactive, 200
  190.  
  191.     Input, input, L1, {enter}{esc}{backspace}{up}{down}{pgup}{pgdn}{tab}{left}{right}
  192.  
  193.     if closeifinactivated <>
  194.         settimer, CloseIfInactive, off
  195.  
  196.     if ErrorLevel = EndKey:enter
  197.     {
  198.         GoSub, ActivateWindow
  199.         break
  200.     }
  201.  
  202.     if ErrorLevel = EndKey:escape
  203.     {
  204.         Gui, cancel
  205.  
  206.         ; restore the originally active window if
  207.         ; activateselectioninbg is enabled
  208.         if activateselectioninbg <>
  209.             WinActivate, ahk_id %orig_active_id%
  210.  
  211.         break
  212.     }
  213.  
  214.     if ErrorLevel = EndKey:backspace
  215.     {
  216.         GoSub, DeleteSearchChar
  217.         continue
  218.     }
  219.  
  220.     if ErrorLevel = EndKey:tab
  221.         if completion =
  222.             continue
  223.         else
  224.             input = %completion%
  225.  
  226.     ; pass these keys to the selector window
  227.  
  228.     if ErrorLevel = EndKey:up
  229.     {
  230.         Send, {up}
  231.         GoSuB ActivateWindowInBackgroundIfEnabled
  232.         continue
  233.     }
  234.  
  235.     if ErrorLevel = EndKey:down
  236.     {
  237.         Send, {down}
  238.         GoSuB ActivateWindowInBackgroundIfEnabled
  239.         continue
  240.     }
  241.  
  242.     if ErrorLevel = EndKey:pgup
  243.     {
  244.         Send, {pgup}
  245.  
  246.         GoSuB ActivateWindowInBackgroundIfEnabled
  247.         continue
  248.     }
  249.  
  250.     if ErrorLevel = EndKey:pgdn
  251.     {
  252.         Send, {pgdn}
  253.         GoSuB ActivateWindowInBackgroundIfEnabled
  254.         continue
  255.     }
  256.  
  257.     if ErrorLevel = EndKey:left
  258.     {
  259.         direction = -1
  260.         GoSuB MoveSwitcher
  261.         continue
  262.     }
  263.  
  264.     if ErrorLevel = EndKey:right
  265.     {
  266.         direction = 1
  267.         GoSuB MoveSwitcher
  268.         continue
  269.     }
  270.  
  271.     ; FIXME: probably other error level cases
  272.     ; should be handled here (interruption?)
  273.  
  274.     ; invoke digit shortcuts if applicable
  275.     if digitshortcuts <>
  276.         if numwin <= 10
  277.             if input in 1,2,3,4,5,6,7,8,9,0
  278.             {
  279.                 if input = 0
  280.                     input = 10
  281.  
  282.                 if numwin < %input%
  283.                 {
  284.                     if nomatchsound <>
  285.                         SoundPlay, %nomatchsound%
  286.                     continue
  287.                 }
  288.  
  289.                 GuiControl, choose, ListBox1, %input%
  290.                 GoSub, ActivateWindow
  291.                 break
  292.             }
  293.  
  294.     ; process typed character
  295.  
  296.     search = %search%%input%
  297.     GuiControl,, Edit1, %search%
  298.     GoSub, RefreshWindowList
  299. }
  300.  
  301. Gosub, CleanExit
  302.  
  303. return
  304.  
  305. ;----------------------------------------------------------------------
  306. ;
  307. ; Refresh the list of windows according to the search criteria
  308. ;
  309. ; Sets: numwin  - see the documentation of global variables
  310. ;       idarray - see the documentation of global variables
  311. ;
  312. RefreshWindowList:
  313.    ; refresh the list of windows if necessary
  314.  
  315.     if ( dynamicwindowlist = "yes" or numallwin = 0 )
  316.     {
  317.         numallwin = 0
  318.  
  319.         WinGet, id, list, , , Program Manager
  320.         Loop, %id%
  321.         {
  322.             StringTrimRight, this_id, id%a_index%, 0
  323.             WinGetTitle, title, ahk_id %this_id%
  324.  
  325.             ; FIXME: windows with empty titles?
  326.             if title =
  327.                 continue
  328.  
  329.             ; don't add the switcher window
  330.             if switcher_id = %this_id%
  331.                 continue
  332.  
  333.             ; show process name if enabled
  334.             if showprocessname <>
  335.             {
  336.                 WinGet, procname, ProcessName, ahk_id %this_id%
  337.  
  338.                 stringgetpos, pos, procname, .
  339.                 if ErrorLevel <> 1
  340.                 {
  341.                     stringleft, procname, procname, %pos%
  342.                 }
  343.  
  344.                 stringupper, procname, procname
  345.                 title = %procname%: %title%
  346.             }
  347.  
  348.             ; don't add titles which match any of the filters
  349.             if filterlist <>
  350.             {
  351.                 filtered =
  352.  
  353.                 loop
  354.                 {
  355.                     stringtrimright, filter, filters%a_index%, 0
  356.                     if filter =
  357.                       break
  358.                     else
  359.                         ifinstring, title, %filter%
  360.                         {
  361.                            filtered = yes
  362.                            break
  363.                         }
  364.                 }
  365.  
  366.                 if filtered = yes
  367.                     continue
  368.             }
  369.  
  370.             ; replace pipe (|) characters in the window title,
  371.             ; because Gui Add uses it for separating listbox items
  372.             StringReplace, title, title, |, -, all
  373.  
  374.             numallwin += 1
  375.             allwinarray%numallwin% = %title%
  376.             allwinidarray%numallwin% = %this_id%
  377.         }
  378.     }
  379.  
  380.     ; filter the window list according to the search criteria
  381.  
  382.     winlist =
  383.     numwin = 0
  384.  
  385.     Loop, %numallwin%
  386.     {
  387.         StringTrimRight, title, allwinarray%a_index%, 0
  388.         StringTrimRight, this_id, allwinidarray%a_index%, 0
  389.  
  390.         ; don't add the windows not matching the search string
  391.         ; if there is a search string
  392.         if search <>
  393.             if firstlettermatch =
  394.             {
  395.                 if title not contains %search%,
  396.                     continue
  397.             }
  398.             else
  399.             {
  400.                 stringlen, search_len, search
  401.  
  402.                 index = 1
  403.                 match =
  404.  
  405.                 loop, parse, title, %A_Space%
  406.                 {                  
  407.                     stringleft, first_letter, A_LoopField, 1
  408.  
  409.                     ; only words beginning with an alphanumeric
  410.                     ; character are taken into account
  411.                     if first_letter not in 1,2,3,4,5,6,7,8,9,0,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
  412.                         continue
  413.  
  414.                     stringmid, search_char, search, %index%, 1
  415.  
  416.                     if first_letter <> %search_char%
  417.                         break
  418.  
  419.                     index += 1
  420.  
  421.                     ; no more search characters
  422.                     if index > %search_len%
  423.                     {
  424.                         match = yes
  425.                         break
  426.                     }
  427.                 }
  428.  
  429.                 if match =
  430.                     continue    ; no match
  431.             }
  432.  
  433.         if winlist <>
  434.             winlist = %winlist%|
  435.         winlist = %winlist%%title%`r%this_id%
  436.  
  437.         numwin += 1
  438.         winarray%numwin% = %title%
  439.     }
  440.  
  441.     ; if the pattern didn't match any window
  442.     if numwin = 0
  443.         ; if the search string is empty then we can't do much
  444.         if search =
  445.         {
  446.             Gui, cancel
  447.             Gosub, CleanExit
  448.         }
  449.         ; delete the last character
  450.         else
  451.         {
  452.             if nomatchsound <>
  453.                 SoundPlay, %nomatchsound%
  454.  
  455.             GoSub, DeleteSearchChar
  456.             return
  457.         }
  458.  
  459.     ; sort the list alphabetically
  460.     Sort, winlist, D|
  461.  
  462.     ; add digit shortcuts if there are ten or less windows
  463.     ; in the list and digit shortcuts are enabled
  464.     if digitshortcuts <>
  465.         if numwin <= 10
  466.         {
  467.             digitlist =
  468.             digit = 1
  469.             loop, parse, winlist, |
  470.             {
  471.                 ; FIXME: windows with empty title?
  472.                 if A_LoopField <>
  473.                 {
  474.                     if digitlist <>
  475.                         digitlist = %digitlist%|
  476.                     digitlist = %digitlist%%digit%%A_Space%%A_Space%%A_Space%%A_LoopField%
  477.  
  478.                     digit += 1
  479.                     if digit = 10
  480.                         digit = 0
  481.                 }
  482.             }
  483.             winlist = %digitlist%
  484.         }
  485.  
  486.     ; strip window IDs from the sorted list
  487.     titlelist =
  488.     arrayindex = 1
  489.  
  490.     loop, parse, winlist, |
  491.     {
  492.         stringgetpos, pos, A_LoopField, `r
  493.  
  494.         stringleft, title, A_LoopField, %pos%
  495.         titlelist = %titlelist%|%title%
  496.  
  497.         pos += 2 ; skip the separator char
  498.         stringmid, id, A_LoopField, %pos%, 10000
  499.         idarray%arrayindex% = %id%
  500.         ++arrayindex
  501.     }
  502.  
  503.     ; show the list
  504.     GuiControl,, ListBox1, %titlelist%
  505.     GuiControl, Choose, ListBox1, 1
  506.  
  507.     if numwin = 1
  508.         if autoactivateifonlyone <>
  509.         {
  510.             GoSub, ActivateWindow
  511.             Gosub, CleanExit
  512.         }
  513.  
  514.     GoSub ActivateWindowInBackgroundIfEnabled
  515.  
  516.     completion =
  517.  
  518.     if tabcompletion =
  519.         return
  520.  
  521.     ; completion is not implemented for first letter match mode
  522.     if firstlettermatch <>
  523.         return
  524.  
  525.     ; determine possible completion if there is
  526.     ; a search string and there are more than one
  527.     ; window in the list
  528.  
  529.     if search =
  530.         return
  531.    
  532.     if numwin = 1
  533.         return
  534.  
  535.     loop
  536.     {
  537.         nextchar =
  538.  
  539.         loop, %numwin%
  540.         {
  541.             stringtrimleft, title, winarray%a_index%, 0
  542.  
  543.             if nextchar =
  544.             {
  545.                 substr = %search%%completion%
  546.                 stringlen, substr_len, substr
  547.                 stringgetpos, pos, title, %substr%
  548.  
  549.                 if pos = -1
  550.                     break
  551.  
  552.                 pos += %substr_len%
  553.  
  554.                 ; if the substring matches the end of the
  555.                 ; string then no more characters can be completed
  556.                 stringlen, title_len, title
  557.                 if pos >= %title_len%
  558.                 {
  559.                     pos = -1
  560.                     break
  561.                 }
  562.  
  563.                 ; stringmid has different position semantics
  564.                 ; than stringgetpos. strange...
  565.                 pos += 1
  566.                 stringmid, nextchar, title, %pos%, 1
  567.                 substr = %substr%%nextchar%
  568.              }
  569.              else
  570.              {
  571.                 stringgetpos, pos, title, %substr%
  572.                 if pos = -1
  573.                     break
  574.              }
  575.         }
  576.  
  577.         if pos = -1
  578.             break
  579.         else
  580.             completion = %completion%%nextchar%
  581.     }
  582.  
  583.     if completion <>
  584.         GuiControl,, Edit1, %search%[%completion%]
  585.  
  586. return
  587.  
  588. ;----------------------------------------------------------------------
  589. ;
  590. ; Delete last search char and update the window list
  591. ;
  592. DeleteSearchChar:
  593.  
  594. if search =
  595.     return
  596.  
  597. StringTrimRight, search, search, 1
  598. GuiControl,, Edit1, %search%
  599. GoSub, RefreshWindowList
  600.  
  601. return
  602.  
  603. ;----------------------------------------------------------------------
  604. ;
  605. ; Activate selected window
  606. ;
  607. ActivateWindow:
  608.  
  609. Gui, submit
  610. stringtrimleft, window_id, idarray%index%, 0
  611. WinActivate, ahk_id %window_id%
  612.  
  613. return
  614.  
  615. ;----------------------------------------------------------------------
  616. ;
  617. ; Activate selected window in the background
  618. ;
  619. ActivateWindowInBackground:
  620.  
  621. guicontrolget, index,, ListBox1
  622. stringtrimleft, window_id, idarray%index%, 0
  623.  
  624. if prev_active_id <> %window_id%
  625. {
  626.     WinActivate, ahk_id %window_id%
  627.     WinActivate, ahk_id %switcher_id%
  628.     prev_active_id = %window_id%
  629. }
  630.  
  631. return
  632.  
  633. ;----------------------------------------------------------------------
  634. ;
  635. ; Activate selected window in the background if the option is enabled.
  636. ; If an activation delay is set then a timer is started instead of
  637. ; activating the window immediately.
  638. ;
  639. ActivateWindowInBackgroundIfEnabled:
  640.  
  641. if activateselectioninbg =
  642.     return
  643.  
  644. ; Don't do it just after the switcher is activated. It is confusing
  645. ; if active window is changed immediately.
  646. WinGet, id, ID, ahk_id %switcher_id%
  647. if id =
  648.     return
  649.  
  650. if bgactivationdelay =
  651.     GoSub ActivateWindowInBackground
  652. else
  653.     settimer, BgActivationTimer, %bgactivationdelay%
  654.  
  655. return
  656.  
  657. ;----------------------------------------------------------------------
  658. ;
  659. ; Check if the user is idle and if so activate the currently selected
  660. ; window in the background
  661. ;
  662. BgActivationTimer:
  663.  
  664. settimer, BgActivationTimer, off
  665.  
  666. GoSub ActivateWindowInBackground
  667.  
  668. return
  669.  
  670. ;----------------------------------------------------------------------
  671. ;
  672. ; Stop background window activation timer if necessary and exit
  673. ;
  674. CleanExit:
  675.  
  676. settimer, BgActivationTimer, off
  677.  
  678. exit
  679.  
  680. ;----------------------------------------------------------------------
  681. ;
  682. ; Cancel keyboard input if GUI is closed.
  683. ;
  684. GuiClose:
  685.  
  686. send, {esc}
  687.  
  688. return
  689.  
  690. ;----------------------------------------------------------------------
  691. ;
  692. ; Handle mouse click events on the list box
  693. ;
  694. ListBoxClick:
  695. if (A_GuiControlEvent = "Normal"
  696.     and !GetKeyState("Down", "P") and !GetKeyState("Up", "P"))
  697.     send, {enter}
  698. return
  699.  
  700. ;----------------------------------------------------------------------
  701. ;
  702. ; Move switcher window horizontally
  703. ;
  704. ; Input: direction - 1 for right, -1 for left
  705. ;
  706. MoveSwitcher:
  707.  
  708. direction *= 100
  709. WinGetPos, x, y, width, , ahk_id %switcher_id%
  710. x += %direction%
  711.  
  712. if x < 0
  713.     x = 0
  714. else
  715. {
  716.    SysGet screensize, MonitorWorkArea
  717.    screensizeRight -= %width%
  718.    if x > %screensizeRight%
  719.       x = %screensizeRight%
  720. }
  721.  
  722. prevdelay = %A_WinDelay%
  723. SetWinDelay, -1
  724. WinMove, ahk_id %switcher_id%, , %x%, %y%
  725. SetWinDelay, %prevdelay%
  726.  
  727. return
  728.  
  729. ;----------------------------------------------------------------------
  730. ;
  731. ; Close the switcher window if the user activated an other window
  732. ;
  733. CloseIfInactive:
  734.  
  735. ifwinnotactive, ahk_id %switcher_id%
  736.     send, {esc}
  737.  
  738. return
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement