http://qs1969.pair.com?node_id=207045

pType

update: If you want a compiled and ready to install executable: try downloading the installer. You'll still need the MS Agent components you can download from the Microsoft MS Agent site

pType is a Windows-only application that enables handicapped people who have only little possibilities to operate a computer to write and speak using an on-screen keyboard, by using only two controls: the left and right mousebuttons. The left mousebutton is used to select an item, the right mousebutton is used to move the selection to the next item. You have to choose a row which contains the item you want to select first, then you can choose the item within the row. If a row is accidentally selected you can return to rowselection using the up-arrow.

In order to use the application, the mousecursor should be pointed on the 'background', not on one of the items on the screen. Just point the mouse somewhere on the white background.

There are three 'general' controls: 'talk', to speak the text that is entered, 'backspace' to remove the last character, and 'again' to clear the text. To control the application, you can either use mousebuttons (by clicking on the white background of the application) or the Adremo electric wheelchair (using the headsupport)

The bottom of the code shows three seperate files: ptype_en.po and ptype_nl.po, which are used for the multi-langual part, and ptype.ini, defining the keyboard layout. If you want to execute this, you need to save those in the directory you're running pType from.

Apart from the modules listed, you may need two more files: ptype.ico (the icon) and inpout32.dll (only if you select Adremo support). You can download these from http://jouke.pvoice.org/files

History:
October 11, 2002: 1.0 First release
October 14, 2002: 1.0a Added the possibility to return to rowselection using the 'up' arrow
October 18, 2002: 1.0b Added the Adremo-support
October 22, 2002: 1.0c Added multi-language support and made it more configurable

Author: Jouke Visser
Last modification: October 22, 2002
Modules used:

Wx
Image::Info (Image::Info::JPEG for the JPEG images)
IO::File;
Win32::MSAgent;
Win32::API (for use of InpOut32.dll)
Locale::Maketext::Lexicon
File::Find
#!/usr/bin/perl use warnings; use strict; ###################################################################### +# package main; our $ADREMO_PARPORT_MASK = 0xf8; our $PARPORT_ADDRESS = 0x379; my $wxobj = pType->new(); $wxobj->MainLoop(); ###################################################################### +# package pType; use Wx qw(wxDefaultPosition wxDefaultSize); use base qw(Wx::App); #--------------------------------------------------------------------- +- sub OnInit { my $self = shift; my $Appname = "pType"; my $Appvendor = "pVoice Applications - Jouke Visser"; $self->SetAppName('pType'); $self->SetVendorName($Appvendor); # We get the configuration from the Windows registry # If it's not initialized, we provide some defaults my $config = Wx::ConfigBase::Get; my $Appversion = $config->Read('Appversion', '1.0c'); my $Appicon = $config->Read('Appicon', 'ptype.ico'); my $iniFile = $config->Read('iniFile', 'ptype.ini'); # $init represents if we have initialized the app before my $init = not $config->Read('Device'); my $Device = $config->Read('Device', 'icon'); # icon = mouse left/right buttons # adremo = electric wheelchair adremo my $Agentchar = $config->Read('Agentchar', 'Merlin'); my $Language = $config->Read('Language', 'English (US)'); my $L10N = $config->Read('L10N', 'en'); my $Voice = $config->Read('Voice', 'Adult Female #1') +; my $Interval = $config->ReadInt('Interval', 10); # We update the registry $config->Write('Appversion', $Appversion); $config->Write('Appicon', $Appicon); $config->Write('iniFile', $iniFile); $config->Write('Device', $Device); $config->Write('Agentchar',$Agentchar); $config->Write('Language', $Language); $config->Write('L10N', $L10N); $config->Write('Voice', $Voice); $config->WriteInt('Interval', $Interval); if ($init) { my $pref = PrefFrame->new( undef, #parent -1, # id 'Preferences', # title wxDefaultPosition, # position wxDefaultSize); # size $pref->Show(1); } else { my $frame = pTypeFrame->new( undef, # Parent windo +w -1, # Window id $self->GetAppName. ' '. $config->Read('Appversion'), # Title wxDefaultPosition, # position X, +Y wxDefaultSize # size X, Y ); $frame->Show(1); # Show the frame } } ###################################################################### +# package pTypeFrame; use Wx qw(:everything); use Wx::Image; use Wx::Event qw( EVT_LEFT_UP EVT_RIGHT_UP EVT_MENU EVT_TIMER EVT_CLOSE); use Image::Info qw(image_info); use IO::File; use Win32::MSAgent; use Win32::API; use base qw(Wx::Frame); #--------------------------------------------------------------------- +- sub new { my $class = shift; my $self = $class->SUPER::new(@_); # Get the registry handler into $self->{config} $self->{config} = Wx::ConfigBase::Get; # Get the language for internationalization $self->{lh} = L10N->get_handle($self->{config}->Read('L10N')); # Set the frame's icon my $icon=Wx::Icon->new( $self->{config}->Read('Appicon'), # name wxBITMAP_TYPE_ICO); # type $self->SetIcon($icon ); # to get the right dimensions for the items, se maximize the # window, show it, get the dimensions, and hide it again $self->Maximize(1); $self->Show(1); my ($x, $y) = ( $self->GetClientSize()->GetWidth(), $self->GetClientSize()->GetHeight() ); $self->Show(0); # Initialize the Image handlers Wx::InitAllImageHandlers; $self->{MAX_X} = 150; $self->{MAX_Y} = 150; my $key_id = 10_000; # create a new panel $self->{panel} = Wx::Panel->new($self, -1); $self->{panel}->SetBackgroundColour(wxWHITE); # Initialize the MS Agent stuff $self->{agent} = Win32::MSAgent->new(); $self->{agent} ->Characters->Load($self->{config}->Read('Agentchar'), $self->{config}->Read('Agentchar').".acs") +; # To be able to access the character from the $self->{panel}'s # action methods, we have to define it as a property of that $self->{panel}->{char} = $self->{agent} ->Characters($self->{config} ->Read('Agentchar')) +; $self->{panel}->{char}->Voice($self->{config}->Read('Language'), $self->{config}->Read('Voice')); $self->{panel}->{char}->MoveTo(0, 350); $self->{tls} = Wx::FlexGridSizer->new(0,1); my ($ids, $buttons, @rows); # Read they key-layout from the inifile open(KEYS, $self->{config}->Read('iniFile')) || die $self->{lh}->maketext("Can't open ").$self->{config}-> +Read('iniFile').": $!"; while (<KEYS>) { chomp; my @row = split(/\s/, $_); push @rows, \@row; } close(KEYS); # Draw the screen my $i; for ($i =0; $i < @rows; $i++) { my $row = $rows[$i]; ($self->{panel}->{panels}->{$i}, $key_id, $ids, $buttons) = $self->AddRow( $self->{panel}, # parent $key_id, # lowest id wxDefaultPosition, # position [$x,80], # size $row); # rowcontents $self->{panel}->{panels}->{$i}->SetBackgroundColour(wxWHITE); # update the button and actionlist $self->{panel}->{buttons}->{$i} = $buttons; @{$self->{panel}->{actions}->{$i}} = map {$_ = ' ' if $_ eq 'space' +; $_} 'up', @$row; $self->{tls}->Add($self->{panel}->{panels}->{$i}, # what to ad +d 0, # unused wxALL|wxALIGN_CENTRE, # style 2); # padding } # This panel is the panel with the control buttons Talk, Delete an +d # Again $self->{panel} ->{panels}->{$i}=Wx::Panel->new($self->{panel}, # paren +t -1, # id wxDefaultPosition, # pos [$x,$self->{MAX_Y}]); # size $self->{panel}->{panels}->{$i}->SetBackgroundColour(wxWHITE); my $sizer = Wx::FlexGridSizer->new(1,0); my $tbutton = Wx::BitmapButton->new ($self->{panel} ->{panels}->{$i}, # parent -1, # id $self->getimgfile("./img/talk.jpg") +, # image wxDefaultPosition, # position wxDefaultSize, # size wxSUNKEN_BORDER); # style $tbutton->SetBackgroundColour(wxWHITE); $sizer->Add($tbutton, 0, wxALL|wxALIGN_CENTRE, 2); # update the button and action list push @{$self->{panel}->{buttons}->{$i}}, $tbutton; push @{$self->{panel}->{actions}->{$i}}, 'talk'; my $dbutton = Wx::BitmapButton->new ($self->{panel} ->{panels}->{$i}, # parent -1, # id $self->getimgfile("./img/delete.jpg") +, # image wxDefaultPosition, # position wxDefaultSize, # size wxSUNKEN_BORDER); # style $dbutton->SetBackgroundColour(wxWHITE); $sizer->Add($dbutton, 0, wxALL|wxALIGN_CENTRE, 2); # update the button and actionlist push @{$self->{panel}->{buttons}->{$i}}, $dbutton; push @{$self->{panel}->{actions}->{$i}}, 'delete'; my $abutton = Wx::BitmapButton->new ($self->{panel} ->{panels}->{$i}, # parent -1, # id $self->getimgfile("./img/again.jpg") +, # image wxDefaultPosition, # position wxDefaultSize, # size wxSUNKEN_BORDER); # style $abutton->SetBackgroundColour(wxWHITE); $sizer->Add($abutton, 0, wxALL|wxALIGN_CENTRE, 2); # update teh button and actionlist push @{$self->{panel}->{actions}->{$i}}, 'again'; push @{$self->{panel}->{buttons}->{$i}}, $abutton; $self->{panel}->{panels}->{$i}->SetSizer($sizer); $self->{panel}->{panels}->{$i}->SetAutoLayout(1); $sizer->Fit($self->{panel}->{panels}->{$i}); $self->{tls}->Add( $self->{panel}->{panels}->{$i}, # what to add 0, # unused wxALL|wxALIGN_CENTRE, # style 2); # padding $self->{panel}->{lastrow} = $i; # Create the TextControl my $font = Wx::Font->new( 24, # font size wxSWISS, # font family wxNORMAL, # style wxNORMAL, # weight 0, # underline? 'Comic Sans MS'); # face name my $ta = Wx::TextAttr->new( Wx::Colour->new(0,0,0), # textcol Wx::Colour->new(255,255,255),# backgr. $font); # font $self->{rowsizer} = Wx::FlexGridSizer->new(1,0); $self->{panel}->{text} = Wx::TextCtrl->new ( $self->{panel}, # parent -1, # id '', # text wxDefaultPosition,# position [$x,60], # size wxTE_RICH| wxTE_MULTILINE); # style # set the text-attributes $self->{panel}->{text}->SetDefaultStyle($ta); $self->{rowsizer}->Add($self->{panel}->{text}, # what to add 0, # unused wxALL|wxALIGN_CENTRE, # style 2); # padding $self->{tls}->Add($self->{rowsizer}, 0, wxALL|wxALIGN_CENTRE, 2); # Set the sizer for the overall panel $self->{panel}->SetSizer($self->{tls}); $self->{panel}->SetAutoLayout(1); # Select the first row $self->{panel}->{selectedrow} = 0; $self->{panel}->{selecteditem} = 0; $self->{panel}->{panels}->{$self->{panel}->{selectedrow}} ->SetBackgroundColour(wxRED); $self->{panel}->{rowselection} = 1; # Create three menus my $mfile = Wx::Menu->new(); my $mhelp = Wx::Menu->new(); my $medit = Wx::Menu->new(); # With one item each my( $ID_ABOUT, $ID_PREFS, $ID_EXIT ) = ( 1..100); $mhelp->Append( $ID_ABOUT, $self->{lh}->maketext('&About...')); $medit->Append( $ID_PREFS, $self->{lh}->maketext('&Preferences... +')); $mfile->Append( $ID_EXIT, $self->{lh}->maketext('E&xit...')); # Create the menubar and append the individual menus my $mbar = Wx::MenuBar->new(); $mbar->Append($mfile, $self->{lh}->maketext("&File")); $mbar->Append($medit, $self->{lh}->maketext("&Edit")); $mbar->Append($mhelp, $self->{lh}->maketext("&Help")); # Assign the menu to our frame $self->SetMenuBar($mbar); # declare the events EVT_MENU($self, $ID_EXIT, \&OnQuit); EVT_MENU($self, $ID_ABOUT, \&OnAbout); EVT_MENU($self, $ID_PREFS, \&OnPrefs); # Show the MS Agent $self->{panel}->{char}->Show(); # save the parent in the panel's properties $self->{panel}->{parent} = $self; # The events for the icon device EVT_LEFT_UP($self->{panel}, \&OnLeft); EVT_RIGHT_UP($self->{panel}, \&OnRight); # The event for the adremo device $self->{timer} = Wx::Timer->new($self,1); $self->{timer}->Start($self->{config}->ReadInt('Interval')); EVT_TIMER($self, 1, \&MonitorPort); # Our close handler for exiting the application nicely EVT_CLOSE($self, \&OnExit); return $self; } #--------------------------------------------------------------------- +- # This sub is used to monitor the parallel port for the adremo device sub MonitorPort { my ($self, $event) = @_; # do nothing if the device is not adremo or # if we're already running return if $self->{config}->Read('Device') eq 'icon'; return if $self->{monitorrun}; $self->{monitorrun} = 1; $self->{getportval} = Win32::API->new( "inpout32", # dll "Inp32", # function ["I"], # Parameterlis +t "I") # returnvalue if not exists $self->{getportval}; my $curvalue = ($self->{getportval} ->Call($main::PARPORT_ADDRESS) & $main::ADREMO_PARPORT_MASK); if (not defined $curvalue) { $self->{monitorrun} = 0; return } $self->{lastvalue} = 0 if not exists $self->{lastvalue}; if ($curvalue != $self->{lastvalue}) { # 0x38 means a headmove to the right $self->OnNext if $curvalue == 0x38; # 0xf8 means a headmove to the left $self->OnSelect if $curvalue == 0xf8; $self->{lastvalue} = $curvalue; } $self->{monitorrun} = 0; } #--------------------------------------------------------------------- +- # This sub is being called when the left mousebutton is clicked sub OnLeft { my ($self, $event) = @_; # do nothing if we're not using the icon device return if $self->{parent}->{config}->Read('Device') eq 'adremo'; $self->{parent}->OnSelect; } #--------------------------------------------------------------------- +- # This sub is being called when the right mousebutton is clicked sub OnRight { my ($self, $event) = @_; # do nothing if we're not using the icon device return if $self->{parent}->{config}->Read('Device') eq 'adremo'; $self->{parent}->OnNext; } #--------------------------------------------------------------------- +- # This sub is being called when the next row or next item should be # highlighted sub OnNext { my $self = $_[0]->{panel}; if ($self->{rowselection}) # select the next row if we're using rowselection { $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxWHITE); $self->{selectedrow} = $self->{selectedrow} < $self->{lastrow} +? $self->{selectedrow} + 1 : 0; $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxRED); } else # select the next item in the row if we're not in rowselection { $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = $self->{selecteditem} < $#{$self->{buttons} ->{$self->{selectedrow}}} ? $self->{selecteditem} + 1 : 0; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxRED); } $self->Refresh(); } #--------------------------------------------------------------------- +- # This sub is being called when a row or item is selected sub OnSelect { my ($self) = $_[0]->{panel}; if ($self->{rowselection}) { if (scalar @{$self->{buttons}->{$self->{selectedrow}}} != 1) # leave rowselection mode if there is more than one item # in this row { $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = 0; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxRED); $self->{rowselection} = 0; } else # Just do the action associated with the item in the row # if there is just one item { if ($self->{actions}->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'talk') # Speak the text inside the textcontrol { $self->{char}->Speak($self->{text}->GetValue()) } elsif ($self->{actions} ->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'delete') # Do a 'backspace' { $self->{text}->SetValue(substr($self->{text} ->GetValue,0, -1)) } elsif ($self->{actions} ->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'again') # Empty the textcontrol { $self->{text}->SetValue('') } elsif ($self->{actions} ->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'up') # go back into rowselection { $self->{rowselection}=1; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = 0; $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxRED); } else # Add the selected character to the textcontrol { $self->{text}->AppendText($self->{actions} ->{$self->{selectedrow}} ->[$self->{selecteditem}] +) } $self->{rowselection}=1; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = 0; $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxRED); } } else # We are not in rowselection, so do the action associated with # the item { if ($self->{actions}->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'talk') # speak the text in the textcontrol { $self->{char}->Speak($self->{text}->GetValue()) } elsif ($self->{actions}->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'delete') # do a 'backspace' { $self->{text}->SetValue(substr($self->{text} ->GetValue,0, -1)) } elsif ($self->{actions}->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'again') # empty the text control { $self->{text}->SetValue('') } elsif ($self->{actions}->{$self->{selectedrow}} ->[$self->{selecteditem}] eq 'up') # the up arrow is for going back to rowselection { $self->{rowselection}=1; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = 0; $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxRED); } else # add the character selected to the textcontrol { $self->{text}->AppendText( $self->{actions} ->{$self->{selectedrow}} ->[$self->{selecteditem}]) } # go back into rowselection $self->{rowselection}=1; $self->{buttons}->{$self->{selectedrow}} ->[$self->{selecteditem}] ->SetBackgroundColour(wxWHITE); $self->{selecteditem} = 0; $self->{panels}->{$self->{selectedrow}} ->SetBackgroundColour(wxRED); } # update the screen $self->Refresh(); } #--------------------------------------------------------------------- +- # This sub gets a file from disk, resizes it if nessecary # and returns the Wx-internal bitmap sub getimgfile { my $self = shift; my $file = shift; # Determine the imagetype my $info = image_info($file); my $contenttype = $info->{file_media_type}; if (my $image = Wx::Image->new($file, $contenttype)) # we can load the image { my ($x,$y) = ($image->GetWidth, $image->GetHeight); # resize only if it's larger than MAX_X and MAX_Y if (($x > $self->{MAX_X}) || ($y > $self->{MAX_Y})) { if ($x >= $y) { my $factor = $x/$self->{MAX_X}; $image = $image->Scale($self->{MAX_X},$y/$factor); } else { my $factor = $y/$self->{MAX_Y}; $image = $image->Scale($x/$factor, $self->{MAX_Y}); } } #and return the image return $image->ConvertToBitmap(); } else { # if we can't open the file, warn and return nothing warn "Can't open $file: $!"; return undef; } } #--------------------------------------------------------------------- +- # This sub adds a row to the screen, given the starting number # for the ids, the position (as an arrayref), the size of the # entire row and the letters of the keys. # It returns the panel it created, the last id used, the arrayref # of ids used and the arrayref of buttons created. sub AddRow { my ($self, $parent, $id, $position, $size, $keys) = @_; # Create a new panel my $panel = Wx::Panel->new($parent, -1, $position, $size); my $sizer = Wx::FlexGridSizer->new(1,0); my $ids = {}; my @buttons = (); # Add the 'up' button my $button = Wx::BitmapButton->new ($panel, # parent $id++, # id $self->getimgfile("./img/up.jpg"), # image wxDefaultPosition, # position wxDefaultSize, # size wxSUNKEN_BORDER); # style $button->SetBackgroundColour(wxWHITE); $sizer->Add($button, 0, wxALL|wxALIGN_CENTRE, 2); push @buttons, $button; # Add the defined keys for this row for (@$keys) { $ids->{$id} = $_; $ids->{$id} = ' ' if $_ eq 'space'; my $button = Wx::BitmapButton->new ($panel, # parent $id++, # id $self->getimgfile("./img/$_.jpg") +, # image wxDefaultPosition, # position wxDefaultSize, # size wxSUNKEN_BORDER); # style $button->SetBackgroundColour(wxWHITE); $sizer->Add($button, 0, wxALL|wxALIGN_CENTRE, 2); push @buttons, $button; } $panel->SetSizer($sizer); $panel->SetAutoLayout(1); $sizer->Fit($panel); return $panel, $id, $ids, \@buttons; } #--------------------------------------------------------------------- +- # This sub is being called when the 'File'-'Exit' is selected sub OnQuit { my ($self, $event) = @_; # Let the MS Agent character wave to the user $self->{panel}->{char}->Play("Wave"); $self->{panel}->{char}->Speak("\\Emp\\".$self->{lh}->maketext("Bye +")."!"); sleep(4); # and close $self->Close( 1 ); } #--------------------------------------------------------------------- +- # This sub is being called when the application is closed sub OnExit { my ($self, $event) = @_; # stop the timer for the port monitor $self->{timer}->Stop(); $self->Destroy(); } #--------------------------------------------------------------------- +- # This sub is being called when 'Help'-'About' is selected sub OnAbout { my ($self, $event) = @_; my $appname = $self->{config}->GetAppName; my $wxperlstr = $self->{lh}->maketext('Uses wxPerl'); my $message = <<MSG; $appname Copyright (c) 2002 Jouke Visser $wxperlstr $Wx::VERSION MSG # display a simple about box Wx::MessageBox( $message.wxVERSION_STRING, # message $self->{lh}->maketext('About '). $self->{config}->GetAppName, # title wxOK |wxICON_INFORMATION, # style $self ); # parent } #--------------------------------------------------------------------- +- # This sub is being called when 'Edit'-'Preferences' is selected sub OnPrefs { my ($self, $event) = @_; $self->{prefs} = PrefFrame->new($self, # parent -1, # id $self->{lh} # title ->maketext("Preferences")); $self->{prefs}->Show(1); } ###################################################################### +# package PrefFrame; use Wx qw(:everything); use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX); use File::Find; use Win32::MSAgent; use base qw(Wx::MiniFrame); #--------------------------------------------------------------------- +- sub new { my $class = shift; # call the superclass' constructor my $self = $class->SUPER::new(@_); $self->{config} = Wx::ConfigBase::Get; # Get the language for internationalization $self->{lh} = L10N->get_handle($self->{config}->Read('L10N')); # set the frame's icon my $icon=Wx::Icon->new( $self->{config}->Read('Appicon'), wxBITMAP_TYPE_ICO); $self->SetIcon($icon ); # create a new panel $self->{panel} = Wx::Panel->new($self, -1); $self->{tls} = Wx::FlexGridSizer->new(0,1); $self->{device} = Wx::RadioBox ->new($self->{panel}, # paren +t -1, # id $self->{lh}->maketext('Control'),# label wxDefaultPosition, # pos wxDefaultSize, # size [$self->{lh}->maketext('Mousebuttons'), $self->{lh}->maketext('Adremo')],# item +s 2, # max wxRA_SPECIFY_COLS); # cols $self->{tls}->Add($self->{device}, 0, wxGROW|wxALL, 2); # preselect the right item if ($self->{config}->Read('Device') eq 'adremo') { $self->{device}->SetSelection(1); } else { $self->{device}->SetSelection(0); } $self->{intervallabel} = Wx::StaticText->new($self->{panel}, # parent -1, # id $self->{lh}->maketext('Interval')); # label $self->{tls}->Add($self->{intervallabel}, 0, wxGROW|wxALL, 2); $self->{interval} = Wx::TextCtrl->new($self->{panel}, # parent -1, # id $self->{config} ->ReadInt('Interval')); # default $self->{tls}->Add($self->{interval}, 0, wxGROW|wxALL, 2); $self->{L10N} = Wx::RadioBox->new($self->{panel}, # parent -1, # id $self->{lh} # label ->maketext('Language'), wxDefaultPosition, # pos wxDefaultSize, # size [$self->{lh} # items ->maketext('English'), $self->{lh} ->maketext('Dutch')], 2, # max wxRA_SPECIFY_COLS);# cols $self->{tls}->Add($self->{L10N}, 0, wxGROW|wxALL, 2); # preselect the right item if ($self->{config}->Read('L10N') eq 'nl') { $self->{L10N}->SetSelection(1); } else { $self->{L10N}->SetSelection(0); } $self->{agentcharlabel} = Wx::StaticText->new($self->{panel}, # parent -1, # id $self->{lh} # label ->maketext('Agent character')); $self->{tls}->Add($self->{agentcharlabel}, 0, wxGROW|wxALL, 2); # Find installed characters on this system my $systemroot = $ENV{SYSTEMROOT} || $ENV{WINDIR} || 'C:\WINDOWS'; my (@chars, %chars); find(sub {$chars{ucfirst(lc($1))} = $File::Find::name if $_=~ /(.* +)?\.acs$/i; }, "$systemroot\\msagent"); @chars = keys %chars; my $selchar=0; for (my $i=0; $i<@chars; $i++) { $selchar = $i if $chars[$i] eq $self->{config}->Read('Agentcha +r'); } $self->{chars} = Wx::ComboBox->new( $self->{panel}, # parent -1, # id '', # text wxDefaultPosition, # position wxDefaultSize, # size \@chars, # list wxCB_DROPDOWN| wxCB_READONLY); # style $self->{tls}->Add($self->{chars}, 0, wxGROW|wxALL, 2); $self->{chars}->SetSelection($selchar); $self->{agentlanglabel} = Wx::StaticText->new($self->{panel}, # parent -1, # id $self->{lh} # label ->maketext('Agent language')); $self->{tls}->Add($self->{agentlanglabel}, 0, wxGROW|wxALL, 2); # Translate all available languages for MS Agent $self->{alllangs} = [ 'English (US)', 'English (British)', 'Dutch', 'French', 'German', 'Italian', 'Japanese', 'Korean', 'Portuguese (Brazil)', 'Russian', 'Spanish']; my @translangs = map {$self->{lh}->maketext($_)} @{$self->{alllang +s}}; # Find the index of the current language my $sellang=0; for (my $i=0; $i<@{$self->{alllangs}}; $i++) { $sellang = $i if $self->{alllangs}->[$i] eq $self->{config}->Read('Language'); } $self->{langs} = Wx::ComboBox->new( $self->{panel}, # parent -1, # id '', # text wxDefaultPosition, # position wxDefaultSize, # size \@translangs, # list wxCB_DROPDOWN| wxCB_READONLY); # style $self->{tls}->Add($self->{langs}, 0, wxGROW|wxALL, 2); # Select the current selected language $self->{langs}->SetSelection($sellang); $self->{agentvoicelabel} = Wx::StaticText->new($self->{panel}, # parent -1, # id $self->{lh} # label ->maketext('Agent Voice')); $self->{tls}->Add($self->{agentvoicelabel}, 0, wxGROW|wxALL, 2); # Retrieve all voices for the current language my @allvoices =Win32::MSAgent->new ->Characters ->ListVoices($self->{config} ->Read('Language')) +; $self->{allvoices} = \@allvoices; # Find the index of the current selected voice my $selvoice=0; for (my $i=0; $i<@{$self->{allvoices}}; $i++) { $selvoice = $i if $self->{allvoices}->[$i] eq $self->{config}->Read('Voice'); } $self->{voices} = Wx::ComboBox->new( $self->{panel}, # parent -1, # id '', # text wxDefaultPosition, # position wxDefaultSize, # size $self->{allvoices}, # list wxCB_DROPDOWN| wxCB_READONLY); # style $self->{tls}->Add($self->{voices}, 0, wxGROW|wxALL, 2); # Select the voice that is currently used $self->{voices}->SetSelection($selvoice); $self->{button} = Wx::Button->new( $self->{panel}, # parent -1, # id 'OK'); # label $self->{tls}->Add($self->{button},0, wxGROW|wxALL, 2); $self->{NBlabel} = Wx::StaticText->new($self->{panel}, # parent -1, # id $self->{lh} # label ->maketext('Change notice')); $self->{tls}->Add($self->{NBlabel}, 0, wxGROW|wxALL, 2); $self->{panel}->SetSizer($self->{tls}); $self->{panel}->SetAutoLayout(1); $self->{tls}->Fit($self); # What to do if the button is clicked EVT_BUTTON($self, $self->{button}, \&OnButton); EVT_COMBOBOX($self, $self->{langs}, \&OnCBLang); return $self; } #--------------------------------------------------------------------- +- # This sub is being called when the language combobox has changed sub OnCBLang { my ($self, $event) = @_; my $lang = $self->{alllangs}->[$self->{langs}->GetSelection]; # Retrieve all voices for the current language my @allvoices =Win32::MSAgent->new ->Characters ->ListVoices($lang); $self->{allvoices} = \@allvoices; # clear the voices CB $self->{voices}->Clear; # and fill it with the voices for this language $self->{voices}->Append($_) foreach @{$self->{allvoices}}; $self->{voices}->SetSelection(0); } #--------------------------------------------------------------------- +- # This sub is called when the 'ok' button is selected to apply the # changes of the preferences sub OnButton { my ($self, $event) = @_; if ($self->{device}->GetSelection == 0) # we want to use the 'icon' device { $self->OnMB() } else # we want to use the 'adremo' device { $self->OnAdremo() } # update the interval $self->OnIntervalChange($self->{interval}->GetValue); if ($self->{L10N}->GetSelection == 0) # we want to use the 'english' language { $self->OnL10NChange('en') } else # we want to use the 'dutch' language { $self->OnL10NChange('nl') } # update the agentcharacter $self->OnCharChange($self->{chars}->GetStringSelection); # update the agentlanguage $self->OnLangChange($self->{alllangs}->[$self->{langs}->GetSelecti +on]); # update the agentvoice $self->OnVoiceChange($self->{voices}->GetStringSelection); # If this frame has no parent, we have been called before pType # itself has started, so we start the pType frame now if (not defined $self->GetParent) { my $frame = pTypeFrame->new( undef, # Parent windo +w -1, # Window id $self->{config}->GetAppName. ' '. $self->{config}->Read('Appversion +'), wxDefaultPosition, # position X, +Y wxDefaultSize # size X, Y ); $frame->Show(1); # Show the frame } # close the window $self->Close(); } #--------------------------------------------------------------------- +- # This sub is being called to indicate we want to use the 'Icon' devic +e sub OnMB { my ($self) = @_; $self->{config}->Write('Device','icon'); } #--------------------------------------------------------------------- +- # This sub is being called to indicate we want to use the 'Adremo' sub OnAdremo { my ($self) = @_; $self->{config}->Write('Device','adremo'); } #--------------------------------------------------------------------- +- # This sub is being called when we want to change the Adremo interval sub OnIntervalChange { my ($self, $interval) = @_; $self->{config}->WriteInt('Interval', $interval); } #--------------------------------------------------------------------- +- # This sub is being called when we want to change the Agent Character sub OnCharChange { my ($self, $char) = @_; return if not $char; $self->{config}->Write('Agentchar', $char); } #--------------------------------------------------------------------- +- # This sub is being called when we want to change the Agent Language sub OnLangChange { my ($self, $lang) = @_; return if not $lang; $self->{config}->Write('Language', $lang); } #--------------------------------------------------------------------- +- # This sub is being called when we want to change the Agent Voice sub OnVoiceChange { my ($self, $voice) = @_; return if not $voice; $self->{config}->Write('Voice', $voice); } #--------------------------------------------------------------------- +- # This sub is being called when we want to change the L10N language sub OnL10NChange { my ($self, $L10N) = @_; $self->{config}->Write('L10N', $L10N); } ###################################################################### +# package L10N; use base 'Locale::Maketext'; use Locale::Maketext::Lexicon {nl => [Gettext => 'ptype_nl.po'], en => [Gettext => 'ptype_en.po']}; ====================================================================== file ptype_en.po: msgid "&About..." msgstr "&About..." msgid "E&xit..." msgstr "E&xit\tCtrl-X" msgid "&Preferences..." msgstr "&Preferences\tCtrl-P" msgid "Can't open " msgstr "Can't open " msgid "&File" msgstr "&File" msgid "&Edit" msgstr "&Edit" msgid "&Help" msgstr "&Help" msgid "Bye" msgstr "Bye-bye" msgid "Uses wxPerl" msgstr "Uses wxPerl" msgid "About " msgstr "About " msgid "Preferences" msgstr "Preferences" msgid "Control" msgstr "Control" msgid "Mousebuttons" msgstr "Mousebuttons" msgid "Adremo" msgstr "Adremo" msgid "Interval" msgstr "Adremo refresh-interval (ms)" msgid "Language" msgstr "Language" msgid "English" msgstr "English" msgid "Dutch" msgstr "Dutch" msgid "Agent character" msgstr "Agent character" msgid "Agent language" msgstr "Agent language (speechsynthesis)" msgid "Change notice" msgstr "Most changes become active only after restarting the applicati +on" msgid "English (US)" msgstr "English (US)" msgid "English (British)" msgstr "English (British)" msgid "French" msgstr "French" msgid "German" msgstr "German" msgid "Italian" msgstr "Italian" msgid "Japanese" msgstr "Japanese" msgid "Korean" msgstr "Korean" msgid "Portuguese (Brazil)" msgstr "Portuguese (Brazil)" msgid "Russian" msgstr "Russian" msgid "Spanish" msgstr "Spanish" msgid "Agent Voice" msgstr "MS Agent Voice" ====================================================================== file ptype_nl.po: msgid "&About..." msgstr "&Over..." msgid "E&xit..." msgstr "&Afsluiten\tCtrl-A" msgid "&Preferences..." msgstr "&Voorkeuren\tCtrl-V" msgid "Can't open " msgstr "Fout bij het openen van " msgid "&File" msgstr "&Bestand" msgid "&Edit" msgstr "B&ewerken" msgid "&Help" msgstr "&Help" msgid "Bye" msgstr "Doei" msgid "Uses wxPerl" msgstr "Gebruikt wxPerl" msgid "About " msgstr "Over " msgid "Preferences" msgstr "Voorkeuren" msgid "Control" msgstr "Besturing" msgid "Mousebuttons" msgstr "Muisknoppen" msgid "Adremo" msgstr "Adremo" msgid "Interval" msgstr "Adremo verversings-interval (ms)" msgid "Language" msgstr "Taal" msgid "English" msgstr "Engels" msgid "Dutch" msgstr "Nederlands" msgid "Agent character" msgstr "Agent karakter" msgid "Agent language" msgstr "Agent taal (spraaksynthese)" msgid "Change notice" msgstr "De meeste wijzigingen worden pas actief nadat de applicatie is + herstart" msgid "English (US)" msgstr "Engels (VS)" msgid "English (British)" msgstr "Engels (Brits)" msgid "French" msgstr "Frans" msgid "German" msgstr "Duits" msgid "Italian" msgstr "Italiaans" msgid "Japanese" msgstr "Japans" msgid "Korean" msgstr "Koreaans" msgid "Portuguese (Brazil)" msgstr "Portuguees (Brazilie)" msgid "Russian" msgstr "Russisch" msgid "Spanish" msgstr "Spaans" msgid "Agent Voice" msgstr "MS Agent Stem" ====================================================================== file ptype.ini: 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 space