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