File Coverage

blib/lib/Tk/Win32RotLabel.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Tk::Win32RotLabel;
2              
3             our $VERSION = 0.4;
4              
5 1     1   14715 use Tk;
  0            
  0            
6             use Tk::widgets qw/Label/;
7             use base qw/Tk::Derived Tk::Label/;
8             use Win32::API;
9              
10             use strict;
11             use Carp;
12              
13             our (
14             $CreateFont,
15             $SelectObject,
16             $DeleteObject,
17             $GetDC,
18             $ReleaseDC,
19             $ExtTextOut,
20             $GetTextExtent,
21             $SetBkColor,
22             $SetTextColor,
23              
24             %configured,
25             );
26              
27             Construct Tk::Widget 'Win32RotLabel';
28              
29             # load the proper Win32::API suroutines at init.
30             sub ClassInit {
31             my ($class, $mw) = @_;
32              
33             $class->SUPER::ClassInit($mw);
34              
35             $CreateFont = new Win32::API('gdi32' , 'CreateFont', [('N') x 13,
36             'P'], 'N');
37             $SelectObject = new Win32::API('gdi32' , 'SelectObject', [qw/N N/], 'N');
38             $DeleteObject = new Win32::API('gdi32' , 'DeleteObject', ['P'], 'I');
39             $GetDC = new Win32::API('user32', 'GetDC', ['N'], 'N');
40             $ReleaseDC = new Win32::API('user32', 'ReleaseDC', [qw/N N/], 'I');
41             $ExtTextOut = new Win32::API('gdi32' , 'ExtTextOut', [qw/N I I N P P
42             I P/], 'I');
43             $GetTextExtent = new Win32::API('user32', 'GetTabbedTextExtent', [qw/N P N
44             N P/], 'I');
45             $SetBkColor = new Win32::API('gdi32', 'SetBkColor', [qw/N N/], 'N');
46             $SetTextColor = new Win32::API('gdi32', 'SetTextColor', [qw/N N/], 'N');
47             }
48              
49             sub Populate {
50             my ($w, $args) = @_;
51              
52             # clean up any images the user adds .. that's not the point.
53             delete $args->{-image};
54             $args->{-bitmap} = 'transparent';
55              
56             $w->SUPER::Populate($args);
57              
58             $w->ConfigSpecs(
59             -angle => [qw/METHOD angle Angle/, 0],
60             -text => [qw/METHOD text Text/, ''],
61             -textvariable => [qw/METHOD textvariabel Textvariable/, undef],
62             -font => [qw/PASSIVE font Font/, ['Times New Roman']],
63             );
64              
65             my $top = $w->toplevel;
66             unless ($configured{$top}) {
67             $top->bind('' => [\&_updateDescendants, $top]);
68             $configured{$top} = 1;
69             }
70             }
71              
72             sub angle {
73             my ($w, $a) = @_;
74              
75             if (defined $a) {
76             # should check if it's numeric .. TBD
77             $a = 0 if $a < 0;
78             $a = 360 if $a > 360;
79              
80             $w->{ANGLE} = $a;
81             }
82             $w->{ANGLE};
83             }
84             sub text {
85             my ($w, $t) = @_;
86              
87             if (defined $t) {
88             $w->{TEXT} = $t;
89             }
90             $w->{TEXT};
91             }
92              
93             sub textvariable {
94             my ($w, $tv) = @_;
95              
96             if (defined $tv) {
97             $w->{TEXTV} = $tv;
98             }
99             $w->{TEXTV};
100             }
101              
102             sub configure {
103             my $w = shift;
104             $w->SUPER::configure(@_);
105              
106             # update label if there is anything worthy.
107             my %a = @_;
108             $w->_updateMe if $a{-text} || $a{-textvariable} || $a{-font} || $a{-angle};
109             }
110              
111             # called by the top level.
112             # simply called _updateMe for every descendant Win32RotLabel widget.
113             sub _updateDescendants {
114             my $m = shift;
115              
116             ref $_ eq 'Tk::Win32RotLabel' && $_->_updateMe for $m->children;
117             }
118              
119             # this method draws the text.
120             sub _updateMe {
121             my $w = shift;
122             return unless $w->toplevel->ismapped;
123              
124             # first off, get the background and foreground colors
125             # in rgb syntax.
126             my $depth = $w->screendepth;
127             my @vis = $w->visualsavailable;
128             my $bg = $w->cget('-bg');
129             my $fg = $w->cget('-fg') || 'black';
130             my @bgRGB = $w->rgb($bg);
131             my @fgRGB = $w->rgb($fg);
132              
133             $_ = int(255 * $_ / 65535) for @bgRGB, @fgRGB;
134              
135             # Tk uses #RGB
136             # Win32 uses #BGR ... don't ask me why.
137             my $wbg = sprintf "0x%02X%02X%02X" => reverse @bgRGB;
138             my $wfg = sprintf "0x%02X%02X%02X" => reverse @fgRGB;
139              
140             # get the angle.
141             my $angle = $w->{ANGLE};
142              
143             # get the font object.
144             my $fontO = $w->cget('-font');
145             my $family = $w->fontActual($fontO, '-family');
146             my $size = $w->fontActual($fontO, '-size');
147             my $weight = $w->fontActual($fontO, '-weight');
148             my $slant = $w->fontActual($fontO, '-slant');
149             my $uline = $w->fontActual($fontO, '-underline');
150             my $strike = $w->fontActual($fontO, '-overstrike');
151              
152             # get the device context.
153             $w->update;
154             my $id = eval($w->id);
155             my $hdc = $GetDC->Call($id);
156              
157             # create the logical font.
158             my $font = $CreateFont->Call(int($size * 108 / 72), # by trial and error
159             0, $angle * 10, 0,
160             ($weight eq 'normal' ? 400 : 700),
161             ($slant eq 'roman' ? 0 : 1),
162             $uline,
163             $strike,
164             0, 0, 0, 0, 0,
165             $family);
166              
167             # select the font into the device context.
168             my $old = $SelectObject->Call($hdc, $font);
169              
170             # set the bg/fg colors.
171             $SetBkColor ->Call($hdc, eval $wbg);
172             $SetTextColor->Call($hdc, eval $wfg);
173              
174             # get the text string.
175             my $text;
176             if (defined $w->{TEXTV} && ref($w->{TEXTV}) eq 'SCALAR') {
177             $text = ${$w->{TEXTV}};
178             } else {
179             $text = $w->{TEXT};
180             }
181             my $len = length $text;
182              
183             # get the extent of the text.
184             my $r = $GetTextExtent->Call($hdc, $text, $len, 0, 0);
185             my $y = $r >> 16;
186             my $x = $r & 65535;
187              
188             # calculate the desired size of the label.
189             my $cos = abs(cos $angle * 3.14159 / 180);
190             my $sin = abs(sin $angle * 3.14159 / 180);
191              
192             my $W = $x * $cos + $y * $sin;
193             my $H = $y * $cos + $x * $sin;
194              
195             $w->configure(-width => $W,
196             -height => $H);
197              
198             $w->update;
199              
200             # get actual size.
201             $W = $w->reqwidth;
202             $H = $w->reqheight;
203              
204             # determine the location of the text.
205             my ($X, $Y) = (0, 0);
206             if ($angle <= 90) {
207             $Y = $x * $sin;
208             } elsif ($angle <= 180) {
209             $Y = $H;
210             $X = $x * $cos;
211             } elsif ($angle <= 270) {
212             $X = $W;
213             $Y = $H - $x * $sin;
214             } else {
215             $X = $W - $x * $cos;
216             }
217             # dump out the text.
218             $ExtTextOut->Call(
219             $hdc,
220             int $X,
221             int $Y,
222             0,
223             0,
224             $text, $len,
225             0
226             );
227              
228             # clean up.
229             $SelectObject->Call($hdc, $old);
230             $DeleteObject->Call($font);
231             $ReleaseDC ->Call($id, $hdc);
232             }
233              
234             __END__