Hi Mikhael, :)

Find attached a patch that includes the WindowList tracker &
updated FvwmWindowMenu module which actually utilises the new
tracker.

Comments most welcome.

SCoTT. :)
Index: modules/ChangeLog
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/ChangeLog,v
retrieving revision 1.1110
diff -u -r1.1110 ChangeLog
--- modules/ChangeLog   1 Jun 2004 11:46:54 -0000       1.1110
+++ modules/ChangeLog   2 Jun 2004 11:28:48 -0000
@@ -1,3 +1,10 @@
+2004-06-01  Scott Smedley  <[EMAIL PROTECTED]>
+
+       * FvwmWindowMenu/FvwmWindowMenu.in:
+       * FvwmWindowMenu/FvwmWindowMenu.1:
+       Rewrote module to use new WindowList tracker.
+       Updated man page.
+
 2004-06-01  Dominik Vogt  <[EMAIL PROTECTED]>
 
        * FvwmConsole/FvwmConsoleC.c (main):
Index: modules/FvwmWindowMenu/FvwmWindowMenu.1
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/FvwmWindowMenu/FvwmWindowMenu.1,v
retrieving revision 1.2
diff -u -r1.2 FvwmWindowMenu.1
--- modules/FvwmWindowMenu/FvwmWindowMenu.1     12 Nov 2003 21:27:41 -0000      
1.2
+++ modules/FvwmWindowMenu/FvwmWindowMenu.1     2 Jun 2004 11:28:50 -0000
@@ -128,8 +128,8 @@
 .rm #[ #] #H #V #F C
 .\" ========================================================================
 .\"
-.IX Title "FVWMWINDOWMENU 1"
-.TH FVWMWINDOWMENU 1 "2003-11-12" "perl v5.8.1" "FVWM Modules"
+.IX Title "FVWMWINDOWMENU.IN 1"
+.TH FVWMWINDOWMENU.IN 1 "2004-06-02" "perl v5.8.1" "FVWM Module"
 .SH "NAME"
 FvwmWindowMenu \- open configurable fvwm menu listing current windows
 .SH "SYNOPSIS"
@@ -149,13 +149,14 @@
 module does not draw its own window, but instead creates an
 \&\fIfvwm\fR menu and asks \fIfvwm\fR to pop it up.
 .PP
-By defining a set of regular expressions using Show, windows may
-be sorted into sections based on the regexp matching their
-name, icon name, class or resource.
+By defining a set of regular expressions, windows may
+be sorted into sections based on a regexp matching the window
+name, class or resource and included in the menu.
 .PP
-Similarly, matches in DontShow will be excluded from the list.
+Similarly, another set of regular expressions can be used to exclude
+items from the menu.
 .PP
-Any windows not matching an instance of Show or DontShow will
+Any windows not matching an instance of the include or exclude list will
 be placed in the last section of the menu.
 .SH "USAGE"
 .IX Header "USAGE"
@@ -186,41 +187,44 @@
 \&\fBMenu\fR and \fBPopup\fR. If the module was started with \*(L"\-g\*(R" 
switch, it
 additionally supports \fBPostBar\fR (not implemented yet).
 .PP
-Set module options for windows to show or not show. The syntax is:
+Set module options for windows to include (Show) or exclude (DontShow).
+The syntax is:
 .PP
-.Vb 2
-\&    *FvwmWindowMenu: Show type = pattern
-\&    *FvwmWindowMenu: DontShow type = pattern
+.Vb 6
+\&    *FvwmWindowMenu: ShowName pattern
+\&    *FvwmWindowMenu: ShowClass pattern
+\&    *FvwmWindowMenu: ShowResource pattern
+\&    *FvwmWindowMenu: DontShowName pattern
+\&    *FvwmWindowMenu: DontShowClass pattern
+\&    *FvwmWindowMenu: DontShowResource pattern
 .Ve
 .PP
-where type is one of \fIname\fR, \fIicon\fR, \fIclass\fR or \fIresource\fR. 
Pattern is
-a perl regular expression that will be evaluated in m// context.
+Pattern is a perl regular expression that will be evaluated in m// context.
 See \fIperlre\fR\|(1).
 .PP
 For example:
 .PP
 .Vb 2
-\&    *FvwmWindowMenu: Show resource = Galeon|Navigator|mozilla-bin
-\&    *FvwmWindowMenu: Show name = ^emacs
+\&    *FvwmWindowMenu: ShowResource ^gvim
+\&    *FvwmWindowMenu: ShowName Galeon|Navigator|mozilla-bin|Firefox
 .Ve
 .PP
-will define two sections containing respectively browsers, and emacs.
-Number of sections is unlimited. The strings are perl regular
-expressions that will be evaluated in m// context. See \fIperlre\fR\|(1).
+will define two sections containing respectively browsers, and GVim. A third
+section will contain all other windows.
 .PP
 Similarly:
 .PP
 .Vb 2
-\&    *FvwmWindowMenu: DontShow name = ^Fvwm
-\&    *FvwmWindowMenu: DontShow class = Gkrellm
+\&    *FvwmWindowMenu: DontShowName ^Fvwm
+\&    *FvwmWindowMenu: DontShowClass Gkrellm
 .Ve
 .PP
 will cause the menu to ignore windows with name beginning with Fvwm
 or class gkrellm.
 .PP
 Other options:
-.IP "*FvwmWindowMenu: \fIOnlyIconic\fR {on|off}" 4
-.IX Item "*FvwmWindowMenu: OnlyIconic {on|off}"
+.IP "*FvwmWindowMenu: \fIOnlyIconified\fR {on|off}" 4
+.IX Item "*FvwmWindowMenu: OnlyIconified {on|off}"
 show only iconified windows
 .IP "*FvwmWindowMenu: \fIAllDesks\fR {on|off}" 4
 .IX Item "*FvwmWindowMenu: AllDesks {on|off}"
@@ -228,8 +232,8 @@
 .IP "*FvwmWindowMenu: \fIAllPages\fR {on|off}" 4
 .IX Item "*FvwmWindowMenu: AllPages {on|off}"
 show windows from all pages
-.IP "*FvwmWindowMenu: \fIMaxlen\fR 32" 4
-.IX Item "*FvwmWindowMenu: Maxlen 32"
+.IP "*FvwmWindowMenu: \fIMaxLen\fR 32" 4
+.IX Item "*FvwmWindowMenu: MaxLen 32"
 max length in chars of entry
 .IP "*FvwmWindowMenu: \fIMenuName\fR MyMenu" 4
 .IX Item "*FvwmWindowMenu: MenuName MyMenu"
@@ -254,7 +258,11 @@
 .ie n .IP "%x, %y" 4
 .el .IP "%x, \f(CW%y\fR" 4
 .IX Item "%x, %y"
-the window x or y coordinates on current page
+the window x or y coordinates w.r.t. the page the window is on.
+.ie n .IP "%X, %Y" 4
+.el .IP "%X, \f(CW%Y\fR" 4
+.IX Item "%X, %Y"
+the window x or y coordinates w.r.t. the desk the window is on.
 .IP "%d" 4
 .IX Item "%d"
 the window desk number
@@ -293,6 +301,12 @@
 .Vb 1
 \&    Key Super_R A A SendToModule FvwmWindowMenu Post Root c c WarpTitle
 .Ve
-.SH "AUTHOR"
-.IX Header "AUTHOR"
-Ric Lister <http://cns.georgetown.edu/~ric/>
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+.RE
+.IP "Ric Lister <http://cns.georgetown.edu/~ric/>"
+.IX Item "Ric Lister <http://cns.georgetown.edu/~ric/>"
+.RE
+.PD 0
+.IP "Scott Smedley"
+.IX Item "Scott Smedley"
Index: modules/FvwmWindowMenu/FvwmWindowMenu.in
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/FvwmWindowMenu/FvwmWindowMenu.in,v
retrieving revision 1.3
diff -u -r1.3 FvwmWindowMenu.in
--- modules/FvwmWindowMenu/FvwmWindowMenu.in    12 Nov 2003 21:27:41 -0000      
1.3
+++ modules/FvwmWindowMenu/FvwmWindowMenu.in    2 Jun 2004 11:28:51 -0000
@@ -28,9 +28,7 @@
 
 use lib "@FVWM_PERLLIBDIR@";
 use FVWM::Module;
-use General::FileSystem "-quiet";
 use General::Parse;
-use Getopt::Long;
 
 my $moduleType = "";
 my $moduleClass = "FVWM::Module";
@@ -47,293 +45,178 @@
        }
 }
 
-# default user options
-my $opt = {
-       onlyiconic   => 0,      # only list iconified windows
-       alldesks     => 0,      # list windows on all desks
-       allpages     => 0,      # list windows on all pages
-       maxlen       => 32,     # max chars for window name in menu
-       menuname     => "MenuFvwmWindowMenu",  # name of fvwm menu to create
-       menustyle    => undef,  # name of fvwm menu style to use
-       itemformat   => "%m%n%t%t(+%x+%y) - Desk %d",  # format of menu items
-       function     => "WindowListFunc",
-};
-
-# get size of your screen
-my $screen;
-
-# I would like a better way of getting screen dimensions!
-open(XWININFO, "xwininfo -root|") || die "can't run xwininfo";
-while (<XWININFO>) {
-       $screen->{w} = $1 if /Width:\s*(\d+)/;
-       $screen->{h} = $1 if /Height:\s*(\d+)/;
-}
-close(XWININFO);
-
-
-# some global vars
-my %window;
-my $desk = 0;
-my $page_nx = 0;
-my $page_ny = 0;
-
-# show = window matches to display in order, each entry defines a section
-# dontshow = window matches to ignore, none of these will show up in the menu
-my (@show, @dontshow);
-
 # init the module
 # set Debug = 0 for no messages at all
-# set Debug = 1 to see my messages about window decisions
+# set Debug = 1 to see messages about window decisions
 # set Debug = 2 to see also perllib messages about communication
 my $module = new $moduleClass(
        Name => "FvwmWindowMenu",
-       Mask => M_WINDOW_NAME | M_STRING |
-               M_NEW_PAGE | M_NEW_DESK |
-               M_ADD_WINDOW | M_CONFIGURE_WINDOW |
-               M_RES_NAME | M_RES_CLASS | M_ICON_NAME |
-               M_DESTROY_WINDOW | M_END_WINDOWLIST |
-               M_ICONIFY | M_DEICONIFY | M_MINI_ICON |
-               M_CONFIG_INFO | M_SENDCONFIG,
+       Mask => M_STRING,
        Debug => 0,
 );
 
 $module->debug("starting " . $module->name);
 
-# process module options
-$module->addHandler(M_CONFIG_INFO, sub {
-       my ($module, $event) = @_;
-
-       my $modname = $module->name;
-       return unless $event->_text =~ /^\*$modname(.*)$/;
-       $_ = $1;
-
-       # get show array
-       if ( /^show\s+(\w+)\s*=\s*(.*)$/i ) {
-               (my $type = $1) =~ tr/A-Z/a-z/;
-               push(@show, { type => $type, pattern => $2 });
-       }
-
-       # get dontshow array
-       elsif ( /^dontshow\s+(\w+)\s*=\s*(.*)$/i ) {
-               (my $type = $1) =~ tr/A-Z/a-z/;
-               push(@dontshow, { type => $type, pattern => $2 });
-       }
-
-       # match format string in quotes
-       elsif ( /^itemformat\s+([\"\'])(.*)\1/i ) {
-               $opt->{itemformat} = $2;
-       }
-
-       # get all other options, arg of 'off' sets opt to 0
-       elsif (  /^(\w+)\s*(\w+)/i ) {
-               (my $arg1 = $1) =~ tr/A-Z/a-z/;
-               $opt->{$arg1} = ($2 =~ /^off$/i) ? 0 : $2;
-       }
-
-       # should probably do more error checking
-});
+my $configTracker = $module->track('ModuleConfig',
+       DefaultConfig => {
+               OnlyIconified => 'off',
+               AllDesks => 'off',
+               AllPages => 'off',
+               MaxLen => 32,
+               MenuName => 'MyMenu',
+               MenuStyle => '',
+               Debug => 0,
+               Function => 'MyWindowListFunc',
+               ItemFormat => '%m%n%t%t(+%x+%y) - Desk %d',
+               ShowName => '',
+               ShowClass => '',
+               ShowResource => '',
+               DontShowName => '',
+               DontShowClass => '',
+               DontShowResource => '',
+       });
 
+my $pConfig = $configTracker->data();
+my $winTracker = $module->track("WindowList", "!stack icons names winfo");
 
-# handler to get page and desk info
-$module->addHandler(M_NEW_PAGE, sub {
-       my ($module, $event) = @_;
-
-       $desk = $event->_desk;
-
-       my $width  = $event->_vp_width || 1;
-       my $height = $event->_vp_height || 1;
-
-       $page_nx = int($event->_vp_x / $width);
-       $page_ny = int($event->_vp_y / $height);
-});
-
-
-$module->addHandler(M_NEW_DESK, sub {
-       my ($module, $event) = @_;
-       $desk = $event->_desk;
-});
-
-
-$module->addHandler(M_STRING, sub{
+$module->addHandler(M_STRING, sub {
        my ($module, $event) = @_;
        my ($action, $args) = getToken($event->_text);
        return unless $action;
        if ($action =~ /^Post|Menu|Popup$/i) {
-               &PopupMenu($action, $args);
+               PopupMenu($action, $args);
        } elsif ($action =~ /^ShowBar$/i) {
                if ($moduleType ne "gtk") {
                        $module->debug("Not started with Gtk support", 0);
                        return;
                }
-               &PopupTaskBar();
+               PopupTaskBar();
        } else {
                $module->debug("Unknown action $action", 0);
        }
 });
 
-
-$module->addHandler(M_ADD_WINDOW|M_CONFIGURE_WINDOW, sub {
-       my ($module, $event) = @_;
-
-       $window{$event->_win_id}->{desk} = $event->_desk;
-       $window{$event->_win_id}->{x} = $event->_frame_x;
-       $window{$event->_win_id}->{y} = $event->_frame_y;
-});
-
-
-$module->addHandler(M_DESTROY_WINDOW, sub {
-       my ($module, $event) = @_;
-       delete $window{$event->_win_id};
-});
-
-
-$module->addHandler(M_WINDOW_NAME, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{name} = $event->_name;
-});
-
-$module->addHandler(M_ICON_NAME, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{icon} = $event->_name;
-});
-
-$module->addHandler(M_RES_CLASS, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{class} = $event->_name;
-});
-
-$module->addHandler(M_RES_NAME, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{resource} = $event->_name;
-});
-
-
-
-$module->addHandler(M_ICONIFY, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{iconic} = 1;
-});
-
-
-$module->addHandler(M_DEICONIFY, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{iconic} = 0;
-});
-
-
-$module->addHandler(M_MINI_ICON, sub {
-       my ($module, $event) = @_;
-       $window{$event->_win_id}->{mini_icon} = $event->_name;
-});
-
-
 # does all the work and pops up the menu
-sub PopupMenu {
+sub PopupMenu ($$) {
        my ($action, $args) = @_;
-       my $command = $action =~ /^Popup$/i? "Popup": "Menu";
-
-       my @section;
+       my $command = ($action =~ /^Popup$/i ? "Popup" : "Menu");
 
-       # loop on list of windows
-       foreach my $id ( keys %window ) {
+       my @aSection;
 
-               $module->debug("\t$id - " . $window{$id}->{name});
+       # loop on list of all windows
+       my $pData = $winTracker->data();
+       foreach my $id (keys(%{$pData}))
+       {
+               my $p = $pData->{$id};
+               $module->debug("\t$id - " . $p->{window_name});
+
+               if ($pConfig->{AllDesks} =~ /off/i &&
+                       $p->{desk} != $winTracker->pageInfo('desk_n'))
+               {
+                       $module->debug("\t\tnot on this desk");
+                       next;
+               }
 
-               # skip windows in the dontshow array
-               my $dont_show_me;
-               foreach ( @dontshow ) {
-                       my $p = $_->{pattern};
-                       $dont_show_me = 1, last
-                               if $window{$id}->{$_->{type}} =~ /$p/;
+               if ($pConfig->{AllPages} =~ /off/i &&
+                       ($p->{page_x} != $winTracker->pageInfo('page_x') ||
+                        $p->{page_y} != $winTracker->pageInfo('page_y')))
+               {
+                       $module->debug("\t\tnot on this page");
+                       next;
                }
 
-               $module->debug("\t\tin dontshow list"), next
-                       if $dont_show_me;
+               if ($pConfig->{OnlyIconified} =~ /on/i && !$p->{iconified})
+               {
+                       $module->debug("\t\tnot iconified");
+                       next;
+               }
 
-               # skip windows not on this desk if alldesks not set
-               $module->debug("\t\tnot on this desk"), next
-                       unless $opt->{alldesks} || $window{$id}->{desk} == 
$desk;
-
-               # show only iconic windows if onlyiconic set
-               $module->debug("\t\tskipping because not iconified"), next
-                       if $opt->{onlyiconic} && ! $window{$id}->{iconic};
-
-               # skip window if its top left is not on this page
-               $module->debug("\t\tnot on this page"), next
-                       if ! $opt->{allpages} && (
-                               $window{$id}->{x} < 0 ||
-                               $window{$id}->{x} >= $screen->{w} ||
-                               $window{$id}->{y} < 0 ||
-                               $window{$id}->{y} >= $screen->{h});
-
-               # loop on sections and choose which one to file in
-               my $sectctr = 0;
-               foreach ( @show ) {
-                       my $p = $_->{pattern};
-                       last if $window{$id}->{$_->{type}} =~ /$p/ ;
-                       ++$sectctr;
+               if (($pConfig->{DontShowName} ne '' &&
+                       $p->{window_name} =~ /$pConfig->{DontShowName}/i) ||
+                       ($pConfig->{DontShowClass} ne '' &&
+                       $p->{res_class_name} =~ /$pConfig->{DontShowClass}/i) ||
+                       ($pConfig->{DontShowResource} ne '' &&
+                       $p->{res_name} =~ /$pConfig->{DontShowResource}/i))
+               {
+                       $module->debug("\t\tin dontshow list");
+                       next;
                }
 
-               # if we got here add the window to the last section
-               # which is 'none of the above'
-               $module->debug("\t\tadding to section $sectctr");
-               &AddToSection(\$section[$sectctr], $id);
+               my $section = 3;
+               if ($pConfig->{ShowName} ne '' &&
+                       $p->{window_name} =~ /$pConfig->{ShowName}/i) {
+                       $section = 0;
+               } elsif ($pConfig->{ShowClass} ne '' &&
+                       $p->{res_class_name} =~ /$pConfig->{ShowClass}/i) {
+                       $section = 1;
+               } elsif ($pConfig->{ShowResource} ne '' &&
+                       $p->{res_name} =~ /$pConfig->{ShowResource}/i) {
+                       $section = 2;
+               }
+               $module->debug("\t\tadding to section $section");
+               AddToSection(\$aSection[$section], $id);
        }
 
 
        # tell fvwm to start the menu
-       $module->send("DestroyMenu $opt->{menuname}");
-       $module->send("AddToMenu $opt->{menuname} 'Desk $desk, "
-               . "Page $page_nx $page_ny' Title");
+       $module->send("DestroyMenu recreate $pConfig->{MenuName}");
+       $module->send("AddToMenu " . $pConfig->{MenuName} . " 'Desk " .
+               $winTracker->pageInfo('desk_n') . ", Page " .
+               $winTracker->pageInfo('page_x') . ' ' .
+               $winTracker->pageInfo('page_y') . "' Title");
 
        # now loop on sections sending menu entries to fvwm
-       while ( @section ) {
-               my $s = shift @section;
+       while ( @aSection ) {
+               my $s = shift @aSection;
 
                if ($s) {
                        $module->send($s);
-
                        # add separator after section unless it is the last
-                       $module->send("+ \"\" Nop") if @section;
+                       $module->send("+ \"\" Nop") if @aSection;
                }
        }
 
        # set a menustyle if one given
-       $module->send("ChangeMenuStyle $opt->{menustyle} $opt->{menuname}")
-               if $opt->{menustyle};
+       $module->send("ChangeMenuStyle $pConfig->{MenuStyle} 
$pConfig->{MenuName}")
+               if ($pConfig->{MenuStyle} ne '');
 
        # popup the menu with args we were sent
-       $module->send("$command $opt->{menuname} $args");
+       $module->send("$command $pConfig->{MenuName} $args");
 }
 
 
 # build a line containing the fvwm menu entry for a window
-# then add it to the appropriate member of the global array @section
+# then add it to the appropriate member of the global array @aSection
 # args: pointer to section, window id
-sub AddToSection {
+sub AddToSection ($$) {
        my ($s, $id) = @_;
 
-       my $format = $opt->{itemformat};
+       my $format = $pConfig->{ItemFormat};
+       my $p = $winTracker->data($id);
 
        # hack: insert __%__ instead of % to avoid bogus substitution later
        $format =~ s/%%/__%____%__/g;
 
        # make format string substitutions
        $format =~ s/%t/\t/g;
-       $format =~ s/%n/&Shorten($window{$id}->{name}, $opt->{maxlen})/ge;
-       $format =~ s/%i/&Shorten($window{$id}->{icon}, $opt->{maxlen})/ge;
-       $format =~ s/%c/&Shorten($window{$id}->{class}, $opt->{maxlen})/ge;
-       $format =~ s/%r/&Shorten($window{$id}->{resource}, $opt->{maxlen})/ge;
-       $format =~ s/%x/$window{$id}->{x}/g;
-       $format =~ s/%y/$window{$id}->{y}/g;
-       $format =~ s/%d/$window{$id}->{desk}/g;
-
-       $format =~ s/%m/__%__$window{$id}->{mini_icon}__%__/g;
-
-       if ($window{$id}->{iconic}) {
-               $format =~ s/%M/__%__$window{$id}->{mini_icon}__%__/g;
-       }
-       else {
+       $format =~ s/%n/&Shorten($p->{window_name})/ge;
+       $format =~ s/%i/&Shorten($p->{icon_name})/ge;
+       $format =~ s/%c/&Shorten($p->{res_class_name})/ge;
+       $format =~ s/%r/&Shorten($p->{res_name})/ge;
+       $format =~ s/%X/$p->{x}/g;
+       $format =~ s/%Y/$p->{y}/g;
+       $format =~ s/%x/$p->{frame_x}/g;
+       $format =~ s/%y/$p->{frame_y}/g;
+       $format =~ s/%d/$p->{desk}/g;
+
+       # TODO: doesn't handle EWMH icons yet.
+       $format =~ s/%m// if ($p->{mini_icon_name} eq 'ewmh_mini_icon');
+       $format =~ s/%m/__%__$p->{mini_icon_name}__%__/g;
+
+       # %M is strange - does anyone really want this behaviour? -- SS.
+       if ($p->{iconified}) {
+               $format =~ s/%M/__%__$p->{mini_icon_name}__%__/g;
+       } else {
                $format =~ s/%M//g;
        }
        
@@ -345,13 +228,14 @@
 
        # add the entry to the section
        # support two ways for now: window context (new), window id param (old)
-       $$s .= qq(+ "$format" WindowId $id $opt->{function} $id\n);
+       $$s .= qq(+ "$format" WindowId $id $pConfig->{Function} $id\n);
 }
 
 
 # shorten a string to given length and append ellipses
-sub Shorten {
-       my($string, $length) = @_;
+sub Shorten ($) {
+       my ($string) = @_;
+       my $length = $pConfig->{MaxLen};
 
        my $r = substr($string, 0, $length);
        $r .= "..." if length($string) > $length;
@@ -359,7 +243,6 @@
        return $r;
 }
 
-
 sub PopupTaskBar () {
        my ($w, $h) = (180, 60);
 
@@ -367,7 +250,10 @@
        $window->set_title("FvwmWindowMenuBar");
        $window->set_border_width(5);
        $window->set_usize($w, $h);
-       $window->set_uposition(($screen->{w} - $w) / 2, ($screen->{h} - $h) / 
2);
+
+       my $screenW = $winTracker->pageInfo('vp_width');
+       my $screenH = $winTracker->pageInfo('vp_height');
+       $window->set_uposition(($screenW - $w) / 2, ($screenH - $h) / 2);
 
        my $frame = new Gtk::Frame();
        $window->add($frame);
@@ -390,9 +276,6 @@
 }
 
 
-# main loop
-$module->send("Send_ConfigInfo");
-$module->send("Send_WindowList");
 $module->send(
        "Style FvwmWindowMenuBar UsePPosition, !Title, !Borders, " .
        "StaysOnTop, WindowListSkip, CascadePlacement, SloppyFocus"
@@ -425,13 +308,14 @@
 module does not draw its own window, but instead creates an
 I<fvwm> menu and asks I<fvwm> to pop it up.
 
-By defining a set of regular expressions using Show, windows may
-be sorted into sections based on the regexp matching their
-name, icon name, class or resource.
+By defining a set of regular expressions, windows may
+be sorted into sections based on a regexp matching the window
+name, class or resource and included in the menu.
 
-Similarly, matches in DontShow will be excluded from the list.
+Similarly, another set of regular expressions can be used to exclude
+items from the menu.
 
-Any windows not matching an instance of Show or DontShow will
+Any windows not matching an instance of the include or exclude list will
 be placed in the last section of the menu.
 
 =head1 USAGE
@@ -457,28 +341,32 @@
 B<Menu> and B<Popup>. If the module was started with "-g" switch, it
 additionally supports B<PostBar> (not implemented yet).
 
-Set module options for windows to show or not show. The syntax is:
+Set module options for windows to include (Show) or exclude (DontShow).
+The syntax is:
 
-    *FvwmWindowMenu: Show type = pattern
-    *FvwmWindowMenu: DontShow type = pattern
+    *FvwmWindowMenu: ShowName pattern
+    *FvwmWindowMenu: ShowClass pattern
+    *FvwmWindowMenu: ShowResource pattern
+    *FvwmWindowMenu: DontShowName pattern
+    *FvwmWindowMenu: DontShowClass pattern
+    *FvwmWindowMenu: DontShowResource pattern
 
-where type is one of I<name>, I<icon>, I<class> or I<resource>. Pattern is
-a perl regular expression that will be evaluated in m// context.
+Pattern is a perl regular expression that will be evaluated in m// context.
 See perlre(1).
 
 For example:
 
-    *FvwmWindowMenu: Show resource = Galeon|Navigator|mozilla-bin
-    *FvwmWindowMenu: Show name = ^emacs
+    *FvwmWindowMenu: ShowResource ^gvim
+    *FvwmWindowMenu: ShowName Galeon|Navigator|mozilla-bin|Firefox
+
+will define two sections containing respectively browsers, and GVim. A third
+section will contain all other windows.
 
-will define two sections containing respectively browsers, and emacs.
-Number of sections is unlimited. The strings are perl regular
-expressions that will be evaluated in m// context. See perlre(1).
 
 Similarly:
 
-    *FvwmWindowMenu: DontShow name = ^Fvwm
-    *FvwmWindowMenu: DontShow class = Gkrellm
+    *FvwmWindowMenu: DontShowName ^Fvwm
+    *FvwmWindowMenu: DontShowClass Gkrellm
 
 will cause the menu to ignore windows with name beginning with Fvwm
 or class gkrellm.
@@ -487,7 +375,7 @@
 
 =over 4
 
-=item *FvwmWindowMenu: I<OnlyIconic> {on|off}
+=item *FvwmWindowMenu: I<OnlyIconified> {on|off}
 
 show only iconified windows
 
@@ -499,7 +387,7 @@
 
 show windows from all pages
 
-=item *FvwmWindowMenu: I<Maxlen> 32
+=item *FvwmWindowMenu: I<MaxLen> 32
 
 max length in chars of entry
 
@@ -531,7 +419,11 @@
 
 =item %x, %y
 
-the window x or y coordinates on current page
+the window x or y coordinates w.r.t. the page the window is on.
+
+=item %X, %Y
+
+the window x or y coordinates w.r.t. the desk the window is on.
 
 =item %d
 
@@ -573,8 +465,10 @@
 
     Key Super_R A A SendToModule FvwmWindowMenu Post Root c c WarpTitle
 
-=head1 AUTHOR
+=head1 AUTHORS
+
+=item Ric Lister <http://cns.georgetown.edu/~ric/>
 
-Ric Lister <http://cns.georgetown.edu/~ric/>
+=item Scott Smedley
 
 =cut
Index: perllib/ChangeLog
===================================================================
RCS file: /home/cvs/fvwm/fvwm/perllib/ChangeLog,v
retrieving revision 1.46
diff -u -r1.46 ChangeLog
--- perllib/ChangeLog   24 Apr 2004 19:13:13 -0000      1.46
+++ perllib/ChangeLog   2 Jun 2004 11:28:52 -0000
@@ -1,3 +1,8 @@
+2004-06-01  Scott Smedley  <[EMAIL PROTECTED]>
+
+       * FVWM/Tracker/WindowList.pm:
+       actually implemented tracker & all observables.
+
 2004-04-24  Mikhael Goikhman  <[EMAIL PROTECTED]>
 
        * FVWM/Tracker.pm:
Index: perllib/FVWM/Tracker/WindowList.pm
===================================================================
RCS file: /home/cvs/fvwm/fvwm/perllib/FVWM/Tracker/WindowList.pm,v
retrieving revision 1.4
diff -u -r1.4 WindowList.pm
--- perllib/FVWM/Tracker/WindowList.pm  25 Oct 2003 03:00:01 -0000      1.4
+++ perllib/FVWM/Tracker/WindowList.pm  2 Jun 2004 11:28:53 -0000
@@ -1,4 +1,4 @@
-# Copyright (c) 2003 Mikhael Goikhman
+# Copyright (c) 2004 Mikhael Goikhman, Scott Smedley
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -20,11 +20,25 @@
 
 use FVWM::Tracker qw(base);
 
+my $windowEvents = M_ADD_WINDOW | M_CONFIGURE_WINDOW | M_DESTROY_WINDOW |
+       M_ICONIFY | M_DEICONIFY;
+my $nameEvents = M_RES_NAME | M_RES_CLASS | M_WINDOW_NAME | M_VISIBLE_NAME |
+       M_ICON_NAME;
+my $stackEvents = M_RESTACK | M_RAISE_WINDOW | M_LOWER_WINDOW;
+my $iconEvents = M_ICON_LOCATION | M_ICON_FILE | M_DEFAULTICON | M_MINI_ICON;
+
 sub observables ($) {
        return [
                "window added",
                "window deleted",
                "window properties updated",
+               "window moved",
+               "window resized",
+               "window iconified",
+               "window deiconified",
+               "window name updated",
+               "window stack updated",
+               "window icon updated",
        ];
 }
 
@@ -36,41 +50,38 @@
        my $self = $class->FVWM::Tracker::new($module);
 
        $self->{options} = [ @options ];
-
        return $self;
 }
 
-sub addRequestedInfoHandlers ($$$$) {
+sub addRequestedInfoHandlers ($$) {
        my $self = shift;
-       my $handlerC = shift;
-       my $handlerD = shift;
-       my $handlerP = shift;
+       my $handler = shift;
 
        my $useWInfo = 1;
        my $useNames = 1;
        my $useStack = 0;
        my $useIcons = 0;
-       foreach my $option (@{$self->{options}}) {  
+       foreach (@{$self->{options}}) {  
                /^(\!?)winfo$/ and $useWInfo = $1 ne '!';
                /^(\!?)names$/ and $useNames = $1 ne '!';
                /^(\!?)stack$/ and $useStack = $1 ne '!';
                /^(\!?)icons$/ and $useIcons = $1 ne '!';
        }
-       my $mask1  = 0;
-       my $mask2  = 0;
-       my $xmask2 = 0;
-       $mask1 |= M_ADD_WINDOW | M_CONFIGURE_WINDOW if $useWInfo;
-       $mask2 |= M_RES_NAME | M_RES_CLASS | M_WINDOW_NAME | M_VISIBLE_NAME | 
M_ICON_NAME
-               if $useNames;
-       $mask2 |= M_RESTACK | M_RAISE_WINDOW | M_LOWER_WINDOW if $useStack;  
-       $mask2 |= M_ICON_LOCATION | M_ICON_FILE | M_DEFAULTICON | M_MINI_ICON
-               if $useIcons;
-       $xmask2 |= MX_VISIBLE_ICON_NAME if $useNames;
-
-       $self->addHandler($mask1, $handlerC) if $mask1;
-       $self->addHandler(M_DESTROY_WINDOW, $handlerD);
-       $self->addHandler($mask2, $handlerP) if $mask2;
-       $self->addHandler($xmask2, $handlerP) if $xmask2;
+       my $mask  = 0;
+       $mask |= $windowEvents if $useWInfo;
+       $mask |= $nameEvents if $useNames;
+       $mask |= $stackEvents if $useStack;  
+       $mask |= $iconEvents if $useIcons;
+
+       # Adding MX_VISIBLE_ICON_NAME to $nameEvents does not work.
+       my $xmask = 0;
+       $xmask |= MX_VISIBLE_ICON_NAME if $useNames;
+
+       $self->addHandler($mask, $handler) if $mask;
+       $self->addHandler($xmask, $handler) if $xmask;
+       $self->addHandler(M_NEW_PAGE | M_NEW_DESK, sub {
+               $self->handlerPageInfo($_[1]);
+       }) if $useWInfo;
 }
 
 sub start ($) {
@@ -78,37 +89,55 @@
 
        $self->{data} = {};
 
-       ### TODO
        $self->addRequestedInfoHandlers(sub {
                my $event = $_[1];
-               $self->calculateInternals($event->args);
-       }, sub {
-               my $event = $_[1];
-               $self->calculateInternals($event->args);
-       }, sub {
-               my $event = $_[1];
-               $self->calculateInternals($event->args);
+               $self->calculateInternals($event);
        });
 
        $self->requestWindowListEvents;
-
-       ### temporary
-       $self->deleteHandlers;
-
        my $result = $self->SUPER::start;
-
        $self->deleteHandlers;
 
-       ### TODO
        $self->addRequestedInfoHandlers(sub {
                my $event = $_[1];
-               $self->calculateInternals($event->args);
-       }, sub {
-               my $event = $_[1];
-               $self->calculateInternals($event->args);
-       }, sub {
-               my $event = $_[1];
-               $self->calculateInternals($event->args);
+               my ($winId, $oldHash) = $self->calculateInternals($event);
+               return unless defined $winId;
+               my $type = $event->type();
+               if ($type & M_ADD_WINDOW) {
+                       $self->notify("window added", $winId);
+               } elsif ($type & M_CONFIGURE_WINDOW) {
+                       $self->notify("window properties updated", $winId, 
$oldHash);
+                       # The "window properties updated" observable is very 
broad &
+                       # occurs as a result of many different operations - 
here we
+                       # try & determine if some common operations occur & 
invoke more
+                       # specific observables. This may reduce the amount of 
parsing
+                       # external modules have to do.
+                       my $p = $self->{data}->{$winId};
+                       if ($p->{frame_width} != $oldHash->{frame_width} ||
+                               $p->{frame_height} != $oldHash->{frame_height})
+                       {
+                               $self->notify("window resized", $winId, 
$oldHash);
+                       }
+                       elsif ($p->{desk} != $oldHash->{desk} ||
+                               $p->{x} != $oldHash->{x} ||
+                               $p->{y} != $oldHash->{y})
+                       {
+                               $self->notify("window moved", $winId, $oldHash);
+                       }
+                       # We can easily add other specific observables here as 
required.
+               } elsif ($type & M_DESTROY_WINDOW) {
+                       $self->notify("window deleted", $winId, $oldHash);
+               } elsif ($type & M_ICONIFY) {
+                       $self->notify("window iconified", $winId, $oldHash);
+               } elsif ($type & M_DEICONIFY) {
+                       $self->notify("window deiconified", $winId, $oldHash);
+               } elsif ($type & $nameEvents || $type & MX_VISIBLE_ICON_NAME) {
+                       $self->notify("window name updated", $winId, $oldHash);
+               } elsif ($type & $stackEvents) {
+                       $self->notify("window stack updated", $winId, $oldHash);
+               } elsif ($type & $iconEvents) {
+                       $self->notify("window icon updated", $winId, $oldHash);
+               }
        });
 
        return $result;
@@ -116,12 +145,83 @@
 
 sub calculateInternals ($$) {
        my $self = shift;
-       my $args = shift;
+       my $event = shift;
+       my $args = $event->args;
        my $data = $self->{data};
+       my $winId = $args->{win_id};
+
+       my $oldHash = undef;
+       if (defined $data->{$winId}) {
+               # make a copy of the hash cos we are about to modify it.
+               my %tmp = %{$data->{$winId}};
+               $oldHash = \%tmp;
+       }
+
+       # There are some fields that are not unique to all events. To ensure
+       # we don't clobber them, we rename some fields. For example, the 'name'
+       # field of M_MINI_ICON events is renamed to 'mini_icon_name'.
+       foreach ('name', 'x', 'y', 'width', 'height') {
+               if (defined $args->{$_}) {
+                       (my $name = lc($event->name())) =~ s/^.*?_//;
+                       $name .= "_$_" if ($name !~ /$_$/);
+                       $args->{$name} = $args->{$_};
+                       delete $args->{$_};
+               }
+       }
+
+       # Please leave this (commented-out) code here. I often enable it
+       # when debugging -- SS.
+#      print(STDERR "Got " . $event->name() . " event\n");
+#      foreach (sort(keys %{$args})) {
+#              print(STDERR "    $_=" . $args->{$_} . "\n");
+#      }
+
+       @{$data->{$winId}}{keys %{$args}} = values %{$args};
+
+       if (defined $args->{frame_x} && defined $self->{page_info}) {
+               # frame_x & frame_y are _relative_ coords of the window to the
+               # current page - calculate the _absolute_ coords - x & y.
+               my $pW = $data->{$winId};
+               $pW->{x} = $self->pageInfo('vp_x') + $args->{frame_x};
+               $pW->{y} = $self->pageInfo('vp_y') + $args->{frame_y};
+               $pW->{page_x} = int($pW->{x} / $self->pageInfo('vp_width'));
+               $pW->{page_y} = int($pW->{y} / $self->pageInfo('vp_height'));
+       }
 
-       ### TODO
+       my $type = $event->type();
+       if ($type & M_ADD_WINDOW || $type & M_DEICONIFY) {
+               $data->{$winId}->{iconified} = 0;
+       } elsif ($type & M_ICONIFY) {
+               $data->{$winId}->{iconified} = 1;
+       } elsif ($type & M_DESTROY_WINDOW) {
+               delete $data->{$winId};
+       }
+
+       return wantarray ? ($winId, $oldHash) : $winId;
 }
 
+sub handlerPageInfo ($$) {
+       my $self = shift;
+       my $event = shift;
+       my $args = $event->args;
+
+       @{$self->{page_info}}{keys %{$args}} = values %{$args};
+
+       if ($event->type() & M_NEW_PAGE) {
+               $self->{page_info}->{page_x} = int($args->{vp_x} / 
$args->{vp_width});
+               $self->{page_info}->{page_y} = int($args->{vp_y} / 
$args->{vp_height});
+       }
+}
+
+sub pageInfo ($;$) {
+       my $self = shift;
+       my $id = shift;
+       my $data = $self->{page_info};
+       return $data unless defined $id;
+       return $data->{$id};
+}
+
+
 sub data ($;$) { 
        my $self = shift;
        my $id = shift;
@@ -158,24 +258,31 @@
 
 This tracker defines the following observables:
 
-    "window added",
-    "window deleted",
-    "window properties updated",
-
-NOT USABLE YET.
+       "window added",
+       "window deleted",
+       "window properties updated",
+       "window moved",
+       "window resized",
+       "window iconified",
+       "window deiconified",
+       "window name updated",
+       "window stack updated",
+       "window icon updated",
 
 =head1 SYNOPSYS
  
 Using B<FVWM::Module> $module object (preferably):
 
-    my $windowsTracker = $module->track("ModuleConfig");
-    my $windows = $windowsTracker->data;
-    my $windowSizeX = $windows->{$winId}->{'x'};
+       my $windowsTracker = $module->track("WindowList", $options);
+       my $windows = $windowsTracker->data;
+       my $windowSizeX = $windows->{$winId}->{'x'};
 
 or:
 
-    my $windowsTracker = $module->track("WindowList");
-    my $windowSizeX = $windowsTracker->data($winId)->{'x'};
+       my $windowsTracker = $module->track("WindowList", $options);
+       my $windowSizeX = $windowsTracker->data($winId)->{'x'};
+
+Default $options string is: "!stack !icons names winfo"
 
 =head1 OVERRIDDEN METHODS
 
@@ -190,7 +297,7 @@
 =item B<data> [I<window-id>]
 
 Returns array ref of window hash refs. or one window hash ref if
-I<window-id> is given. The hash keys are not finalized yet.
+I<window-id> is given.
 
 =item B<dump> [I<window-id>]
 
@@ -198,9 +305,25 @@
 
 =back
 
-=head1 AUTHOR
+=head1 METHODS
 
-Mikhael Goikhman <[EMAIL PROTECTED]>.
+=over 4
+
+=item B<pageInfo> [I<field>]
+
+Returns hash ref of page/desk info, or actual hash value using B<field> as a 
key (if specified).
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item Mikhael Goikhman <[EMAIL PROTECTED]>
+
+=item Scott Smedley
+
+=back
 
 =head1 SEE ALSO
 

Reply via email to