1# contains
2#    DockManager
3#    DockManager::LaunchPad
4#    DockManager::Toolbar
5#    DockManager::ToolbarDocker
6#    DockManager::Panelbar
7#    DockManager::S::SpeedButton;
8#
9
10use strict;
11use warnings;
12use Prima;
13use Prima::Utils;
14use Prima::Docks;
15use Prima::Notebooks;
16use Prima::Lists;
17use Prima::StdBitmap;
18
19package Prima::DockManager::LaunchPad;
20use vars qw(@ISA);
21@ISA = qw(Prima::Notebook Prima::SimpleWidgetDocker);
22
23sub profile_default
24{
25	my $def = $_[0]-> SUPER::profile_default;
26	my %prf = (
27		fingerprint => 0x0000FFFF,
28		dockup      => undef,
29	);
30	@$def{keys %prf} = values %prf;
31	return $def;
32}
33
34sub init
35{
36	my $self = shift;
37	my %profile = $self-> SUPER::init( @_);
38	$self-> $_( $profile{$_}) for ( qw(fingerprint dockup));
39	return %profile;
40}
41
42# inner part of toolbar tandem
43
44package Prima::DockManager::ToolbarDocker;
45use vars qw(@ISA);
46@ISA = qw(Prima::SingleLinearWidgetDocker);
47
48sub profile_default
49{
50	my $def = $_[0]-> SUPER::profile_default;
51	my %prf = (
52		parentDocker => undef,
53		instance     => undef,
54		x_sizeable   => 0,
55		y_sizeable   => 0,
56	);
57	@$def{keys %prf} = values %prf;
58	return $def;
59}
60
61sub init
62{
63	my ($self, %profile) = @_;
64	%profile = $self-> SUPER::init( %profile);
65	$self-> $_($profile{$_}) for qw( parentDocker instance);
66	return %profile;
67}
68
69sub get_extent
70{
71	my $self = $_[0];
72	my @ext = (0,0);
73	for ($self-> docklings) {
74		my @z = $_-> rect;
75		for (0,1) {
76			$ext[$_] = $z[$_+2] if $ext[$_] < $z[$_+2]
77		}
78	}
79	return @ext;
80}
81
82sub update_size
83{
84	my ( $self, @sz) = @_;
85	my $o = $self-> parentDocker;
86	return unless $o;
87	@sz = $self-> size unless scalar @sz;
88	my @r  = $o-> client2frame( 0, 0, @sz);
89	$o-> size( $r[2] - $r[0], $r[3] - $r[1]);
90	$self-> size( @sz);
91	if ( $o-> dock) {
92		$o-> redock;
93	} else {
94		@r = $o-> externalDocker-> client2frame( 0,0, @sz);
95		$o-> externalDocker-> size($r[2] - $r[0], $r[3] - $r[1]);
96		$self-> rect( 0,0,@sz); # respect growMode
97	}
98}
99
100# this part is responsible for changing toolbar's size when new tools are docked in
101sub dock
102{
103	return $_[0]-> {dock} unless $#_;
104	my $self = shift;
105	my @sz = $self-> size;
106	$self-> SUPER::dock(@_);
107	my @sz1 = $self-> size;
108	my $resize = 0;
109	my @ext = $self-> get_extent;
110	for ( 0, 1) {
111		$resize = 1, $sz1[$_] = $ext[$_] if $sz1[$_] > $ext[$_];
112	}
113	return if !$resize && ($sz[0] == $sz1[0] && $sz[1] == $sz1[1]);
114	$self-> size( @sz1);
115	$self-> update_size( @sz1);
116}
117
118sub rearrange
119{
120	my $self = $_[0];
121# fast version of rearrange, without real redocking
122	my $v = $self-> vertical;
123	my @ext = (0,0);
124	my ( $xid, $yid) = ( $v ? 0 : 1, $v ? 1 : 0);
125	my $a;
126	for ( $self-> docklings) {
127		$a = $_ unless $a; #
128		my @sz = $_-> size;
129		$_-> origin( $v ? ( 0, $ext[1]) : ( $ext[0], 0));
130		$ext[$xid] = $sz[$xid] if $ext[$xid] < $sz[$xid];
131		$ext[$yid] += $sz[$yid];
132	}
133	if ( $a) {
134		$self-> size( @ext);
135		#innvoke dock-undock, just to be sure, but for only one widget
136		$self-> redock_widget( $a);
137	}
138}
139
140sub parentDocker
141{
142	return $_[0]-> {parentDocker} unless $#_;
143	$_[0]-> {parentDocker} = $_[1];
144}
145
146sub instance
147{
148	return $_[0]-> {instance} unless $#_;
149	$_[0]-> {instance} = $_[1];
150}
151
152sub on_dockerror
153{
154	my ( $self, $urchin) = @_;
155	$self-> redock_widget( $urchin);
156}
157
158sub on_undock
159{
160	my $self = $_[0];
161	return if scalar(@{$self->{docklings}});
162	my $i = $self-> instance;
163	$i-> post( sub {
164		return if scalar(@{$self->{docklings}});
165		if ( $self-> parentDocker-> autoClose) {
166			$self-> parentDocker-> destroy;
167		} else {
168			$i-> toolbar_visible( $self-> parentDocker, 0);
169			$i-> notify(q(ToolbarChange));
170		}
171	});
172}
173
174package Prima::DockManager::Toolbar;
175use vars qw(@ISA);
176@ISA = qw(Prima::LinearDockerShuttle);
177
178sub profile_default
179{
180	my $def = $_[0]-> SUPER::profile_default;
181	my %prf = (
182		instance    => undef,
183		childDocker => undef,
184		autoClose   => 1,
185	);
186	@$def{keys %prf} = values %prf;
187	return $def;
188}
189
190sub init
191{
192	my $self = shift;
193	my %profile = $self-> SUPER::init( @_);
194	$self-> $_( $profile{$_}) for ( qw(instance childDocker autoClose));
195	return %profile;
196}
197
198sub autoClose
199{
200	return $_[0]-> {autoClose} unless $#_;
201	$_[0]-> {autoClose} = $_[1];
202}
203
204sub childDocker
205{
206	return $_[0]-> {childDocker} unless $#_;
207	$_[0]-> {childDocker} = $_[1];
208}
209
210sub instance
211{
212	return $_[0]-> {instance} unless $#_;
213	$_[0]-> {instance} = $_[1];
214}
215
216sub on_getcaps
217{
218	my ( $self, $docker, $prf) = @_;
219	$self-> SUPER::on_getcaps( $docker, $prf);
220	my @cd = $self-> {childDocker}-> size;
221	my @i = @{$self-> {indents}};
222	my @sz = ($i[2] + $i[0] + $cd[0], $i[3] + $i[1] + $cd[1]);
223	$prf-> {sizeMin} = [ @sz];
224	my $vs = $docker-> can('vertical') ? $docker-> vertical : 0;
225	my $v  = $self-> {vertical};
226	$prf-> {sizes} = ( $v == $vs) ? [[@sz]] : [[@sz[1,0]]];
227}
228
229sub on_dock
230{
231	my $self = $_[0];
232	my $nv   = $self-> dock-> can('vertical') ? $self-> dock-> vertical : 0;
233	return if $nv == $self-> {vertical};
234	$self-> vertical( $nv);
235	my $c = $self-> {childDocker};
236	$c-> vertical( $nv);
237	$c-> rect( $self-> frame2client( 0, 0, $self-> size));
238	$c-> rearrange;
239}
240
241package Prima::DockManager::Panelbar;
242use vars qw(@ISA);
243@ISA = qw(Prima::LinearDockerShuttle);
244
245sub profile_default
246{
247	my $def = $_[0]-> SUPER::profile_default;
248	my %prf = (
249		vertical    => 1,
250		instance    => undef,
251		externalDockerProfile =>  { borderStyle => bs::Sizeable },
252		x_sizeable  => 1,
253		y_sizeable  => 1,
254	);
255	@$def{keys %prf} = values %prf;
256	return $def;
257}
258
259sub init
260{
261	my $self = shift;
262	my %profile = $self-> SUPER::init( @_);
263	$self-> $_( $profile{$_}) for ( qw(instance));
264	return %profile;
265}
266
267sub instance
268{
269	return $_[0]-> {instance} unless $#_;
270	$_[0]-> {instance} = $_[1];
271}
272
273
274# flags for fingerprints - for different dockers and stages.
275package
276    dmfp;
277
278use constant Tools     => 0x0F000; # those who want tools, must set this
279use constant Toolbar   => 0x10000; # those who want toolbars, must set this
280use constant LaunchPad => 0x20000; # tools that want to be disposable, set this
281
282package Prima::DockManager;
283use vars qw(@ISA);
284@ISA = qw(Prima::Component Prima::AbstractDocker::Interface);
285
286{
287my %RNT = (
288	%{Prima::Component->notification_types()},
289	CommandChange  => nt::Notification,
290	ToolbarChange  => nt::Notification,
291	PanelChange    => nt::Notification,
292	Command        => nt::Command,
293);
294
295sub notification_types { return \%RNT; }
296}
297
298sub profile_default
299{
300	my $def = $_[0]-> SUPER::profile_default;
301	my %prf = (
302		interactiveDrag => 0,
303		commands        => {},
304	);
305	@$def{keys %prf} = values %prf;
306	return $def;
307}
308
309sub init
310{
311	my $self = shift;
312	$self-> {commands} = {};
313	$self-> {interactiveDrag} = 0;
314	my %profile = $self-> SUPER::init( @_);
315	$self-> {classes} = [];
316	$self-> {hiddenToolbars} = [];
317	$self-> {toolbars} = [];
318	$self-> {panels} = [];
319	$self-> $_( $profile{$_}) for ( qw( commands interactiveDrag));
320	return %profile;
321}
322
323sub interactiveDrag
324{
325	return $_[0]-> {interactiveDrag} unless $#_;
326	my ( $self, $id) = @_;
327	return if $id == $self-> {interactiveDrag};
328	$self-> {interactiveDrag} = $id;
329	if ( $id) {
330		for ( $self-> toolbars)  {
331			$_-> enabled(1) for $_-> childDocker-> docklings;
332		}
333	} else {
334		my $c = $self-> {commands};
335		for ( $self-> toolbars)  {
336			for ( $_-> childDocker-> docklings) {
337				next if !defined $_->{CLSID} || !exists($c-> {$_->{CLSID}}) || $c-> {$_->{CLSID}};
338				$_-> enabled(0);
339			}
340		}
341	}
342}
343
344sub toolbars { return @{$_[0]->{toolbars}}}
345sub panels   { return @{$_[0]->{panels}}}
346
347sub fingerprint { return 0xFFFFFFFF }
348
349sub register_tool
350{
351	my ( $self, $CLSID, $hash) = @_;
352	$hash-> {tool} = 1;
353	push @{$self-> {classes}}, $CLSID, $hash;
354}
355
356sub register_panel
357{
358	my ( $self, $CLSID, $hash) = @_;
359	$hash-> {tool} = 0;
360	push @{$self-> {classes}}, $CLSID, $hash;
361}
362
363sub get_class
364{
365	my ( $self, $CLSID) = @_;
366	my $i;
367	my $c = $self-> {classes};
368	my $x = scalar @$c;
369	for ( $i = 0; $i < $x; $i+=2) {
370		return $$c[$i+1] if $CLSID eq $$c[$i];
371	}
372}
373
374sub panel_by_id
375{
376	my ( $self, $name) = @_;
377	for ( @{$self-> {panels}}) {
378		return $_ if $_-> {CLSID} eq $name;
379	}
380}
381
382sub toolbar_by_name
383{
384	my ( $self, $name) = @_;
385	for ( @{$self-> {toolbars}}) {
386		return $_ if $_-> name eq $name;
387	}
388}
389
390sub post
391{
392	my ( $self, $sub, @parms) = @_;
393	$self-> post_message( 'sub', [ $sub, @parms]);
394}
395
396sub on_postmessage
397{
398	my ( $self, $id, $val) = @_;
399	return unless $id eq 'sub';
400	my $sub = shift @$val;
401	$sub->( $self, @$val);
402}
403
404sub create_manager
405{
406	my ( $self, $where, %profile) = @_;
407	my @o   = exists($profile{origin}) ? @{$profile{origin}} : (0,0);
408	my @sz  = exists($profile{size})   ? @{$profile{size}}   : ($where-> size);
409	my ( @items, %items);
410	my @lclasses;
411	my $i = 0;
412	my $cls = $self-> {classes};
413	my $xcls = scalar @$cls;
414	for ( $i = 0; $i < $xcls; $i += 2) {
415		my ( $CLSID, $hash) = @$cls[$i, $i+1];
416		next unless $hash->{tool};
417		push ( @lclasses, $CLSID);
418		$CLSID =~ m/^([^\:]+)(?:\:|$)/;
419		next if !$1 || exists($items{$1});
420		$items{$1} = 1;
421		push( @items, $1);
422	}
423	$i = 0;
424	my $nb;
425	my $lb = $where-> insert( ListBox =>
426		origin  => [@o],
427		size    => [ int($sz[0] / 3), $sz[1]],
428		name    => 'GroupSelector',
429		vScroll => 1,
430		items   => \@items,
431		growMode=> gm::Client,
432		focusedItem => 0,
433		onSelectItem => sub {
434			my ($self,$lst,$state) = @_;
435			return unless $state;
436			$nb-> pageIndex( $self-> focusedItem);
437		},
438		exists($profile{listboxProfile}) ? %{$profile{listboxProfile}} : (),
439	);
440	$nb = $where-> insert( 'Prima::DockManager::LaunchPad' =>
441		exists($profile{dockerProfile}) ? %{$profile{dockerProfile}} : (),
442		origin      => [ $o[0] + int($sz[0] / 3), 0],
443		size        => [ $sz[0] - int($sz[0] / 3), $sz[1]],
444		name        => 'PageFlipper',
445		pageCount   => scalar(@items),
446		growMode    => gm::GrowHiY|gm::GrowLoX,
447		fingerprint => dmfp::LaunchPad,
448		dockup      => $self,
449	);
450	$nb-> {dockingRoot} = $self;
451	$i = 0;
452	my %x = @$cls;
453	my @szn = $nb-> size;
454
455	for ( @items) {
456		my $iid = $_;
457		my @d = grep { m/^([^\:]+)(?:\:|$)/; my $j = $1 || ''; $j eq $iid; } @lclasses;
458		my @org  = (5,5);
459		my $maxy = 0;
460		my @ctrls;
461		my $ix = 0;
462		for ( @d) {
463			my %acp = exists($x{$_}-> {profile}) ? %{$x{$_}-> {profile}} : ();
464			my $ctrl = $nb-> insert_to_page( $i, $x{$_}->{class} =>
465				growMode => gm::GrowLoY,
466				%acp,
467				onMouseDown => \&Control_MouseDown_FirstStage,
468				onKeyDown   => \&Control_KeyDown,
469				origin      => [ @org],
470			);
471			$ctrl-> {CLSID} = $_;
472			$ctrl-> {DOCKMAN} = $self;
473			push ( @ctrls, $ctrl);
474			my @s = $ctrl-> size;
475			if (( $s[0] + $org[0] > $szn[0] - 5) && ( $ix > 0)) {
476				$ctrl-> origin( $org[0] = 5, $org[1] += $maxy + 3);
477				$maxy = 0;
478			} else {
479				$org[0] += $s[0] + 1;
480				$maxy = $s[1] if $maxy < $s[1];
481			}
482			$ix++;
483		}
484		if ( $org[1] + $maxy < $szn[1] - 5) {
485			my $d = $sz[1] - 5 - $org[1] - $maxy;
486			for ( @ctrls) {
487				my @o = $_-> origin;
488				$_-> origin( $o[0], $o[1] + $d);
489			}
490		}
491		$i++;
492	}
493	return $lb, $nb;
494}
495
496sub create_tool
497{
498	my ( $self, $where, $CLSID, @rect) = @_;
499	my $x = $self-> get_class( $CLSID);
500	return unless $x;
501	my %acp = exists($x-> {profile}) ? %{$x-> {profile}} : ();
502	%acp = ( %acp,
503		onMouseDown => \&Control_MouseDown,
504		onKeyDown   => \&Control_KeyDown,
505		onDestroy   => \&Control_Destroy,
506	);
507	$acp{rect} = \@rect if 4 == scalar @rect;
508	my $ctrl = $where-> insert( $x->{class} => %acp);
509	$ctrl-> {CLSID}   = $CLSID;
510	$ctrl-> {DOCKMAN} = $self;
511	$ctrl-> enabled(0) if !$self-> {interactiveDrag} && exists( $self-> {commands}->{$CLSID})
512	&& !$self-> {commands}->{$CLSID};
513	return $ctrl;
514}
515
516sub create_toolbar
517{
518	my ( $self, %profile) = @_;
519	my $v    = $profile{vertical} || 0;
520	my $dock = $profile{dock};
521	my $auto = exists( $profile{autoClose}) ? $profile{autoClose} : ( exists($profile{name}) ? 1 : 0);
522	my $name = $profile{name} || $self-> auto_toolbar_name;
523	my $visible = exists($profile{visible}) ? $profile{visible} : 1;
524	my @r    = $profile{rect} ? @{$profile{rect}} : ( 0, 0, 10, 10);
525	my $acd  = $profile{dockerProfile}  || {};
526	my $aci  = $profile{toolbarProfile} || {};
527
528	my $x = Prima::DockManager::Toolbar-> create(
529		dockingRoot => $self,
530		name        => $name,
531		text        => $name,
532		visible     => $visible,
533		vertical    => $v,
534		instance    => $self,
535		autoClose   => $auto,
536		onEDSClose  => \&Toolbar_EDSClose,
537		%$acd,
538	);
539
540	my @i = @{$x-> indents};
541	$x-> rect( $r[0] - $i[0], $r[1] - $i[1], $r[2] + $i[2], $r[3] + $i[3]);
542	@r = $x-> frame2client( $x-> rect);
543
544	my $xcl = $x-> insert( 'Prima::DockManager::ToolbarDocker' =>
545		%$aci,
546		origin       => [ @i[0,1]],
547		size         => [ $r[2] - $r[0], $r[3] - $r[1]],
548		vertical     => $v,
549		growMode     => gm::Client,
550		fingerprint  => dmfp::Toolbar,
551		parentDocker => $x,
552		name         => $name,
553		instance     => $self,
554		onDestroy   => \&Toolbar_Destroy,
555	);
556	$x-> client( $xcl);
557	$x-> childDocker( $xcl);
558	$self-> add_subdocker( $xcl);
559	if ( $profile{dock}) {
560		$x-> dock( $dock, $dock-> client_to_screen( $x-> rect));
561	} else {
562		$x-> externalDocker-> rect( $x-> externalDocker-> client2frame( @r));
563	}
564	push ( @{$self-> {toolbars}}, $x);
565	$self-> toolbar_visible( $x, 0) unless $visible;
566	$self-> notify(q(ToolbarChange));
567	return $x, $xcl;
568}
569
570sub create_panel
571{
572	my ( $self, $CLSID, %profile) = @_;
573	my $prf = $self-> get_class( $CLSID);
574	return unless $prf;
575	my %prf = (
576		dockingRoot         => $self,
577		x_sizeable          => 1,
578		y_sizeable          => 1,
579		instance            => $self,
580	);
581	$prf{text} = $prf-> {text} if exists $prf-> {text};
582	my $x = Prima::DockManager::Panelbar-> create( %prf,
583		$prf-> {dockerProfile} ? %{$prf-> {dockerProfile}} : (),
584		$profile{dockerProfile} ? %{$profile{dockerProfile}} : (),
585		dock  => undef,
586	);
587	$x-> onEDSClose( \&Panel_EDSClose);
588	$prf-> {text} = $x-> text unless exists $prf-> {text};
589	my @rrc = $x-> frame2client( 0, 0, $x-> size);
590	my $xcl = $x-> insert( $prf->{class} =>
591		growMode => gm::Client,
592		$prf-> {profile} ? %{$prf-> {profile}} : (),
593		$profile{profile} ? %{$profile{profile}} : (),
594		rect      => [@rrc],
595	);
596	$xcl-> add_notification( 'Destroy', \&Panelbar_Destroy, $x);
597	$x-> client( $xcl);
598	push( @{$self-> {panels}}, $x);
599	$x-> {CLSID} = $CLSID;
600	if ( $prf-> {dockerProfile}-> {dock} || $profile{dockerProfile}-> {dock}) {
601		my $dock = $prf-> {dockerProfile}-> {dock} || $profile{dockerProfile}-> {dock};
602		$x-> dock( $dock, $dock-> client_to_screen( $x-> rect));
603	}
604	$self-> notify(q(PanelChange));
605	return ( $x, $xcl);
606}
607
608sub auto_toolbar_name
609{
610	my $name;
611	my $self = $_[0];
612	my @ids;
613	for ( @{$self->{toolbars}}) {
614		my $x = $_-> name;
615		next unless $x =~ m/^ToolBar(\d+)$/;
616		$ids[$1] = 1;
617	}
618	my $i = 0;
619	for ( @ids) {
620		$i++, next unless $i; # skip ToolBar0
621		$name = $i, last unless $_;
622		$i++;
623	}
624	$name = scalar(@ids) unless defined $name;
625	$name++ unless $name;
626	return "ToolBar$name";
627}
628
629sub toolbar_menuitems
630{
631	my ( $self, $sub) = @_;
632	my @items;
633	for ( @{$self->{toolbars}}) {
634		my $vis = $_-> dock() ? $_-> visible : $_-> externalDocker-> visible;
635		$vis = ( $vis ? '*' : '') . $_-> name;
636		push ( @items, [ $vis => $_-> name => $sub ]);
637	}
638	return \@items;
639}
640
641sub panel_menuitems
642{
643	my ( $self, $sub) = @_;
644	my @items;
645	my %h = map { $_->{CLSID} => 1} @{$self-> {panels}};
646	my $i;
647	my $c = $self-> {classes};
648	for ( $i = 0; $i < @$c; $i += 2) {
649		my ( $CLSID, $hash) = @$c[$i,$i+1];
650		next if $hash->{tool};
651		my $vis = ( $h{$CLSID} ? '*' : '') . $CLSID;
652		push ( @items, [ $vis => $hash-> {text} => $sub ]);
653	}
654	return \@items;
655}
656
657sub toolbar_visible
658{
659	my ( $self, $d, $visible) = @_;
660	return unless $d;
661	if ( $d-> dock) {
662		return if $visible == $d-> visible;
663		if ( $visible) {
664			$d-> dock_back;
665		} else {
666			$d-> dock( undef);
667			$d-> externalDocker-> visible( $visible);
668		}
669	} else {
670		return if $visible == $d-> externalDocker-> visible;
671		$d-> externalDocker-> visible( $visible);
672	}
673}
674
675sub panel_visible
676{
677	my ( $self, $panelbarCLSID, $visible) = @_;
678	my $d = $self-> panel_by_id( $panelbarCLSID);
679	my $hash = $self-> get_class( $panelbarCLSID);
680	if ( $visible) {
681		return if $d;
682		my %pf;
683		if ( $hash-> {lastUsedDock} && Prima::Object::alive($hash-> {lastUsedDock})) {
684			$pf{dockerProfile}->{dock} = $hash-> {lastUsedDock};
685		}
686		if ( $hash-> {lastUsedRect}) {
687			$pf{dockerProfile}->{rect} = $hash-> {lastUsedRect};
688		}
689		my ( $x, $xcl) = $self-> create_panel( $panelbarCLSID, %pf);
690		$x-> bring_to_front;
691	} else {
692		return unless $d;
693		$hash-> {lastUsedDock} = $d-> dock;
694		$hash-> {lastUsedRect} = [$d-> dock ? $d-> rect : $d-> externalDocker-> rect],
695		$d-> close;
696	}
697}
698
699sub predefined_toolbars
700{
701	my $self = shift;
702	my %toolbars = map { $_-> name => $_ } @{$self-> {toolbars}};
703	my %c = @{$self-> {classes}};
704	my @o  = ( 10, $::application-> height - 100);
705	my @as = $::application-> size;
706	for ( @_) {
707		my $rec = $_;
708		next if $toolbars{$_-> {name}};
709		my @org = (0,0);
710		my $maxy = 0;
711		my @ctrls;
712		my @list = $rec->{list} ? @{$rec->{list}} : ();
713		for ( @list) {
714			my $ctrl = $self-> create_tool( $::application, $_);
715			next unless $ctrl;
716			$ctrl-> origin( @org);
717			my @sz = $ctrl-> size;
718			$org[0] += $sz[0];
719			$maxy = $sz[1] if $maxy < $sz[1];
720			push ( @ctrls, $ctrl);
721		}
722		my @oz = $rec->{origin} ? @{$rec->{origin}} : ( $rec->{dock} ? (0,0) : @o);
723		my ( $x, $xcl) = $self-> create_toolbar(
724			name    => $_->{name},
725			rect    => [ @oz, $oz[0]+$org[0], $oz[1]+$maxy],
726			visible => 1,
727			dock    => $rec->{dock},
728		);
729		for ( @ctrls) {
730			$_-> owner( $xcl);
731			$xcl-> dock( $_);
732		}
733		$xcl-> rearrange;
734		$o[0] += 25;
735		$o[1] -= 25;
736	}
737}
738
739sub predefined_panels
740{
741	my ( $self, @rec) = @_;
742	my $i;
743	my %pan = map { $_-> {CLSID} => 1 } @{$self-> {panels}};
744	for ( $i = 0; $i < scalar @rec; $i += 2) {
745		my ( $CLSID, $dock) = @rec[$i, $i+1];
746		next if $pan{$CLSID};
747		my ( $a, $b) = $self-> create_panel( $CLSID, dockerProfile => {dock => $dock});
748	}
749}
750
751sub activate
752{
753	my $self = $_[0];
754	for ( @{$self->{panels}}, @{$self-> {toolbars}}) {
755		next if $_-> dock;
756		$_-> externalDocker-> bring_to_front if $_-> externalDocker;
757	}
758}
759
760sub windowState
761{
762	my ( $self, $ws) = @_;
763	if ( $ws == ws::Minimized) {
764	for ( @{$self->{panels}}, @{$self-> {toolbars}}) {
765			next if $_-> dock;
766			my $e = $_-> externalDocker;
767			next unless $e;
768			$e-> hide;
769			push ( @{$self->{hiddenToolbars}}, $e);
770		}
771	} else {
772		$_-> show for @{$self->{hiddenToolbars}};
773		@{$self->{hiddenToolbars}} = ();
774	}
775}
776
777sub commands_enable
778{
779	my ( $self, $enable) = ( shift, shift);
780	my %cmds = map { $_ => 1 } @_;
781	unless ( $self-> interactiveDrag) {
782		for ( $self-> toolbars)  {
783			for ( $_-> childDocker-> docklings) {
784				next if !defined $_->{CLSID} || !$cmds{$_->{CLSID}} || $enable == $self-> {commands}->{$_->{CLSID}};
785				$_-> enabled( $enable);
786			}
787		}
788	}
789	for ( keys %{$self->{commands}}) {
790		next unless $cmds{$_};
791		$self-> {commands}-> {$_} = $enable;
792	}
793	$self-> notify(q(CommandChange));
794}
795
796sub commands
797{
798	return $_[0]-> {commands} unless $#_;
799	my ( $self, $cmds) = @_;
800	$self-> {commands} = $cmds;
801	unless ( $self-> interactiveDrag) {
802		for ( $self-> toolbars)  {
803			for ( $_-> childDocker-> docklings) {
804				next if !defined $_->{CLSID} || !$cmds-> {$_->{CLSID}};
805				$_-> enabled( $cmds-> {$_-> {CLSID}});
806			}
807		}
808	}
809	$self-> notify(q(CommandChange));
810}
811
812# internals
813
814sub autodock
815{
816	my ( $self, $ctrl) = @_;
817	my $dock = $ctrl-> owner;
818	$dock-> undock( $ctrl);
819
820	my ( $x, $xcl) = $self-> create_toolbar(
821		vertical  => $dock-> can('vertical') ? $dock-> vertical : 0,
822		dock      => $dock,
823		rect      => [$ctrl-> rect],
824		autoClose => 1,
825	);
826	$ctrl-> owner( $xcl);
827	$ctrl-> origin( 0,0);
828	$xcl-> dock( $ctrl);
829	return $x;
830}
831
832
833sub Control_KeyDown
834{
835	return unless $_[0]-> {DOCKMAN}-> interactiveDrag;
836	$_[0]-> clear_event;
837}
838
839sub Control_MouseDown_FirstStage
840{
841	my ($self,$btn, $mod, $x,$y) = @_;
842	return unless $btn == mb::Left;
843	my $man = $self-> {DOCKMAN};
844	my $c = Prima::InternalDockerShuttle-> create(
845		owner       => $self-> owner,
846		rect        => [$self-> rect],
847		dockingRoot => $man,
848		fingerprint => dmfp::LaunchPad | dmfp::Toolbar | dmfp::Tools, # allow all docks
849		backColor   => cl::Yellow,
850		onLanding   => \&InternalDockerShuttle_Landing,
851		name        => 'FirstStage',
852		onDock      => sub {
853			my $me = $_[0];
854			if ( $me-> owner-> isa(q(Prima::DockManager::LaunchPad))) {
855				$man-> post( sub { $me-> destroy; });
856				return;
857			}
858			my $ctrl = $man-> create_tool( $me-> owner, $self-> {CLSID}, $me-> rect);
859			return unless $ctrl;
860			$me-> {dock} = undef;
861			$me-> owner-> replace( $me, $ctrl);
862			$man-> post( sub { $me-> destroy; });
863			$man-> autodock( $ctrl)
864				unless $me-> owner-> isa(q(Prima::DockManager::ToolbarDocker));
865		},
866		onFailDock => sub {
867			my ( $me, $ax, $ay) = @_;
868			my ( $x, $xcl) = $man-> create_toolbar( rect => [$me-> rect], autoClose => 1);
869			$xcl-> dock( $man-> create_tool( $xcl, $self-> {CLSID}));
870			$x-> externalDocker-> origin( $ax, $ay);
871			$man-> post( sub { $me-> destroy; });
872		},
873	);
874	$c-> externalDocker-> hide;
875	$::application-> yield;
876	$c-> drag( 1, [ $self-> client_to_screen(0,0,$self-> size)], $c-> screen_to_client( $self-> client_to_screen($x, $y)));
877	$self-> clear_event;
878}
879
880sub Control_Destroy
881{
882	$_[0]-> owner-> undock( $_[0]);
883}
884
885sub Control_MouseDown
886{
887	my ( $self, $btn, $mod, $x, $y) = @_;
888	my $man = $self-> {DOCKMAN};
889	return unless $man-> interactiveDrag;
890	$self-> clear_event;
891	return unless $btn == mb::Left;
892
893	my $c;
894	$c = Prima::InternalDockerShuttle-> create(
895		owner       => $self-> owner,
896		rect        => [$self-> rect],
897		dockingRoot => $man,
898		fingerprint => dmfp::LaunchPad | dmfp::Toolbar | dmfp::Tools, # allow all docks
899		backColor   => cl::White,
900		onLanding   => \&InternalDockerShuttle_Landing,
901		name        => 'SecondStage',
902		onDock      => sub {
903			my $me = $_[0];
904			$me-> {dock} = undef;
905			$me-> owner-> replace( $me, $self);
906			$man-> post( sub { $me-> destroy; });
907			if ( $me-> owner-> isa(q(Prima::DockManager::LaunchPad))) {
908				$man-> post( sub { $self-> destroy; });
909				return;
910			}
911			$man-> autodock( $self) unless $me-> owner-> isa(q(Prima::DockManager::ToolbarDocker));
912		},
913		onFailDock => sub {
914			$self-> owner-> replace( $c, $self);
915			$c-> {dock} = undef;
916			$man-> post( sub { $c-> destroy; });
917		},
918	);
919	$c-> {dock} = $self-> owner;
920	$self-> owner-> replace( $self, $c);
921	$c-> externalDocker-> hide;
922	$c-> hide;
923	$::application-> yield;
924	$c-> drag( 1, [ $self-> client_to_screen(0,0,$self-> size)], $c-> screen_to_client( $self-> client_to_screen($x, $y)));
925}
926
927sub Panelbar_Destroy
928{
929	my $self = $_[0];
930	my $i = $self-> instance;
931	return unless $i;
932	@{$i-> {panels}} = grep { $_ != $self } @{$i->{panels}};
933	@{$i-> {hiddenToolbars}} = grep { $_ != $self } @{$i->{hiddenToolbars}};
934	$i-> notify(q(PanelChange));
935}
936
937sub Toolbar_Destroy
938{
939	my $self = $_[0]-> parentDocker;
940	my $i = $self-> instance;
941	return unless $i;
942	@{$i-> {toolbars}} = grep { $_ != $self } @{$i->{toolbars}};
943	@{$i-> {hiddenToolbars}} = grep { $_ != $self } @{$i->{hiddenToolbars}};
944	$i-> notify(q(ToolbarChange));
945}
946
947sub Toolbar_EDSClose
948{
949	my $e = $_[0]-> externalDocker;
950	$e-> hide;
951	$_[0]-> clear_event;
952	$_[0]-> instance-> notify(q(ToolbarChange));
953}
954
955sub Panel_EDSClose
956{
957	my $hash = $_[0]-> instance-> get_class($_[0]-> {CLSID});
958	return unless $hash;
959	$hash-> {lastUsedDock} = undef;
960	$hash-> {lastUsedRect} = [ $_[0]-> externalDocker-> rect ];
961	$_[0]-> instance-> notify(q(PanelChange));
962}
963
964sub InternalDockerShuttle_Landing
965{
966	my ( $self, $dm, @rc) = @_;
967	return unless $self-> {drag}; # only for interactive
968	my $wi = $::application-> get_widget_from_point($::application-> pointerPos);
969	return if !$wi || $wi == $dm;
970	unless ( $wi-> can('dock')) {
971		$wi = $wi-> owner;
972		return if $wi == $dm;
973	}
974	return unless $wi-> can('dock') && $wi-> isa('Prima::DockManager::ToolbarDocker');
975	$self-> clear_event;
976}
977
978package Prima::DockManager::S::SpeedButton;
979
980sub class
981{
982	my ( $image, $action, %profile) = @_;
983	$image =~ s/\:(\d+)$//;
984	my $index = $1 || 0;
985	my $i = Prima::Icon-> create;
986	undef($i) unless $i-> load(Prima::Utils::find_image($image), index => $index);
987	my $s = $::application ? $::application->uiScaling : 1;
988	$i->ui_scale if $i;
989	return $action, {
990		class   => 'Prima::SpeedButton',
991		profile => {
992			size        => [ 24 * $s, 24 * $s],
993			image       => $i,
994			borderWidth => 1,
995			onClick     => \&on_click,
996			%profile,
997		},
998	},
999}
1000
1001sub on_click
1002{
1003	$_[0]-> owner-> instance-> notify(q(Command), $_[0]-> {CLSID});
1004}
1005
10061;
1007
1008=pod
1009
1010=head1 NAME
1011
1012Prima::DockManager - advanced dockable widgets
1013
1014=head1 DESCRIPTION
1015
1016C<Prima::DockManager> contains classes that implement additional
1017functionality within the dockable widgets paradigm.
1018
1019The module introduces two new dockable widget classes:
1020C<Prima::DockManager::Panelbar>, a general purpose
1021dockable container for variable-sized widgets; and C<Prima::DockManager::Toolbar>,
1022a dockable container for fixed-size command widgets, mostly push buttons.
1023The command widgets, nested in a toolbar, can also be docked.
1024
1025C<Prima::DockManager> class is an application-oriented class in a way
1026that ( mostly ) the only instance of it is needed in the program. It
1027is derived from C<Prima::Component> and therefore is never visualized.
1028The class instance is stored in C<instance> property of all module classes
1029to serve as a docking hierarchy root. Through the document, I<instance>
1030term is referred to C<Prima::DockManager> class instance.
1031
1032The module by itself is not enough to make a docking-aware application work
1033effectively. The reader is urged to look at F<examples/dock.pl>
1034example code, which demonstrates the usage and capabilities of
1035the module.
1036
1037=head1 Prima::DockManager::Toolbar
1038
1039A toolbar widget class. The toolbar has a dual nature; it can dock
1040and accept docking widgets simultaneously. In the scope of C<Prima::DockManager>,
1041the toolbar hosts command widget, mostly push buttons.
1042
1043The toolbar consists of two widgets. The external dockable widget is
1044implemented in C<Prima::DockManager::Toolbar>, and the internal dock
1045in C<Prima::DockManager::ToolbarDocker> classes.
1046
1047=head2 Properties
1048
1049=over
1050
1051=item autoClose BOOLEAN
1052
1053Selects the behavior of a toolbar when all of its command widgets are
1054undocked. If 1, the toolbar is automatically destroyed. If 0
1055it calls C<visible(0)>.
1056
1057=item childDocker WIDGET
1058
1059Pointer to C<Prima::DockManager::ToolbarDocker> instance.
1060
1061See also C<Prima::DockManager::ToolbarDocker::parentDocker>.
1062
1063=item instance INSTANCE
1064
1065C<Prima::DockManager> instance, the docking hierarchy root.
1066
1067=back
1068
1069=head1 Prima::DockManager::ToolbarDocker
1070
1071Internal class, implements a dock widget for command widgets,
1072while serves as a client in a dockable toolbar, which is
1073a C<Prima::LinearDockerShuttle> descendant. When its size is
1074changed due an eventual rearrange of its docked widgets, also resizes
1075the toolbar.
1076
1077=head2 Properties
1078
1079=over
1080
1081=item instance INSTANCE
1082
1083C<Prima::DockManager> instance, the docking hierarchy root.
1084
1085=item parentDocker WIDGET
1086
1087Pointer to C<Prima::DockManager::Toolbar> instance. When in
1088the docked state, C<parentDocker> value is always equals to C<owner>.
1089
1090See also C<Prima::DockManager::Toolbar::childDocker>.
1091
1092=back
1093
1094=head2 Methods
1095
1096=over
1097
1098=item get_extent
1099
1100Calculates the minimal rectangle that encloses all docked widgets
1101and returns its extensions.
1102
1103=item update_size
1104
1105Called when size is changed to resizes the owner widget. If it is in the docked
1106state, the size change might result in change of position or docking state.
1107
1108=back
1109
1110=head1 Prima::DockManager::Panelbar
1111
1112The class is derived from C<Prima::LinearDockerShuttle>, and
1113is different only in that C<instance> property is introduced,
1114and the external shuttle can be resized interactively.
1115
1116The class is to be used as a simple host to sizeable widgets.
1117The user can dispose of the panel bar by clicking close button
1118on the external shuttle.
1119
1120=head2 Properties
1121
1122=over
1123
1124=item instance INSTANCE
1125
1126C<Prima::DockManager> instance, the docking hierarchy root.
1127
1128=back
1129
1130=head1 Prima::DockManager
1131
1132A binder class, contains set of functions that groups
1133toolbars, panels, and command widgets together under the docking
1134hierarchy.
1135
1136The manager servers several purposes.
1137First, it is a command state holder: the command
1138widgets, mostly buttons, usually are in enabled or disabled state in different
1139life stages of a program. The manager maintains the enabled/disabled state
1140by assigning each command an unique scalar value ( farther and in the
1141code referred as I<CLSID> ). The toolbars can be created with set of
1142command widgets, referred via these CLSIDs. The same is valid for
1143the panels - although they do not host command widgets, the widgets that
1144they do host can also be created indirectly via CLSID identifier.
1145In addition to CLSID, the commands can be grouped by sections.
1146Both CLSID and group descriptor scalars are defined by the programmer.
1147
1148Second, C<create_manager> method presents a standard configuration
1149widget, that allows rearranging of normally non-dockable command widgets,
1150by presenting a full set of available commands to the user as icons.
1151Dragging the icons to toolbars, dock widgets or merely outside the
1152configuration widget automatically creates the corresponding command widget.
1153The notable moment here is that the command widgets are not required
1154to know anything about dragging and docking; any C<Prima::Widget>
1155descendant can be used as a command widget.
1156
1157Third, it helps maintaining the toolbars and panels visibility
1158when the main window is hidden or restored. C<windowState> method
1159hides or shows the toolbars and panels effectively.
1160
1161Fourth, it serves as a docking hierarchy root. All docking sessions
1162begin from C<Prima::DockManager> object, which although does not provide
1163docking capabilities itself ( it is C<Prima::Component> descendant ),
1164redirects the docking requests to the lower-level dock widgets.
1165
1166Fifth, it provides number of helper methods and notifications,
1167and enforces use or C<fingerprint> property by all dockable widgets.
1168This property has default value of C<0xFFFF> ( defined in C<Prima::Docks> ).
1169The module contains the fingerprint C<dmfp::XXX> constants with value greater than this,
1170so the toolbars and panels are not docked to a dock widget with the default
1171configuration. The base constant set is:
1172
1173	fdmp::Tools      ( 0x0F000) - dock the command widgets
1174	fdmp::Toolbar    ( 0x10000) - dock the toolbars
1175	fdmp::LaunchPad  ( 0x20000) - allows widgets recycling
1176
1177All this functionality is demonstrated in F<examples/dock.pl>
1178example.
1179
1180=head2 Properties
1181
1182=over
1183
1184=item commands HASH
1185
1186A hash of boolean values, with keys of CLSID scalars.
1187If value is 1, the command is available. If 0, the command
1188is disabled. Changes to this property are reflected in the
1189visible command widgets, which are enabled or disabled
1190immediately. Also, C<CommandChange> notification is triggered.
1191
1192=item fingerprint INTEGER
1193
1194The property is read-only, and always returns C<0xFFFFFFFF>,
1195to allow landing for all dockable widgets. In case when a finer
1196granulation is needed, the default C<fingerprint> values of
1197toolbars and panels can be reset.
1198
1199=item interactiveDrag BOOLEAN
1200
1201If 1, the command widgets can be interactively dragged,
1202created and destroyed. This property is usually operated together
1203with C<create_manager> widget. If 0, the command widgets
1204cannot be dragged.
1205
1206Default value: 0
1207
1208=back
1209
1210=head2 Methods
1211
1212=over
1213
1214=item activate
1215
1216Brings to front all toolbars and panels. To be
1217used inside a callback code of a main window, that has
1218the toolbars and panels attached to:
1219
1220	onActivate => sub { $dock_manager-> activate }
1221
1222=item auto_toolbar_name
1223
1224Returns an unique name for an automatically created
1225toolbar, like C<Toolbar1>, C<Toolbar2> etc.
1226
1227=item commands_enable BOOLEAN, @CLSIDs
1228
1229Enabled or disables commands from CLSIDs array.
1230The changes are reflected in the visible command widgets, which
1231are enabled or disabled immediately.
1232Also, C<CommandChange> notification is triggered.
1233
1234=item create_manager OWNER, %PROFILE
1235
1236Inserts two widgets into OWNER with PROFILE parameters:
1237a listbox with command section groups, displayed as items, that usually correspond to
1238the predefined toolbar names, and a notebook that displays the
1239command icons. The notebook pages are interactively selected by the listbox
1240navigation.
1241
1242The icons, dragged from the notebook, behave as dockable widgets:
1243they can be landed upon a toolbar, or any other dock widget, given
1244it matches the C<fingerprint> ( by default C<dmfp::LaunchPad|dmfp::Toolbar|dmfp::Tools>).
1245C<dmfp::LaunchPad> constant allows the recycling; if a widget is dragged
1246back onto the notebook, it is destroyed.
1247
1248Returns two widgets, the listbox and the notebook.
1249
1250PROFILE recognizes the following keys:
1251
1252=over
1253
1254=item origin X, Y
1255
1256Position where the widgets are to be inserted.
1257Default value is 0,0.
1258
1259=item size X, Y
1260
1261Size of the widget insertion area. By default
1262the widgets occupy all OWNER interior.
1263
1264=item listboxProfile PROFILE
1265
1266Custom parameters, passed to the listbox.
1267
1268=item dockerProfile PROFILE
1269
1270Custom parameteres, passed to the notebook.
1271
1272=back
1273
1274=item create_panel CLSID, %PROFILE
1275
1276Creates a dockable panel of a previously registered CLSID
1277by C<register_panel>. PROFILE recognizes the following keys:
1278
1279=over
1280
1281=item profile HASH
1282
1283Hash of parameters, passed to C<create()> of the panel widget class.
1284Before passing it is merged with the set of parameters, registered
1285by C<register_panel>. The C<profile> hash takes the precedence.
1286
1287=item dockerProfile HASH
1288
1289Constains extra options, passed to C<Prima::DockManager::Panelbar>
1290widget. Before the usage it is merged with the set of parameters,
1291registered by C<register_panel>.
1292
1293NB: The C<dock> key here contains a reference to a desired dock widget.
1294If C<dock> set to C<undef>, the panel is created in the non-docked state.
1295
1296=back
1297
1298Example:
1299
1300	$dock_manager-> create_panel( $CLSID,
1301		dockerProfile => { dock => $main_window }},
1302		profile       => { backColor => cl::Green });
1303
1304
1305=item create_tool OWNER, CLSID, X1, Y1, X2, Y2
1306
1307Inserts a command widget, previously registered with CLSID by C<register_tool>, into
1308OWNER widget with X1 - Y2 coordinates. For automatic maintenance of
1309enable/disable state of command widgets OWNER is expected to be a
1310toolbar. If it is not, the maintenance must be performed separately,
1311for example, by C<CommandChange> event.
1312
1313=item create_toolbar %PROFILE
1314
1315Creates a new toolbar of C<Prima::DockManager::Toolbar> class.
1316The following PROFILE options are recognized:
1317
1318=over
1319
1320=item autoClose BOOLEAN
1321
1322Sets C<autoClose> property of the toolbar.
1323
1324Default value is 1 if C<name> options is set, 0 otherwise.
1325
1326=item dock DOCK
1327
1328Contain a reference to a desired DOCK widget. If C<undef>,
1329the toolbar is created in the non-docked state.
1330
1331=item dockerProfile HASH
1332
1333Parameters passed to C<Prima::DockManager::Toolbar> as
1334creation properties.
1335
1336NB: The C<dock> key here contains a reference to a desired dock widget.
1337If C<dock> set to C<undef>, the panel is created in the non-docked state.
1338
1339=item rect X1, Y1, X2, Y2
1340
1341Selects rectangle of the C<Prima::DockManager::ToolbarDocker> instance
1342in the dock widget ( if docked ) or the screen ( if non-docked ) coordinates.
1343
1344=item toolbarProfile HASH
1345
1346Parameters passed to C<Prima::DockManager::ToolbarDocker> as
1347creation properties.
1348
1349=item vertical BOOLEAN
1350
1351Sets C<vertical> property of the toolbar.
1352
1353=item visible BOOLEAN
1354
1355Selects visibility state of the toolbar.
1356
1357=back
1358
1359=item get_class CLSID
1360
1361Returns class record hash, registered under CLSID, or C<undef>
1362if the class is not registered. The hash format contains
1363the following keys:
1364
1365=over
1366
1367=item class STRING
1368
1369Widget class
1370
1371=item profile HASH
1372
1373Creation parameters passed to C<create> when the widget is created.
1374
1375=item tool BOOLEAN
1376
1377If 1, the class belongs to a control widget. If 0,
1378the class represents a panel client widget.
1379
1380=item lastUsedDock DOCK
1381
1382Saved value of the last used dock widget
1383
1384=item lastUsedRect X1, Y1, X2, Y2
1385
1386Saved coordinates of the widget
1387
1388=back
1389
1390=item panel_by_id CLSID
1391
1392Return reference to a panel widget represented by CLSID scalar,
1393or C<undef> if none found.
1394
1395=item panel_menuitems CALLBACK
1396
1397A helper function; maps all panel names into a structure, ready to
1398feed into C<Prima::AbstractMenu::items> property ( see L<Prima::Menu> ).
1399The action member of the menu item record is set to CALLBACK scalar.
1400
1401=item panel_visible CLSID, BOOLEAN
1402
1403Sets the visibility of a panel, referred by CLSID scalar.
1404If VISIBLE is 0, a panel is destroyed; if 1, new panel instance
1405is created.
1406
1407=item panels
1408
1409Returns all visible panel widgets in an array.
1410
1411=item predefined_panels CLSID, DOCK, [ CLSID, DOCK, ... ]
1412
1413Accepts pairs of scalars, where each first item is a panel CLSID
1414and second is the default dock widget. Checks for panel visibility,
1415and creates the panels that are not visible.
1416
1417The method is useful in program startup, when some panels
1418have to be visible from the beginning.
1419
1420=item predefined_toolbars @PROFILES
1421
1422Accepts array of hashes, where each array item describes a toolbar and
1423a default set of command widgets. Checks for toolbar visibility,
1424and creates the toolbars that are not visible.
1425
1426The method recognizes the following PROFILES options:
1427
1428=over
1429
1430=item dock DOCK
1431
1432The default dock widget.
1433
1434=item list ARRAY
1435
1436Array of CLSIDs corresponding to the command widgets to be inserted
1437into the toolbar.
1438
1439=item name STRING
1440
1441Selects toolbar name.
1442
1443=item origin X, Y
1444
1445Selects the toolbar position relative to the dock ( if C<dock> is specified )
1446or to the screen ( if C<dock> is not specified ).
1447
1448=back
1449
1450The method is useful in program startup, when some panels
1451have to be visible from the beginning.
1452
1453=item register_panel CLSID, PROFILE
1454
1455Registers a panel client class and set of parameters to be associated with
1456CLSID scalar. PROFILE must contain the following keys:
1457
1458=over
1459
1460=item class STRING
1461
1462Client widget class
1463
1464=item text STRING
1465
1466String, displayed in the panel title bar
1467
1468=item dockerProfile HASH
1469
1470Hash of parameters, passed to C<Prima::DockManager::Panelbar>.
1471
1472=item profile
1473
1474Hash of parameters, passed to the client widget.
1475
1476=back
1477
1478=item register_tool CLSID, PROFILE
1479
1480Registers a control widget class and set of parameters to be associated with
1481CLSID scalar. PROFILE must be set the following keys:
1482
1483=over
1484
1485=item class STRING
1486
1487Client widget class
1488
1489=item profile HASH
1490
1491Hash of parameters, passed to the control widget.
1492
1493=back
1494
1495=item toolbar_by_name NAME
1496
1497Returns a pointer to a toolbar of NAME, or C<undef> if none found.
1498
1499=item toolbar_menuitems CALLBACK
1500
1501A helper function; maps all toolbar names into a structure, ready to
1502feed into C<Prima::AbstractMenu::items> property ( see L<Prima::Menu> ).
1503The action member of the menu item record is set to CALLBACK scalar.
1504
1505=item toolbar_visible TOOLBAR, BOOLEAN
1506
1507Sets the visibility of a TOOLBAR.
1508If VISIBLE is 0, the toolbar is hidden; if 1, it is shown.
1509
1510=item toolbars
1511
1512Returns all toolbar widgets in an array.
1513
1514=item windowState INTEGER
1515
1516Mimics interface of C<Prima::Window::windowState>, and maintains
1517visibility of toolbars and panels. If the parameter is C<ws::Minimized>,
1518the toolbars and panels are hidden. On any other parameter these are shown.
1519
1520To be used inside a callback code of a main window, that has the toolbars
1521and panels attached to:
1522
1523	onWindowState => sub { $dock_manager-> windowState( $_[1] ) }
1524
1525=back
1526
1527=head2 Events
1528
1529=over
1530
1531=item Command CLSID
1532
1533A generic event, triggered by a command widget when the user activates
1534it. It can also be called by other means.
1535
1536CLSID is the widget identifier.
1537
1538=item CommandChange
1539
1540Called when C<commands> property changes or C<commands_enable> method is invoked.
1541
1542=item PanelChange
1543
1544Triggered when a panel is created or destroyed by the user.
1545
1546=item ToolbarChange
1547
1548Triggered when a toolbar is created, shown, hidden, or destroyed  by the user.
1549
1550=back
1551
1552=head1 Prima::DockManager::S::SpeedButton
1553
1554The package simplifies creation of C<Prima::SpeedButton> command widgets.
1555
1556=head2 Methods
1557
1558=over
1559
1560=item class IMAGE, CLSID, %PROFILE
1561
1562Builds a hash with parameters, ready to feed C<Prima::DockManager::register_tool>
1563for registering a C<Prima::SpeedButton> class instance with PROFILE parameters.
1564
1565IMAGE is a path to a image file, loaded and stored in the registration hash.
1566IMAGE provides an extended syntax for indicating a frame index, if the image file is multiframed: the frame index is appended to the path name
1567with C<:> character prefix.
1568
1569CLSID scalar is not used; it is returned so the method result can
1570directly be passed into C<register_tool> method.
1571
1572Returns two scalars: CLSID and the registration hash.
1573
1574Example:
1575
1576	$dock_manager-> register_tool(
1577		Prima::DockManager::S::SpeedButton::class( "myicon.gif:2",
1578		q(CLSID::Logo), hint => 'Logo image' ));
1579
1580=back
1581
1582=head1 AUTHOR
1583
1584Dmitry Karasik, E<lt>dmitry@karasik.eu.orgE<gt>.
1585
1586=head1 SEE ALSO
1587
1588L<Prima>, L<Prima::Widget>, L<Prima::Docks>, F<examples/dock.pl>
1589
1590=cut
1591