package AmphetaDesk::OS::Windows; ############################################################################### # This package handles all the GUI routines for the Windows operating system. # # It requires various modules to be compiled into the runtime wrapper script # # - these modules are assumed to be there. If not, oopsy, we got problems. # # # # LIST OF ROUTINES BELOW: # # gui_init - start the gui and display the window. # # gui_listen - listen for window events for our gui. # # gui_note - send a note to our gui window. # # open_url - open a url in the system's default browser. # # _things - various routines that control the gui and should be private. # ############################################################################### use strict; $|++; use AmphetaDesk::Settings; use AmphetaDesk::Utilities; use Win32::API; use Win32::GUI; use Win32::TieRegistry; require Exporter; use vars qw( @ISA @EXPORT ); @ISA = qw( Exporter ); @EXPORT = qw( gui_init gui_listen gui_note open_url ); # these variables are global for # the Windows gui - they just keep # track of various things that are important. my ($hwnd, $icon, $hwnd_class, $window, $menu_bar); my ($img, $systray_icon, $systray_menu, $font, $logbox); # used to circumvent a bug where the # popup menu functions get called twice. my $already_called = 0; # remember the process id. my $fork_id_path; ############################################################################### # gui_init - start the gui and display the window. # ############################################################################### # USAGE: # # gui_init; # # # # NOTES: # # This routine loads specific libraries specific to the OS and attempts to # # do everything necessary to start up a GUI window and start listening for # # events. Further down in this file, you should see supplementary GUI # # routines (starting with _) that are part of the GUI happenings this # # routine inits. # # # # Most of the code within this routine is thanks to David Berube. # # NOTE: All of this code was written for Win32::GUI v0.5xx, which means # # that deprecated v0.6xx things like -style and -exstyle still exist. The # # reason is twofold: one, we still need to use v0.5xx under Windows 95, # # and two, we're too lazy to find out the right equivalents under v0.6xx. # # # # RETURNS: # # 1; this routine always returns happily. # ############################################################################### sub gui_init { # hwnd is a handle to a window - basically, window's # way of keeping track of it's program windows... $hwnd = GUI::GetPerlWindow(); # comment this to see error messages in a dos window # otherwise, this will hide the blasted thing... # GUI::Hide($hwnd); # get the width and height of the user's system. my $screen_width = Win32::GUI::GetSystemMetrics(0); my $screen_height = Win32::GUI::GetSystemMetrics(1); # create the icon handler. $icon = new Win32::GUI::Icon( get_setting("files_gui_win_icon") ); # create a window class for our window. $hwnd_class = new Win32::GUI::Class( -name => "AmphetaDesk Class", -icon => $icon ); # set up our menu bar. these point to various # routines defined later on in this file, as # well as set up key commands for the items. $menu_bar = new Win32::GUI::Menu( "&File" => "AmphetaDesk::OS::Windows::File", " > E&xit" => "AmphetaDesk::OS::Windows::_FileExit", "&Edit" => "AmphetaDesk::OS::Windows::Edit", " > &Copy Ctrl+C" => "AmphetaDesk::OS::Windows::_EditCopy", " > Select &All Ctrl+A" => "AmphetaDesk::OS::Windows::_EditSelectAll", "&Tools" => "AmphetaDesk::OS::Windows::Tools", " > &Open Browser" => "AmphetaDesk::OS::Windows::_OpenBrowser", " > &Refresh Channels" => "AmphetaDesk::OS::Windows::_RefreshChannels" ); # creates the main window. notice that we use a generic # "Name" parameter, which is used for events, which must # have the format Name_EventName. $window = new Win32::GUI::Window( -name => 'AmphetaDesk::OS::Windows::_Window', -text => 'AmphetaDesk', -left => ($screen_width - 600)/2, -top => ($screen_height - 400)/2, -width => 480, -height => 400, -maxsize => [480, 400], -minsize => [480, 400], -menu => $menu_bar, -class => $hwnd_class, -icon => $icon, -maximizebox => 0 ); # create the systray icon. $systray_icon = $window->AddNotifyIcon( -name => "AmphetaDesk::OS::Windows::_Systray", -id => 1, -icon => $icon, -tip => 'AmphetaDesk' ); # create the popup menu. $systray_menu = new Win32::GUI::Menu( "SystrayMenu Functions" => "SystrayMenu", "> Open Browser" => "AmphetaDesk::OS::Windows::_OpenBrowser", "> Refresh Channels" => "AmphetaDesk::OS::Windows::_RefreshChannels", "> Exit" => "AmphetaDesk::OS::Windows::_SystrayExit" ); # create our pretty logo. $window->AddLabel( -text => "", -name => "Bitmap", -left => -34, -top => -3, -width => 505, -height => 116, -style => 14, -visible => 1, ); # actually display the image... $img = new Win32::GUI::Bitmap( get_setting("files_gui_win_logo") ); $window->Bitmap->SetImage($img); $window->Bitmap->Resize(505, 116); # set the font of our log box below. we grab the Win32::GUI version so # that we can work around a bug fixed in the newest version of Win32::GUI, # whilst still providing support for the older (used by our Win95 runtime). my $version = $Win32::GUI::VERSION; my $font_size; if ($version eq "0.0.558") { $font_size = 12; } else { $font_size = 7; } $font = Win32::GUI::Font->new( -name => "Verdana", -size => $font_size ); # create the log box which is gonna hold all our info. $logbox = $window->AddRichEdit( -name => "AmphetaDesk::OS::Windows::_RichEdit", -font => $font, -top => 116, -left => 0, -width => 505, -height => 240, -tabstop => 1, -style => WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_READONLY, -exstyle => WS_EX_CLIENTEDGE ); # and make it a little smaller than our window size. $logbox->Resize( $window->ScaleWidth, $window->ScaleHeight - 118 ); # finally, show all # the junk we just did. $window->Show(); return 1; } ############################################################################### # gui_listen - listen for window events for our gui. # ############################################################################### # USAGE: # # gui_listen; # # # # NOTES: # # This routine checks the event queue and sees if there is anything that # # needs to be done. It's called from the main loop of our program. # # # # RETURNS: # # 1; this routine always returns happily. # ############################################################################### sub gui_listen { # anyone there? Win32::GUI::PeekMessage(0,0,0); Win32::GUI::DoEvents(); return 1; } ############################################################################### # gui_note - send a note to our gui window. # ############################################################################### # USAGE: # # gui_note("This is a gui window line. Yup."); # # # # NOTES: # # Much like note(), only we send our message to our os specific gui window. # # # # RETURNS: # # 1; this routine always returns happily. # ############################################################################### sub gui_note { my ($message) = @_; # select our last line. $logbox->Select(999999,999999); $logbox->ReplaceSel("$message\n", 1); select(undef, undef, undef, 0.25); # autoscroll the log box. $logbox->SendMessage (0x115, 1, 0) while $message =~ /\n|$/g; # listen for good measure. Win32::GUI::PeekMessage(0,0,0); Win32::GUI::DoEvents(); return 1; } ############################################################################### # open_url - open a url in the system's default browser. # ############################################################################### # USAGE: # # open_url( ); # # # # OS SPECIFIC NOTES: # # This routine checks in the registry for the user's default browser. # # If we haven't been told to use anything else, we use the registry # # settings, else we try to pass the $url as a shell parameter to the # # user's preferred choice. # # # # RETURNS: # # 1; we instruct the user to open their browser if we can't. # ############################################################################### sub open_url { # construct our url. my $url = "http://127.0.0.1:" . get_setting("urls_port") . "/index.html"; # we spit out our suggestion just to catch all instances. note("If your browser doesn't load, go to <$url>.", 1); # what browser? my $browser = $Registry->{"Classes\\http\\shell\\open\\command"}->{'\\'}; note("Your registry states that $browser is your default program."); # if a browser_path hasn't been set, try # to open the default browser using the native API. # we do an API call instead of a 'start $url' sort # of thing, because the API call seems far more stable - # we've experienced slowness with 'start $url' # and random crashes on some machines. if ( get_setting("user_browser_path") eq "default" ) { my $ShellExecute = new Win32::API("shell32", "ShellExecuteA", ['N','P', 'P', 'P', 'P', 'I'], 'N'); $ShellExecute->Call(0, "open", $url, 0, 0, 1); } # if a browser_path has been defined, try passing # the $url to the .exe and hope it understands. else { note("But instead, we'll try loading " . get_setting("user_browser_path") . "."); unless ( $fork_id_path = fork ) { system( qq|get_setting("user_browser_path") $url| ); } } return 1; } ############################################################################### # _things - various routines that control the gui and should be private. # ############################################################################### # USAGE: # # These are internally called by the GUI and shouldn't be publically # # used. So stop poking around here. Sheesh. Flippin' nosy people. Sigh. # ############################################################################### # various menu commands. sub AmphetaDesk::OS::Windows::_EditCopy_Click { note("Received 'Copy' request."); $logbox->SendMessage(0x301,0,0); } sub AmphetaDesk::OS::Windows::_EditSelectAll_Click { note("Received 'Select All' request."); $logbox->Select(0,999999); } sub AmphetaDesk::OS::Windows::_FileExit_Click { note("Quitting AmphetaDesk due to File > Exit."); &exit_program; } # re-open the browser window. sub AmphetaDesk::OS::Windows::_OpenBrowser_Click { unless ( $already_called ) { # for some reason, we get two clicks every # time someone clicks a menu item. so, # we create a constant and check later on. $already_called = 1; # helloooooooo, nurse! note("Received 'Open Browser' request from Systray."); # do it!. open_url(); } else { $already_called = 0; } } # Respond to the menu choice for # channel refreshing and browser opening. sub AmphetaDesk::OS::Windows::_RefreshChannels_Click { unless( $already_called ) { # helloooooooo, nurse! note("Received 'Refresh Channels' request from Systray."); note("--------------------------------------------------------------------------------", 1); note("Refreshing the channel list and opening browser window...", 1); note("--------------------------------------------------------------------------------", 1); # do the dirty deed. AmphetaDesk::MyChannels::download_my_channels(); open_url(); # my name is SuperFlippinTiredMan! # for some reason, we get two clicks every # time someone clicks a menu item. so, # we create a constant and check later on. $already_called = 1; } else { $already_called = 0; } } # generic code to force removal of systray icon. sub AmphetaDesk::OS::Windows::_Remove_Systray_Icon { Win32::GUI::NotifyIcon::Delete( $systray_icon->{-parent}, -id => $systray_icon->{-id} ); } # if the systray icon is clicked, re-enable the window. we have # to do this as a _MouseEvent as opposed to _DblClick due to # _DblClick not being supported on a NotifyIcon. The $event # id is one seven: 512 => 'Mouse Move', 514 => 'Left Click', # 515 => 'Left DoubleClick', 517 => 'Right Click', 518 => # 'Right DoubleClick',519 => 'Middle Down', and 520 => 'Middle Up'. # Thanks to Joe Frazier, Jr. for this information. sub AmphetaDesk::OS::Windows::_Systray_MouseEvent { my $event = shift; if ($event == 515) { $window->Enable; $window->Show; } } # If someone right clicks the systray icon, show a menu. sub AmphetaDesk::OS::Windows::_Systray_RightClick { note("Received Systray right click."); # get the x and y coords of the mouse to display the menu at. my($x, $y) = Win32::GUI::GetCursorPos(); # make the popup menu visible at the cursor $window->TrackPopupMenu($systray_menu->{SystrayMenu}, $x, $y-50); } # quit the program. we only do it this way so we can track what was clicked. sub AmphetaDesk::OS::Windows::_SystrayExit_Click { note("Quitting AmphetaDesk due to Systray > Exit."); &exit_program; } # hide the window if it's been minimized. the only # way to get it back would be from the systray icon. # we need to figure out what to do when people click # the "X' on the window. minimize? or close the app? sub AmphetaDesk::OS::Windows::_Window_Minimize { note("Minimizing window to Systray."); $window->Disable; $window->Hide; return -1; } sub AmphetaDesk::OS::Windows::_Window_Terminate { note("Quitting AmphetaDesk due to window termination."); &exit_program; } # our exit code - called from various methods in the Windows # GUI (systray quit, window termination, file->exit, etc.) sub exit_program { # kill our children if they are there. if ( $fork_id_path ) { note("Attempting to kill pid #$fork_id_path."); kill( "TERM", $fork_id_path ); } # remove the icon. AmphetaDesk::OS::Windows::_Remove_Systray_Icon; # ciao. exit; } 1;