=head1 NAME Win32::GUI::Modalizer -- Almost "correct" Windows modal dialog boxes =head1 DESCRIPTION =head2 What are modal/modeless windows? A modal window is a window that has the user's entire attention. Other windows in the application are "frozen" and cannot be selected when the modal window is displayed. If the window is "system modal", no oher window in the entire OS can be selected. The opposite of a modal window is a modeless window. =head2 Win32::GUI::Modalizer Win32::GUI::Modalizer is a class to help you display modal dialog boxes using Win32::GUI. These aren't implemented in a native way (yet, I hope) in Win32::GUI. Modalizer differs from "real" modal windows in the following ways: =over 4 =item * The user may double click the title bar to maximize/restore a modeless window. =item * Modeless windows aren't really disabled, they just give focus to the modal window as soon as they get the focus. =item * The modal window is visible in the task bar. =item * When multiple modeless windows are visible when the modal window is displayed, they can still change Z- order when clicked. =back =head1 VERSION Beta still. =head1 USAGE The SYNOPSIS example program demonstrates two modeless windows and one dialog box. You have to do quite a lot of manual work to get it to work, mainly calling the Modalizer object from every window's Activate() event. Basicaly, what you have to do is: =over 4 =item * Create your window objects. =item * Create the Modalizer and specify the modeless windows. =item * Create event routines for the Activate() events for all your modal windows and all your modeless windows. Call the activate() method from these. =item * That is the basic setup. Now we are ready to display the modal window. In e.g. a Click() event, call the beginDialog() method. The first argument is the modal Window object to display. The second argument is optional, and you only have to use it if you have many modeless windows visible (this is not the case in most situations). The second argument is the Window object that is currently active. It will have it's focus restored when the modal window is hidden. The modal window is displayed. =item * When you want to close the modal window, call the endDialog() method. =item * You're done. Cool! =back =head1 SYNOPSIS #!/usr/local/bin/perl -w use strict; use Win32::GUI; my $winModeless = new Win32::GUI::Window( -left => 13, -top => 32, -width => 439, -height => 260, -name => "winModeless", -text => "Win32::GUI::Modalizer Synopsis [1]" ); $winModeless->AddButton( -text => "&Open", -name => "btnModelessOpen", -left => 358, -top => 207, -width => 70, -height => 21, ); $winModeless->AddButton( -text => "&Test", -name => "btnModelessTest", -left => 358, -top => 20, -width => 70, -height => 21, ); my $winModeless2 = new Win32::GUI::Window( -left => 100, -top => 100, -width => 439, -height => 260, -name => "winModeless2", -text => "Win32::GUI::Modalizer Synopsis [2]" ); $winModeless2->AddButton( -text => "&Open", -name => "btnModeless2Open", -left => 358, -top => 207, -width => 70, -height => 21, ); my $winDialog = new Win32::GUI::DialogBox( -left => 50, -top => 50, -width => 439, -height => 260, -name => "winDialog", -text => "Dialog Box", ); $winDialog->AddButton( -text => "&Ok", -name => "btnDialogOk", -left => 358, -top => 207, -width => 70, -height => 21, ); $winModeless->Show(); $winModeless2->Show(); #Do the Modalizer stuff use Win32::GUI::Modalizer; my $objModalizer = Win32::GUI::Modalizer->new($winModeless, $winModeless2); #Go modal Win32::GUI::Dialog(); #Modeless window sub winModeless_Terminate { return(-1); } sub winModeless_Activate { defined($objModalizer) and $objModalizer->activate($winModeless); return(1); } sub btnModelessOpen_Click { $objModalizer->beginDialog($winDialog, $winModeless); return(1); } sub btnModelessTest_Click { Win32::GUI::MessageBox(0, "Clicked the Test button", "Test Button"); return(1); } #Modeless2 window sub winModeless2_Terminate { return(-1); } sub winModeless2_Activate { defined($objModalizer) and $objModalizer->activate($winModeless2); return(1); } sub btnModeless2Open_Click { $objModalizer->beginDialog($winDialog, $winModeless2); return(1); } #Dialog window sub winDialog_Terminate { $objModalizer->endDialog(); return 0; } sub winDialog_Activate { $objModalizer->activate($winDialog); return(1); } sub btnDialogOk_Click { winDialog_Terminate(); return(1); } #EOF =cut package Win32::GUI::Modalizer; use strict; use Win32::GUI; =head1 PROPERTIES =head2 raWinModeless Hash ref to Win32::GUI Windows to keep in the background while some window is modal. These are the windows you specify in the new() call, but you may change this at will unless you have a modal window visible. =cut sub raWinModeless { my $self = shift; my ($val) = @_; if(defined($val)) { $self->{raWinModeless} = $val; } return($self->{raWinModeless}); } =head2 rhBlocked Hash ref with (key = Window object, value = block level). =cut sub rhBlocked { my $self = shift; my $pkg = ref($self); my ($val) = @_; if(defined($val)) { $self->{rhBlocked} = $val; } return($self->{rhBlocked}); } =head2 winModal The Window object that is modal, or undef if no window is modal. Set to 0 to undef. =cut sub winModal { my $self = shift; my $pkg = ref($self); my ($val) = @_; if(defined($val)) { $self->{winModal} = $val; $self->{winModal} = undef if($val == 0); } return($self->{winModal}); } =head2 winModeless Maybe the Window object that opened the modal window, or undef if no such window was defined. Set to 0 to undef. =cut sub winModeless { my $self = shift; my $pkg = ref($self); my ($val) = @_; if(defined($val)) { $self->{winModeless} = $val; $self->{winModeless} = undef if($val == 0); } return($self->{winModeless}); } =head1 METHODS =head2 new(@aWinModeless) Create new Modalizer object for the windows in @aWinModeless (Win32::GUI::Window objects). These Windows objects are the modeless ones that should be "frozen" when a modal window is displayed. =cut sub new { my $pkg = shift; $pkg = ref($pkg) || $pkg; my (@aWinModeless) = @_; my $self = { raWinModeless => \@aWinModeless, rhBlocked => {}, winModal => undef, winModeless => undef, }; bless $self, $pkg; return($self); } =head2 beginDialog($winModal, [$winModeless]) Set the application in a modal state with $winModal as the modal window. Show $winModal. If $winModeless is passed, this is the main modeless window that was active when the modal window was displayed. This window will get the focus back when endDialog() is called. The sub will return immediately. Call endDialog() to exit the modal state. Return 1 on success, else 0. =cut sub beginDialog { my $self = shift; my $pkg = ref($self); my ($winModal, $winModeless) = @_; defined($winModeless) and $self->winModeless($winModeless); #Already modal? return(0) if($self->winModal()); $self->winModal($winModal); for my $winCur (@{$self->raWinModeless()}) { $self->rhBlocked()->{ $winCur } = 0; } $self->rhBlocked()->{ $winModal } = 1; #print "Modal: Block" . $self->winModal() . "\n"; $winModal->Show(); $winModal->BringWindowToTop(); return(1); } =head2 endDialog() End the modal state, hide the modal window and restore the state of all other affected windows. Return 1 on success, else 0. =cut sub endDialog { my $self = shift; my $pkg = ref($self); #Not modal? return(0) if(!$self->winModal()); #Do the modal window my $winModal = $self->winModal(); $self->winModal(0); $winModal->Hide(); #Will not enter the activate() method, winModal() == 0 #Do windows other than the main modeless window my $winModeless = defined($self->winModeless()) ? $self->winModeless() : 0; for my $winCur (@{$self->raWinModeless()}) { next if($winCur == $winModeless); $winCur->Enable() if($winCur->IsVisible()); $winCur->SetFocus(); } #Do the main modeless window if($self->winModeless()) { $self->winModeless()->Enable(); $self->winModeless()->SetFocus(); $self->winModeless(0); } return(1); } =head2 activate($winActivated) Call this from the Activate() event for all windows that are controlled by Modalizer, both the modeless windows and the modal windows. $winActivated is the Window that just got activated. Return 1 on success, else 0. =cut sub activate { my $self = shift; my $pkg = ref($self); my ($winActivated) = @_; #Not modal? return(0) if(!$self->winModal()); #Selectively block re-entry #print "Modal: " if($self->winModal() == $winActivated); $self->rhBlocked()->{$winActivated}--; if($self->rhBlocked()->{$winActivated} >= 0) { #print "Blocked: $winActivated: " . $self->rhBlocked()->{$winActivated} . "\n\n"; return(0); } #print "Passed: $winActivated\n"; if($winActivated == $self->winModal()) { #Bring to front all visible main windows for my $winCur (@{$self->raWinModeless()}) { if($winCur->IsVisible()) { $self->rhBlocked()->{$winCur} = 1; #print "Modeless: Block: $winCur\n"; $winCur->BringWindowToTop(); } } #Bring to front the modal window $self->rhBlocked()->{ $self->winModal() } = 1; #print "Modal: Block: " . $self->winModal() . "\n"; $self->winModal()->BringWindowToTop(); } else { #Bring to front the modal window $self->rhBlocked()->{ $self->winModal() } = 1; #print "Modal: Block: " . $self->winModal() . "\n"; $self->winModal()->BringWindowToTop(); } #print "\n"; return(1); } =head1 AUTHOR Copyright 2001.. Johan Lindström Same license as Perl. =cut 1; #EOF