Jump to content

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

GroupAdd misbehaves when label is specified


  • Please log in to reply
3 replies to this topic
mklement
  • Members
  • 3 posts
  • Last active: Aug 14 2010 02:22 PM
  • Joined: 02 Jun 2010
Version: AutoHotkey_L, 1.0.48.05.L52
Platform (observed on): Windows 7, W2K3

When you specify a label to jump to in case no matching windows are found for the GroupAdd command, and the group comprises more than 1 one criterion, window activation fails intermittently when executing GroupActivate (i.e. no activation takes place even though it should, and the label is invoked even though it shouldn't).

Note that NOT specifying the label does NOT exhibit the symptom, i.e. activation works correctly (but you have no easy way of knowing whether activation took place).

The following code exhibits the problem on W7 if you do the following:
- Open any Explorer window
- Execute the code, and press Ctrl+Alt+up-arrow to activate the (next) open Explorer Window.

The code will erroneously indicate that no matching window was found.
Note that if you remove the first GroupAdd statement, the problem goes away. If you swap the two GroupAdd statements, it takes a few presses of Ctrl+Alt+up-arrow for the bug to surface.

GroupAdd, explorerWins, ahk_class ExploreWClass, , NO_WIN_IN_GROUP
GroupAdd, explorerWins, ahk_class CabinetWClass, , NO_WIN_IN_GROUP

^!up::
	GroupActivate, explorerWins, R
return

NO_WIN_IN_GROUP:
	msgbox No such window.
return


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

The label of a subroutine to run if no windows matching this specification exist when the GroupActivate command is used.
Source: GroupAdd

Case A: the active window doesn't match any specification in the group. GroupActivate attempts to find a window matching the first specification in the group. Since no windows matching that specification exist, it calls the label. Next time you use GroupActivate, the active window still doesn't match any specification in the group, so it does the same thing again.

Case B: the active window matches one of the specifications in the group. GroupActivate will attempt to activate the next window which matches that specification. If no more matching windows are found, it moves onto the next specification in the group. If no windows match that specification, it calls the label.

There's a difference between "this specification" and "this group" - i.e. if there really aren't any windows matching ahk_class ExploreWClass, the label is called. That is exactly what it is supposed to do. However, strictly speaking it should call the label, then continue and activate the next window in the group - but it doesn't.

Adding to the inconsistency, the label is only executed if windows are evaluated against the specification which defined it. For instance, if you have four specifications (with four different labels) and only the second matches an existing window, only the first label is executed in Case A and only the third is executed in Case B.

I think I have a solution (to only call any label if a full circuit of the window group has been completed), but it needs testing. One problem I can see is that since the label is jumped to (as in Goto, not Gosub), one particular label must take precedence over all the others (either the first or the last window spec to define a label).
The following script achieves the same effect:
GroupAdd, explorerWins, ahk_class ExploreWClass, , NO_WIN_IN_GROUP
GroupAdd, explorerWins, ahk_class CabinetWClass, , NO_WIN_IN_GROUP
 
^!up::
   GroupActivate, explorerWins, R
return
NO_WIN_IN_GROUP:
   IfWinActive ahk_group explorerWins
   {
      ; A window in the group is active. If there are any other matching
      ; windows, calling GroupActivate again should activate the next one.
      ; If there is only one matching window (the active window), we must
      ; not call GroupActivate since it excludes the active window; i.e.
      ; it would recursively call this subroutine, which would cause
      ; stack overflow (crash the script).
      WinGet, explorerWins, List, ahk_group explorerWins
      if explorerWins > 1
      {
         GroupActivate, explorerWins, R
         return
      }
      ; Otherwise there aren't any *other* windows to activate.
   }
   else IfWinExist ahk_group explorerWins
   {
      ; Since the active window is not a match, just activate the first
      ; matching window (which must match a window spec defined after
      ; the one this subroutine is for, otherwise this subroutine would
      ; not have been called).
      WinActivate
      return
   }
   ; I think you get the idea:
   MsgBox No such window.
return

Version: AutoHotkey_L, 1.0.48.05.L52

Confirmed on L53 and mainstream AHK v1.0.48.05.

mklement
  • Members
  • 3 posts
  • Last active: Aug 14 2010 02:22 PM
  • Joined: 02 Jun 2010
Thank you for responding quickly and thinking this through.

Actually, the way I conceived of the label feature was to apply to the group *as a whole*, i.e. to *all* specifications comprising the group.

Thus, I expected to only be able to specify *1* label per group, and expected GroupAdd to either report an error if an attempt was made to specify a different label on a subsequent GroupAdd call, or to make the subsequent label *the* (one and only) label for the group at hand.

But I see now, based on your post and re-reading the documentation page, that it was meant to apply to each individual specification.

It would be nice, however, to have an easy way to find out whether *any* match was found, e.g. by setting ErrorLevel.

Based on my conception I ended up with the following workaround; inefficient, but it works:

; Assumption: NO label was specified during GroupAdd

hWndActiveBefore := WinExist("A")	; Get currently active window

GroupActivate, %groupName%, R   ; try to activate; nothing will happen if no match is found.
	
		; Determine if a matching window was activated, and, if not, whether it was because (a) none was found, or (b) the only matching window was already active.
hWndActiveNow := WinExist("A")
fNoChange := hWndActiveNow = hWndActiveBefore	; Did GroupActivate actually cause a different window to become active?
	fNoMatchingWin := fNoChange && !WinActive("ahk_group" . groupName)


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Actually, the way I conceived of the label feature was to apply to the group *as a whole*, i.e. to *all* specifications comprising the group.

I expected the same when I first read about GroupAdd/GroupActivate. It probably should be that way.

Thus, I expected to only be able to specify *1* label per group, and expected GroupAdd to either report an error if an attempt was made to specify a different label on a subsequent GroupAdd call, or to make the subsequent label *the* (one and only) label for the group at hand.

Currently each window spec has its own label, but which one actually gets called depends on the active window and the order of window specs in the group. Associating the label with the window group instead of the window spec would simplify the code (of my patch at least), reduce memory usage slightly and make the behaviour more consistent. Thanks for bringing it up.

It would be nice, however, to have an easy way to find out whether *any* match was found, e.g. by setting ErrorLevel.

I agree. I wonder why it wasn't done that way to begin with. The current method is rather unusual and not very easy to use by comparison. Additionally, the label passed to GroupAdd is irrelevant to the majority of commands that window groups can be used with (i.e. every command that can accept a window title).