1package Prima::IntUtils; 2 3use strict; 4use warnings; 5use Prima::Const; 6 7package Prima::MouseScroller; 8 9my $scrollTimer; 10 11sub scroll_timer_active 12{ 13 return 0 unless defined $scrollTimer; 14 return $scrollTimer-> {active}; 15} 16 17sub scroll_timer_semaphore 18{ 19 return 0 unless defined $scrollTimer; 20 $#_ ? 21 $scrollTimer-> {semaphore} = $_[1] : 22 return $scrollTimer-> {semaphore}; 23} 24 25sub scroll_timer_stop 26{ 27 return unless defined $scrollTimer; 28 $scrollTimer-> stop; 29 $scrollTimer-> {active} = 0; 30 $scrollTimer-> timeout( $scrollTimer-> {firstRate}); 31 $scrollTimer-> {newRate} = $scrollTimer-> {nextRate}; 32} 33 34sub scroll_timer_start 35{ 36 my $self = $_[0]; 37 $self-> scroll_timer_stop; 38 unless ( defined $scrollTimer) { 39 my @rates = $::application-> get_scroll_rate; 40 $scrollTimer = Prima::Timer-> create( 41 owner => $::application, 42 timeout => $rates[0], 43 name => q(ScrollTimer), 44 onTick => sub { $_[0]-> {delegator}-> ScrollTimer_Tick( @_)}, 45 onDestroy => sub { undef $scrollTimer }, 46 ); 47 @{$scrollTimer}{qw(firstRate nextRate newRate)} = (@rates,$rates[1]); 48 } 49 $scrollTimer-> {delegator} = $self; 50 $scrollTimer-> {semaphore} = 1; 51 $scrollTimer-> {active} = 1; 52 $scrollTimer-> start; 53} 54 55sub ScrollTimer_Tick 56{ 57 my ( $self, $timer) = @_; 58 if ( exists $scrollTimer-> {newRate}) 59 { 60 $timer-> timeout( $scrollTimer-> {newRate}); 61 delete $scrollTimer-> {newRate}; 62 } 63 $scrollTimer-> {semaphore} = 1; 64 $self-> notify(q(MouseMove), 0, $self-> pointerPos); 65 $self-> scroll_timer_stop unless defined $self-> {mouseTransaction}; 66} 67 68package Prima::IntIndents; 69 70sub indents 71{ 72 return wantarray ? @{$_[0]-> {indents}} : [@{$_[0]-> {indents}}] unless $#_; 73 my ( $self, @indents) = @_; 74 @indents = @{$indents[0]} if ( scalar(@indents) == 1) && ( ref($indents[0]) eq 'ARRAY'); 75 for ( @indents) { 76 $_ = 0 if $_ < 0; 77 } 78 $self-> {indents} = \@indents; 79} 80 81sub get_active_area 82{ 83 my @r = ( scalar @_ > 2) ? @_[2,3] : $_[0]-> size; 84 my $i = $_[0]-> {indents}; 85 if ( !defined($_[1]) || $_[1] == 0) { 86 # returns inclusive - exclusive 87 return $$i[0], $$i[1], $r[0] - $$i[2], $r[1] - $$i[3]; 88 } elsif ( $_[1] == 1) { 89 # returns inclusive - inclusive 90 return $$i[0], $$i[1], $r[0] - $$i[2] - 1, $r[1] - $$i[3] - 1; 91 } else { 92 # returns size 93 return $r[0] - $$i[0] - $$i[2], $r[1] - $$i[1] - $$i[3]; 94 } 95} 96 97package Prima::GroupScroller; 98use vars qw(@ISA); 99@ISA = qw(Prima::IntIndents); 100 101use Prima::ScrollBar; 102 103sub setup_indents 104{ 105 my ($self) = @_; 106 $self-> {indents} = [ 0,0,0,0]; 107 my $bw = $self-> {borderWidth}; 108 $self-> {indents}-> [$_] += $bw for 0..3; 109 $self-> {indents}-> [1] += $self-> {hScrollBar}-> height - 1 if $self-> {hScroll}; 110 $self-> {indents}-> [2] += $self-> {vScrollBar}-> width - 1 if $self-> {vScroll}; 111} 112 113sub set_border_width 114{ 115 my ( $self, $bw) = @_; 116 117 my @size = $self-> size; 118 $bw = 0 if $bw < 0; 119 $bw = 1 if $bw > $size[1] / 2; 120 $bw = 1 if $bw > $size[0] / 2; 121 return if $bw == $self-> {borderWidth}; 122 123 my $obw = $self-> {borderWidth}; 124 $self-> {borderWidth} = $bw; 125 126 $self-> {hScrollBar}-> set( 127 left => $bw - 1, 128 bottom => $bw - 1, 129 width => $size[0] - 130 $bw * 2 + 131 2 - 132 ( $self-> {vScroll} ? 133 $self-> {vScrollBar}-> width - 2 : 134 0 135 ), 136 ) if $self-> {hScroll}; 137 138 $self-> {vScrollBar}-> set( 139 top => $size[1] - $bw + 1, 140 right => $size[0] - $bw + 1, 141 bottom => $bw + ( $self-> {hScroll} ? 142 $self-> {hScrollBar}-> height - 2 : 143 0 144 ), 145 ) if $self-> {vScroll}; 146 147 $self-> insert_bone if defined $self-> {bone}; 148 149 $self-> setup_indents; 150 $self-> reset_indents; 151} 152 153sub reset_indents {} 154 155sub insert_bone 156{ 157 my $self = $_[0]; 158 my $bw = $self-> {borderWidth}; 159 $self-> {bone}-> destroy if defined $self-> {bone}; 160 161 $self-> {bone} = Prima::Widget-> new( 162 owner => $self, 163 name => q(Bone), 164 pointerType => cr::Arrow, 165 origin => [ $self-> width - $self-> {vScrollBar}-> width + 3 - $bw, $bw - 1], 166 size => [ $self-> {vScrollBar}-> width-2, $self-> {hScrollBar}-> height-1], 167 growMode => gm::GrowLoX, 168 widgetClass => wc::ScrollBar, 169 designScale => undef, 170 onPaint => sub { 171 my ( $self, $canvas, $owner, $w, $h) = 172 ($_[0], $_[1], $_[0]-> owner, $_[0]-> size); 173 $canvas-> color( $self-> backColor); 174 $canvas-> bar( 0, 1, $w - 2, $h - 1); 175 $canvas-> color( $owner-> light3DColor); 176 $canvas-> line( 0, 0, $w - 1, 0); 177 $canvas-> line( $w - 1, 0, $w - 1, $h - 1); 178 }, 179 ); 180} 181 182sub set_h_scroll 183{ 184 my ( $self, $hs) = @_; 185 return if ($hs ? 1 : 0) == $self-> {hScroll}; 186 my $bw = $self-> {borderWidth} || 0; 187 if ( $hs) { 188 $self-> {hScrollBar} = $self->{scrollBarClass}-> new( 189 owner => $self, 190 name => q(HScroll), 191 vertical => 0, 192 origin => [ $bw-1, $bw-1], 193 growMode => gm::GrowHiX, 194 pointerType => cr::Arrow, 195 width => $self-> width - 196 2 * $bw + 2 - 197 ( $self-> {vScroll} ? 198 $self-> {vScrollBar}-> width - 2 : 199 0), 200 delegations => ['Change'], 201 designScale => undef, 202 %{ $self->{hScrollBarProfile} || {} }, 203 ); 204 $self-> {hScroll} = 1; 205 206 $self-> setup_indents; 207 208 if ( $self-> {vScroll}) { 209 my $h = $self-> {hScrollBar}-> height; 210 $self-> {vScrollBar}-> set( 211 bottom => $self-> {vScrollBar}-> bottom + $h - 2, 212 top => $self-> {vScrollBar}-> top, 213 ); 214 $self-> insert_bone; 215 } 216 } else { 217 $self-> {hScroll} = 0; 218 $self-> setup_indents; 219 $self-> {hScrollBar}-> destroy; 220 221 if ( $self-> {vScroll}) 222 { 223 $self-> {vScrollBar}-> set( 224 bottom => $bw, 225 height => $self-> height - $bw * 2, 226 ); 227 $self-> {bone}-> destroy; 228 delete $self-> {bone}; 229 } 230 } 231 $self-> reset_indents; 232} 233 234sub set_v_scroll 235{ 236 my ( $self, $vs) = @_; 237 return if ($vs ? 1 : 0) == $self-> {vScroll}; 238 239 my $bw = $self-> {borderWidth} || 0; 240 my @size = $self-> size; 241 242 if ( $vs) { 243 my $width = exists( $self->{vScrollBarProfile}->{width} ) ? 244 $self->{vScrollBarProfile}->{width} : 245 $Prima::ScrollBar::stdMetrics[0]; 246 $self-> {vScrollBar} = $self->{scrollBarClass}-> new( 247 owner => $self, 248 name => q(VScroll), 249 vertical => 1, 250 left => $size[0] - $bw - $width + 1, 251 top => $size[1] - $bw + 1, 252 bottom => $bw + ( $self-> {hScroll} ? $self-> {hScrollBar}-> height - 2 : 0), 253 growMode => gm::GrowLoX | gm::GrowHiY, 254 pointerType => cr::Arrow, 255 delegations => ['Change'], 256 designScale => undef, 257 %{ $self->{vScrollBarProfile} || {} }, 258 ); 259 $self-> {vScroll} = 1; 260 261 $self-> setup_indents; 262 263 if ( $self-> {hScroll}) { 264 $self-> {hScrollBar}-> width( 265 $self-> {hScrollBar}-> width - 266 $self-> {vScrollBar}-> width + 2, 267 ); 268 $self-> insert_bone; 269 } 270 } else { 271 $self-> {vScroll} = 0; 272 $self-> setup_indents; 273 $self-> {vScrollBar}-> destroy; 274 if ( $self-> {hScroll}) 275 { 276 $self-> {hScrollBar}-> width( $size[0] - 2 * $bw + 2); 277 $self-> {bone}-> destroy; 278 delete $self-> {bone}; 279 } 280 } 281 $self-> reset_indents; 282} 283 284sub autoHScroll 285{ 286 return $_[0]-> {autoHScroll} unless $#_; 287 my $v = ( $_[1] ? 1 : 0); 288 return unless $v != $_[0]-> {autoHScroll}; 289 $_[0]-> {autoHScroll} = $v; 290} 291 292sub autoVScroll 293{ 294 return $_[0]-> {autoVScroll} unless $#_; 295 my $v = ( $_[1] ? 1 : 0); 296 return unless $v != $_[0]-> {autoVScroll}; 297 $_[0]-> {autoVScroll} = $v; 298} 299 300sub borderWidth {($#_)?($_[0]-> set_border_width( $_[1])):return $_[0]-> {borderWidth}} 301sub hScroll {($#_)?$_[0]-> set_h_scroll ($_[1]):return $_[0]-> {hScroll}} 302sub vScroll {($#_)?$_[0]-> set_v_scroll ($_[1]):return $_[0]-> {vScroll}} 303 304sub draw_border 305{ 306 my ( $self, $canvas, $backColor, @size) = @_; 307 308 @size = $self-> size unless @size; 309 $self-> rect_bevel( 310 $canvas, 311 0, 0, 312 $size[0]-1, $size[1]-1, 313 width => $self-> {borderWidth}, 314 panel => 1, 315 fill => $backColor, 316 ); 317} 318 319package Prima::UndoActions; 320 321sub init_undo 322{ 323 my ($self, $profile) = @_; 324 $self-> {undo} = []; 325 $self-> {redo} = []; 326 $self-> {undoLimit} = $profile->{undoLimit}; 327} 328 329sub begin_undo_group 330{ 331 my $self = $_[0]; 332 return if !$self-> {undoLimit}; 333 if ( $self-> {undo_in_action}) { 334 push @{$self-> {redo}}, [] unless $self-> {grouped_undo}++; 335 } else { 336 push @{$self-> {undo}}, [] unless $self-> {grouped_undo}++; 337 $self-> {redo} = [] if !$self-> {redo_in_action}; 338 } 339} 340 341sub end_undo_group 342{ 343 my $self = $_[0]; 344 return if !$self-> {undoLimit}; 345 346 my $ref = $self-> {undo_in_action} ? 'redo' : 'undo'; 347 $self-> {grouped_undo}-- if $self-> {grouped_undo} > 0; 348 # skip last record if empty 349 pop @{$self-> {$ref}} 350 if !$self-> {grouped_undo} && 351 @{$self-> {$ref}} && 352 0 == @{$self-> {$ref}-> [-1]}; 353 shift @{$self-> {$ref}} if @{$self-> {$ref}} > $self-> {undoLimit}; 354} 355 356sub push_undo_action 357{ 358 my $self = shift; 359 return if !$self-> {undoLimit}; 360 361 my $ref = $self-> {undo_in_action} ? 'redo' : 'undo'; 362 my $action = [ @_ ]; 363 if ( $self-> {grouped_undo}) { 364 push @{$self-> {$ref}}, [] unless @{$self-> {$ref} // []}; 365 push @{$self-> {$ref}-> [-1]}, $action; 366 } else { 367 push @{$self-> {$ref}}, [ $action ]; 368 shift @{$self-> {$ref}} if @{$self-> {$ref}} > $self-> {undoLimit}; 369 $self-> {redo} = [] 370 if !$self-> {redo_in_action} && !$self-> {undo_in_action}; 371 } 372} 373 374sub has_undo_action 375{ 376 my ($self, $method) = @_; 377 my $has = 0; 378 if ( !$self-> {undo_in_action} && @{$self-> {undo} // []} && @{$self-> {undo}-> [-1]}) { 379 my $ok = 1; 380 for ( @{$self-> {undo}-> [-1]}) { 381 $ok = 0, last if $$_[0] ne $method; 382 } 383 $has = 1 if $ok; 384 } 385 return $has; 386} 387 388sub push_group_undo_action 389{ 390 my $self = shift; 391 return if !$self-> {undoLimit}; 392 my $ref = $self-> {undo_in_action} ? 'redo' : 'undo'; 393 return $self-> push_undo_action(@_) if $self-> {grouped_undo}; 394 395 push @{$self-> {$ref}}, [] unless @{$self-> {$ref}}; 396 $self-> {grouped_undo} = 1; 397 $self-> push_undo_action(@_); 398 $self-> {grouped_undo} = 0; 399} 400 401sub can_undo { scalar shift @{ shift->{undo} } } 402sub can_redo { scalar shift @{ shift->{redo} } } 403 404sub undo 405{ 406 my $self = $_[0]; 407 return if $self-> {undo_in_action} || !$self-> {undoLimit}; 408 return unless @{$self-> {undo}}; 409 my $group = pop @{$self-> {undo}}; 410 return unless $group && @$group; 411 412 $self-> {undo_in_action} = 1; 413 $self-> begin_undo_group; 414 for ( reverse @$group) { 415 my ( $method, @params) = @$_; 416 next unless $self-> can($method); 417 $self-> $method( @params); 418 } 419 $self-> end_undo_group; 420 $self-> {undo_in_action} = 0; 421} 422 423sub redo 424{ 425 my $self = $_[0]; 426 return if !$self-> {undoLimit}; 427 return unless @{$self-> {redo}}; 428 my $group = pop @{$self-> {redo}}; 429 return unless $group && @$group; 430 431 $self-> {redo_in_action} = 1; 432 $self-> begin_undo_group; 433 for ( reverse @$group) { 434 my ( $method, @params) = @$_; 435 next unless $self-> can($method); 436 $self-> $method( @params); 437 } 438 $self-> end_undo_group; 439 $self-> {redo_in_action} = 0; 440} 441 442sub undoLimit 443{ 444 return $_[0]-> {undoLimit} unless $#_; 445 446 my ( $self, $ul) = @_; 447 $self-> {undoLimit} = $ul if $ul >= 0; 448 splice @{$self-> {undo}}, 0, $ul - @{$self-> {undo}} if @{$self-> {undo}} > $ul; 449} 450 451package Prima::ListBoxUtils; 452 453sub draw_item_background 454{ 455 my ( $self, $canvas, $left, $bottom, $right, $top, $prelight, $back_color ) = @_; 456 if ( $prelight ) { 457 $back_color //= $canvas-> backColor; 458 my $c = $self-> color; 459 $canvas-> new_gradient( 460 spline => [ 0.75, 0.25 ], 461 palette => [ $self->prelight_color($back_color), $back_color ], 462 )-> bar( $left, $bottom, $right, $top, 0 ); 463 $self-> color($c); 464 } else { 465 $canvas-> backColor($back_color) if $back_color; 466 $canvas-> clear( $left, $bottom, $right, $top); 467 } 468} 469 470package Prima::BidiInput; 471 472sub handle_bidi_input 473{ 474 my ( $self, %opt ) = @_; 475 476 my @ret; 477 if ( $opt{action} eq 'backspace') { 478 if ( $opt{at} == ($opt{rtl} ? 0 : $opt{n_clusters})) { 479 chop $opt{text}; 480 @ret = ( $opt{text}, length($opt{text})); 481 } else { 482 my $curpos = $opt{glyphs}->cursor2offset($opt{at}, $opt{rtl}); 483 if ( $curpos > 0 ) { 484 substr( $opt{text}, $curpos - 1, 1) = ''; 485 @ret = ( $opt{text}, $curpos - 1); 486 } 487 } 488 } elsif ( $opt{action} eq 'delete') { 489 my $curpos = $opt{glyphs}->cursor2offset($opt{at}, $opt{rtl}); 490 if ( $curpos < length($opt{text}) ) { 491 substr( $opt{text}, $curpos, 1, ''); 492 @ret = ( $opt{text}, $curpos); 493 } 494 } elsif ( $opt{action} eq 'cut') { 495 if ( $opt{at} != ($opt{rtl} ? 0 : $opt{n_clusters})) { 496 my ($pos, $len, $rtl) = $opt{glyphs}-> cluster2range( 497 $opt{at} - ($opt{rtl} ? 1 : 0)); 498 substr( $opt{text}, $pos + $len - 1, 1, ''); 499 @ret = ( $opt{text}, $pos + $len - 1); 500 } 501 } elsif ( $opt{action} eq 'insert') { 502 my $curpos = $opt{glyphs}->cursor2offset($opt{at}, $opt{rtl}); 503 substr( $opt{text}, $curpos, 0) = $opt{input}; 504 @ret = ( $opt{text}, $curpos ); 505 } elsif ( $opt{action} eq 'overtype') { 506 my ($append, $shift) = $opt{rtl} ? (0, 1) : ($opt{n_clusters}, 0); 507 my $curpos; 508 if ( $opt{at} == $append) { 509 $opt{text} .= $opt{input}; 510 $curpos = length($opt{text}); 511 } else { 512 my ($pos, $len, $rtl) = $opt{glyphs}-> cluster2range( $opt{at} - $shift ); 513 $pos += $len - 1 if $rtl; 514 substr( $opt{text}, $pos, 1) = $opt{input}; 515 $curpos = $pos + ($rtl ? 0 : 1); 516 } 517 @ret = ( $opt{text}, $curpos ); 518 } else { 519 Carp::cluck("bad input $opt{action}"); 520 } 521 return @ret; 522} 523 5241; 525 526=head1 NAME 527 528Prima::IntUtils - internal functions 529 530=head1 DESCRIPTION 531 532The module provides packages, containing common functionality 533for some standard classes. The packages are designed as a code 534containers, not as widget classes, and are to be used as 535secondary ascendants in the widget inheritance declaration. 536 537=head1 Prima::MouseScroller 538 539Implements routines for emulation of auto repeating mouse events. 540A code inside C<MouseMove> callback can be implemented by 541the following scheme: 542 543 if ( mouse_pointer_inside_the_scrollable_area) { 544 $self-> scroll_timer_stop; 545 } else { 546 $self-> scroll_timer_start unless $self-> scroll_timer_active; 547 return unless $self-> scroll_timer_semaphore; 548 $self-> scroll_timer_semaphore( 0); 549 } 550 551The class uses a semaphore C<{mouseTransaction}>, which should 552be set to non-zero if a widget is in mouse capture state, and set 553to zero or C<undef> otherwise. 554 555The class starts an internal timer, which sets a semaphore and 556calls C<MouseMove> notification when triggered. The timer is 557assigned the timeouts, returned by C<Prima::Application::get_scroll_rate> 558( see L<Prima::Application/get_scroll_rate> ). 559 560=head2 Methods 561 562=over 563 564=item scroll_timer_active 565 566Returns a boolean value indicating if the internal timer is started. 567 568=item scroll_timer_semaphore [ VALUE ] 569 570A semaphore, set to 1 when the internal timer was triggered. It is advisable 571to check the semaphore state to discern a timer-generated event from 572the real mouse movement. If VALUE is specified, it is assigned to the semaphore. 573 574=item scroll_timer_start 575 576Starts the internal timer. 577 578=item scroll_timer_stop 579 580Stops the internal timer. 581 582=back 583 584=head1 Prima::IntIndents 585 586Provides the common functionality for the widgets that delegate part of their 587surface to the border elements. A list box can be of an example, where its 588scroll bars and 3-d borders are such elements. 589 590=head2 Properties 591 592=over 593 594=item indents ARRAY 595 596Contains four integers, specifying the breadth of decoration elements for 597each side. The first integer is width of the left element, the second - height 598of the lower element, the third - width of the right element, the fourth - height 599of the upper element. 600 601The property can accept and return the array either as a four scalars, or as 602an anonymous array of four scalars. 603 604=back 605 606=head2 Methods 607 608=over 609 610=item get_active_area [ TYPE = 0, WIDTH, HEIGHT ] 611 612Calculates and returns the extension of the area without the border elements, 613or the active area. 614The extension are related to the current size of a widget, however, can be 615overridden by specifying WIDTH and HEIGHT. TYPE is an integer, indicating 616the type of calculation: 617 618=over 619 620=item TYPE = 0 621 622Returns four integers, defining the area in the inclusive-exclusive coordinates. 623 624=item TYPE = 1 625 626Returns four integers, defining the area in the inclusive-inclusive coordinates. 627 628=item TYPE = 2 629 630Returns two integers, the size of the area. 631 632=back 633 634=back 635 636=head1 Prima::GroupScroller 637 638The class is used for widgets that contain optional scroll bars, and provides means for 639their maintenance. The class is the descendant of L<Prima::IntIndents>, and adjusts 640the L<indents> property when scrollbars are shown or hidden, or L<borderWidth> is changed. 641 642The class does not provide range selection for the scrollbars; the descentant classes 643must implement that. 644 645The descendant classes must follow the guidelines: 646 647=over 648 649=item * 650 651A class must provide C<borderWidth>, C<hScroll>, and C<vScroll> property keys in profile_default() . 652A class may provide C<autoHScroll> and C<autoVScroll> property keys in profile_default() . 653 654=item * 655 656A class' init() method must set C<{borderWidth}>, C<{hScroll}>, and C<{vScroll}> 657variables to 0 before the initialization, call C<setup_indents> method, 658and then assign the properties from the object profile. 659 660If a class provides C<autoHScroll> and C<autoVScroll> properties, these must be set to 6610 before the initialization. 662 663=item * 664 665If a class needs to overload one of C<borderWidth>, C<hScroll>, C<vScroll>, 666C<autoHScroll>, and C<autoVScroll> properties, 667it is mandatory to call the inherited properties. 668 669=item * 670 671A class must implement the scroll bar notification callbacks: C<HScroll_Change> and C<VScroll_Change>. 672 673=item * 674 675A class must not use the reserved variable names, which are: 676 677 {borderWidth} - internal borderWidth storage 678 {hScroll} - internal hScroll value storage 679 {vScroll} - internal vScroll value storage 680 {hScrollBar} - pointer to the horizontal scroll bar 681 {vScrollBar} - pointer to the vertical scroll bar 682 {bone} - rectangular widget between the scrollbars 683 {autoHScroll} - internal autoHScroll value storage 684 {autoVScroll} - internal autoVScroll value storage 685 686The reserved method names: 687 688 set_h_scroll 689 set_v_scroll 690 insert_bone 691 setup_indents 692 reset_indents 693 borderWidth 694 autoHScroll 695 autoVScroll 696 hScroll 697 vScroll 698 699The reserved widget names: 700 701 HScroll 702 VScroll 703 Bone 704 705=back 706 707=head2 Properties 708 709=over 710 711=item autoHScroll BOOLEAN 712 713Selects if the horizontal scrollbar is to be shown and hidden dynamically, 714depending on the widget layout. 715 716=item autoVScroll BOOLEAN 717 718Selects if the vertical scrollbar is to be shown and hidden dynamically, 719depending on the widget layout. 720 721=item borderWidth INTEGER 722 723Width of 3d-shade border around the widget. 724 725Recommended default value: 2 726 727=item hScroll BOOLEAN 728 729Selects if the horizontal scrollbar is visible. If it is, C<{hScrollBar}> 730points to it. 731 732=item vScroll BOOLEAN 733 734Selects if the vertical scrollbar is visible. If it is, C<{vScrollBar}> 735points to it. 736 737=item scrollBarClass STRING = Prima::ScrollBar 738 739Create-only property that allows to change scrollbar class 740 741=item hScrollBarProfile, vScrollBarProfile HASH 742 743Create-only property that allows to change scrollbar parameters when it is being created 744 745=back 746 747=head2 Properties 748 749=over 750 751=item setup_indents 752 753The method is never called directly; it should be called whenever widget 754layout is changed so that indents are affected. The method is a request 755to recalculate indents, depending on the widget layout. 756 757The method is not reentrant; to receive this callback and update the widget 758layout, that in turn can result in more C<setup_indents> calls, overload 759C<reset_indents> . 760 761=item reset_indents 762 763Called after C<setup_indents> is called and internal widget layout is updated, 764to give a chance to follow-up the layout changes. 765 766=back 767 768=head1 Prima::UndoActions 769 770Used for classes that can edit and undo and redo its content. 771 772=head2 Properties 773 774=over 775 776=item undoLimit INTEGER 777 778Sets limit on number of stored atomic undo operations. If 0, 779undo is disabled. 780 781=back 782 783=head2 Methods 784 785=over 786 787=item begin_undo_group 788 789Opens bracket for group of actions, undone as single operation. 790The bracket is closed by calling C<end_undo_group>. 791 792=item end_undo_group 793 794Closes bracket for group of actions, opened by C<begin_undo_group>. 795 796=item redo 797 798Re-applies changes, formerly rolled back by C<undo>. 799 800=item undo 801 802Rolls back changes into internal array, which size cannot extend C<undoLimit> 803value. In case C<undoLimit> is 0, no undo actions can be made. 804 805=back 806 807=head1 AUTHOR 808 809Dmitry Karasik, E<lt>dmitry@karasik.eu.orgE<gt>. 810 811=head1 SEE ALSO 812 813L<Prima>, L<Prima::Widget>, L<Prima::InputLine>, L<Prima::Lists>, L<Prima::Edit>, 814L<Prima::Outlines>, L<Prima::ScrollBar>. 815 816=cut 817