File Coverage

blib/lib/App/Chart/Gtk2/HScale.pm
Criterion Covered Total %
statement 9 11 81.8
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             # Copyright 2008, 2009, 2010, 2011, 2012 Kevin Ryde
2              
3             # This file is part of Chart.
4             #
5             # Chart is free software; you can redistribute it and/or modify it under the
6             # terms of the GNU General Public License as published by the Free Software
7             # Foundation; either version 3, or (at your option) any later version.
8             #
9             # Chart is distributed in the hope that it will be useful, but WITHOUT ANY
10             # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11             # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12             # details.
13             #
14             # You should have received a copy of the GNU General Public License along
15             # with Chart. If not, see <http://www.gnu.org/licenses/>.
16              
17             package App::Chart::Gtk2::HScale;
18 1     1   414 use 5.008;
  1         3  
19 1     1   4 use strict;
  1         2  
  1         16  
20 1     1   4 use warnings;
  1         1  
  1         28  
21 1     1   240 use Glib::Ex::SignalIds;
  0            
  0            
22             use Gtk2;
23             use Gtk2::Ex::AdjustmentBits 47; # v.47 for set_empty()
24             use List::Util qw(min max);
25             use POSIX ();
26              
27             use App::Chart;
28             use App::Chart::Glib::Ex::MoreUtils;
29             use App::Chart::Glib::Ex::TieWeakNotify;
30              
31             # uncomment this to run the ### lines
32             #use Smart::Comments;
33              
34             use Glib::Object::Subclass
35             'Gtk2::Adjustment',
36             signals => { },
37             properties => [Glib::ParamSpec->double
38             ('pixel-per-value',
39             'pixel-per-value',
40             'Blurb',
41             0, POSIX::DBL_MAX(), 0,
42             Glib::G_PARAM_READWRITE),
43              
44             Glib::ParamSpec->double
45             ('value-per-pixel',
46             'value-per-pixel',
47             'Blurb',
48             0, POSIX::DBL_MAX(), 0,
49             Glib::G_PARAM_READWRITE),
50              
51             Glib::ParamSpec->object
52             ('widget',
53             'widget',
54             'Blurb',
55             'Gtk2::Widget',
56             Glib::G_PARAM_READWRITE),
57             ];
58              
59              
60             sub INIT_INSTANCE {
61             my ($self) = @_;
62             $self->{'value_per_pixel'} = 0;
63             $self->{'pixel_per_value'} = 0;
64              
65             ### INIT_INSTANCE gives
66             ### $self
67             }
68              
69             sub SET_PROPERTY {
70             my ($self, $pspec, $newval) = @_;
71             my $pname = $pspec->get_name;
72             ### HScale SET_PROPERTY(): $pname, $newval
73             $self->{$pname} = $newval; # per default GET_PROPERTY
74              
75             if ($pname eq 'value_per_pixel') {
76             $self->set_value_per_pixel ($newval);
77              
78             } elsif ($pname eq 'pixel_per_value') {
79             $self->set_pixel_per_value ($newval);
80              
81             } elsif ($pname eq 'widget') {
82             my $widget = $newval;
83             App::Chart::Glib::Ex::TieWeakNotify->set ($self, $pname, $widget);
84             $self->{'widget_ids'} = $widget && Glib::Ex::SignalIds->new
85             ($widget,
86             $widget->signal_connect (size_allocate => \&_do_size_allocate,
87             App::Chart::Glib::Ex::MoreUtils::ref_weak ($self)));
88             _update_page_size ($self);
89             }
90              
91             ### SET_PROPERTY gives
92             ### $self
93             }
94              
95             sub set_value_per_pixel {
96             my ($self, $vpp) = @_;
97             ### HScale set_value_per_pixel(): $vpp
98             $self->{'value_per_pixel'} = $vpp;
99             $self->{'pixel_per_value'} = ($vpp == 0 ? 0 : 1.0 / $vpp);
100             _update_page_size ($self);
101             $self->notify ('value-per-pixel');
102             $self->notify ('pixel-per-value');
103             }
104             sub set_pixel_per_value {
105             my ($self, $ppv) = @_;
106             ### HScale set_pixel_per_value(): $ppv
107             $self->{'pixel_per_value'} = $ppv;
108             $self->{'value_per_pixel'} = ($ppv == 0 ? 0 : 1.0 / $ppv);
109             _update_page_size ($self);
110             $self->notify ('value-per-pixel');
111             $self->notify ('pixel-per-value');
112             }
113             sub set_value_range {
114             my ($self, $lo, $hi) = @_;
115             $self->value ($lo);
116             my $width = ($self->{'widget'}
117             ? $self->{'widget'}->allocation->width
118             : 0);
119             $self->set_pixel_per_value ($width / ($hi - $lo));
120             $self->notify ('value');
121             $self->value_changed;
122             }
123              
124             sub get_value_per_pixel {
125             my ($self) = @_;
126             return $self->{'value_per_pixel'};
127             }
128             sub get_pixel_per_value {
129             my ($self) = @_;
130             return $self->{'pixel_per_value'};
131             }
132              
133             sub value_range_inc {
134             my ($self) = @_;
135             my $value = $self->value;
136             return (POSIX::floor ($value),
137             POSIX::ceil ($value + $self->page_size));
138             }
139              
140             sub value_to_pixel {
141             my ($self, $v) = @_;
142             return POSIX::floor
143             (($v - $self->value) * $self->{'pixel_per_value'});
144             }
145             sub value_to_pixel_proc {
146             my ($self) = @_;
147             my $value = $self->value;
148             my $ppv = $self->{'pixel_per_value'};
149             return sub {
150             return POSIX::floor (($_[0] - $value) * $ppv);
151             };
152             }
153              
154             sub pixel_to_value {
155             my ($self, $pixel) = @_;
156             return POSIX::floor ($self->value + $pixel * $self->{'value_per_pixel'});
157             }
158             sub pixel_to_value_proc {
159             my ($self) = @_;
160             my $value = $self->value;
161             my $vpp = $self->{'value_per_pixel'};
162             return sub {
163             return POSIX::floor ($value + $_[0] * $vpp);
164             };
165             }
166              
167             # 'size-allocate' on widget
168             sub _do_size_allocate {
169             my ($widget, $allocation, $ref_weak_self) = @_;
170             my $self = $$ref_weak_self || return;
171             _update_page_size ($self);
172             }
173              
174             sub _update_page_size {
175             my ($self) = @_;
176             my %adjvalues;
177              
178             # whether currently showing the end, or roughly so
179             my $at_end = ($self->value >= $self->upper - $self->page_size * 1.01);
180              
181             my $width = ($self->{'widget'}
182             ? $self->{'widget'}->allocation->width
183             : 0);
184             my $page = $width * $self->{'value_per_pixel'};
185             $adjvalues{'page_size'} = $page;
186             $adjvalues{'page_increment'} = ($page * 0.8);
187             $adjvalues{'step_increment'} = $page * 0.2;
188              
189             # if upper-lower smaller than new page size then extend lower
190             if ($self->upper - $self->lower < $page) {
191             $adjvalues{'lower'} = $self->upper - $page;
192             }
193              
194             # if bigger page size pushes value+page above upper then reduce value to max;
195             # if we were showing the end and a smaller page size means we no longer
196             # are then increase value to its max
197             my $max_value = $self->upper - $page;
198             if ($self->value > $max_value || $at_end) {
199             $adjvalues{'value'} = $max_value;
200             }
201              
202             ### HScale: "page=$page vpp=$self->{'value_per_pixel'} ppv=$self->{'pixel_per_value'}"
203             Gtk2::Ex::AdjustmentBits::set_maybe ($self, %adjvalues);
204             }
205              
206             sub empty {
207             my ($self) = @_;
208             Gtk2::Ex::AdjustmentBits::set_empty($self);
209             }
210              
211             # scroll by $count many steps
212             sub scroll_step {
213             my ($self, $count) = @_;
214             Gtk2::Ex::AdjustmentBits::set_maybe
215             ($self, value => $self->value + $self->step_increment * $count);
216             }
217              
218             1;
219             __END__