~ubuntu-branches/ubuntu/maverick/padre/maverick

1 by Damyan Ivanov
Import upstream version 0.27.ds1
1
package Padre::Wx::Outline;
2
3
use 5.008;
4
use strict;
5
use warnings;
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
6
use Params::Util   ();
1 by Damyan Ivanov
Import upstream version 0.27.ds1
7
use Padre::Wx      ();
8
use Padre::Current ();
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
9
use Padre::Logger;
1 by Damyan Ivanov
Import upstream version 0.27.ds1
10
1.2.3 by Damyan Ivanov
Import upstream version 0.63.ds1
11
our $VERSION = '0.63';
1.1.4 by Damyan Ivanov
Import upstream version 0.36
12
our @ISA     = 'Wx::TreeCtrl';
1 by Damyan Ivanov
Import upstream version 0.27.ds1
13
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
14
use Class::XSAccessor {
15
	accessors => {
16
		force_next => 'force_next',
17
	}
1.1.3 by Damyan Ivanov
Import upstream version 0.35
18
};
19
1 by Damyan Ivanov
Import upstream version 0.27.ds1
20
sub new {
21
	my $class = shift;
22
	my $main  = shift;
23
	my $self  = $class->SUPER::new(
24
		$main->right,
25
		-1,
26
		Wx::wxDefaultPosition,
27
		Wx::wxDefaultSize,
1.1.2 by Damyan Ivanov
Import upstream version 0.34
28
		Wx::wxTR_HIDE_ROOT | Wx::wxTR_SINGLE | Wx::wxTR_HAS_BUTTONS | Wx::wxTR_LINES_AT_ROOT
1 by Damyan Ivanov
Import upstream version 0.27.ds1
29
	);
30
	$self->SetIndent(10);
31
	$self->{force_next} = 0;
32
1.1.2 by Damyan Ivanov
Import upstream version 0.34
33
	Wx::Event::EVT_COMMAND_SET_FOCUS(
34
		$self, $self,
35
		sub {
36
			$self->on_tree_item_set_focus( $_[1] );
37
		},
38
	);
39
1.1.5 by Damyan Ivanov
Import upstream version 0.42
40
	# Double-click a function name
41
	Wx::Event::EVT_TREE_ITEM_ACTIVATED(
1.1.2 by Damyan Ivanov
Import upstream version 0.34
42
		$self, $self,
43
		sub {
1.1.5 by Damyan Ivanov
Import upstream version 0.42
44
			$self->on_tree_item_activated( $_[1] );
45
		}
1 by Damyan Ivanov
Import upstream version 0.27.ds1
46
	);
47
48
	$self->Hide;
49
1.2.2 by Damyan Ivanov
Import upstream version 0.61.ds1
50
	$self->{cache} = {};
51
1 by Damyan Ivanov
Import upstream version 0.27.ds1
52
	return $self;
53
}
54
55
sub right {
56
	$_[0]->GetParent;
57
}
58
59
sub main {
60
	$_[0]->GetGrandParent;
61
}
62
63
sub gettext_label {
64
	Wx::gettext('Outline');
65
}
66
67
sub clear {
1.2.2 by Damyan Ivanov
Import upstream version 0.61.ds1
68
	my ($self) = @_;
69
	$self->DeleteAllItems;
70
	return;
71
}
72
73
################################################################
74
# Cache routines
75
76
sub store_in_cache {
77
	my ( $self, $cache_key, $content ) = @_;
78
79
	if ( defined $cache_key ) {
80
		$self->{cache}->{$cache_key} = $content;
81
	}
82
	return;
83
}
84
85
sub get_from_cache {
86
	my ( $self, $cache_key ) = @_;
87
88
	if ( defined $cache_key and exists $self->{cache}->{$cache_key} ) {
89
		return $self->{cache}->{$cache_key};
90
	}
91
	return;
92
}
93
94
#####################################################################
95
# GUI routines
96
97
sub update_data {
98
	my ( $self, $outline_data, $filename, $right_click_handler ) = @_;
99
100
	$self->Freeze;
101
102
	# Clear out the existing stuff
103
	# TO DO extract data for keeping (sub)trees collapsed/expanded (see below)
104
	#if ( $self->GetCount > 0 ) {
105
	#	my $r = $self->GetRootItem;
106
	#	warn ref $r;
107
	#	use Data::Dumper;
108
	#	my ( $fc, $cookie ) = $self->GetFirstChild($r);
109
	#	warn ref $fc;
110
	#	warn $self->GetItemText($fc) . ': ' . Dumper( $self->GetPlData($fc) );
111
	#}
112
	$self->clear;
113
114
	require Padre::Wx;
115
116
	# If there is no structure, clear the outline pane and return.
117
	unless ($outline_data) {
118
		return;
119
	}
120
121
	# Again, slightly differently
122
	unless (@$outline_data) {
123
		return 1;
124
	}
125
126
	# Add the hidden unused root
127
	my $root = $self->AddRoot(
128
		Wx::gettext('Outline'),
129
		-1,
130
		-1,
131
		Wx::TreeItemData->new('')
132
	);
133
134
	# Update the outline pane
135
	_update_treectrl( $self, $outline_data, $root );
136
137
	# Set MIME type specific event handler
138
	if ( defined $right_click_handler ) {
139
		Wx::Event::EVT_TREE_ITEM_RIGHT_CLICK(
140
			$self,
141
			$self,
142
			$right_click_handler,
143
		);
144
	}
145
146
	# TO DO Expanding all is not acceptable: We need to keep the state
147
	# (i.e., keep the pragmata subtree collapsed if it was collapsed
148
	# by the user)
149
	#$self->ExpandAll;
150
	$self->GetBestSize;
151
	$self->Thaw;
152
153
	$self->store_in_cache( $filename, [ $outline_data, $right_click_handler ] );
154
155
	return 1;
156
}
157
158
sub _update_treectrl {
159
	my ( $outlinebar, $outline, $root ) = @_;
160
161
	foreach my $pkg ( @{$outline} ) {
162
		my $branch = $outlinebar->AppendItem(
163
			$root,
164
			$pkg->{name},
165
			-1, -1,
166
			Wx::TreeItemData->new(
167
				{   line => $pkg->{line},
168
					name => $pkg->{name},
169
					type => 'package',
170
				}
171
			)
172
		);
173
		foreach my $type (qw(pragmata modules attributes methods events)) {
174
			_add_subtree( $outlinebar, $pkg, $type, $branch );
175
		}
176
		$outlinebar->Expand($branch);
177
	}
178
179
	return;
180
}
181
182
sub _add_subtree {
183
	my ( $self, $pkg, $type, $root ) = @_;
184
185
	my %type_caption = (
186
		pragmata => Wx::gettext('Pragmata'),
187
		modules  => Wx::gettext('Modules'),
188
		methods  => Wx::gettext('Methods'),
189
	);
190
191
	my $type_elem = undef;
192
	if ( defined( $pkg->{$type} ) && scalar( @{ $pkg->{$type} } ) > 0 ) {
193
		my $type_caption = ucfirst($type);
194
		if ( exists $type_caption{$type} ) {
195
			$type_caption = $type_caption{$type};
196
		} else {
197
			warn "Type not translated: $type_caption\n";
198
		}
199
200
		$type_elem = $self->AppendItem(
201
			$root,
202
			$type_caption,
203
			-1,
204
			-1,
205
			Wx::TreeItemData->new()
206
		);
207
208
		my @sorted_entries = ();
209
		if ( $type eq 'methods' ) {
210
			my $config = $self->main->{ide}->config;
211
			if ( $config->main_functions_order eq 'original' ) {
212
213
				# That should be the one we got
214
				@sorted_entries = @{ $pkg->{$type} };
215
			} elsif ( $config->main_functions_order eq 'alphabetical_private_last' ) {
216
217
				# ~ comes after \w
218
				my @pre = map { $_->{name} =~ s/^_/~/; $_ } @{ $pkg->{$type} };
219
				@pre = sort { $a->{name} cmp $b->{name} } @pre;
220
				@sorted_entries = map { $_->{name} =~ s/^~/_/; $_ } @pre;
221
			} else {
222
223
				# Alphabetical (aka 'abc')
224
				@sorted_entries = sort { $a->{name} cmp $b->{name} } @{ $pkg->{$type} };
225
			}
226
		} else {
227
			@sorted_entries = sort { $a->{name} cmp $b->{name} } @{ $pkg->{$type} };
228
		}
229
230
		foreach my $item (@sorted_entries) {
231
			$self->AppendItem(
232
				$type_elem,
233
				$item->{name},
234
				-1, -1,
235
				Wx::TreeItemData->new(
236
					{   line => $item->{line},
237
						name => $item->{name},
238
						type => $type,
239
					}
240
				)
241
			);
242
		}
243
	}
244
	if ( defined $type_elem ) {
245
		if ( $type eq 'methods' ) {
246
			$self->Expand($type_elem);
247
		} else {
248
			$self->Collapse($type_elem);
249
		}
250
	}
251
1 by Damyan Ivanov
Import upstream version 0.27.ds1
252
	return;
253
}
254
255
#####################################################################
256
# Timer Control
257
258
sub start {
1.1.5 by Damyan Ivanov
Import upstream version 0.42
259
	my $self = shift; @_ = (); # Feeble attempt to kill Scalars Leaked ($self is leaking)
1 by Damyan Ivanov
Import upstream version 0.27.ds1
260
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
261
	# TO DO: GUI on-start initialisation here
1 by Damyan Ivanov
Import upstream version 0.27.ds1
262
263
	# Set up or reinitialise the timer
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
264
	if ( Params::Util::_INSTANCE( $self->{timer}, 'Wx::Timer' ) ) {
1 by Damyan Ivanov
Import upstream version 0.27.ds1
265
		$self->{timer}->Stop if $self->{timer}->IsRunning;
266
	} else {
267
		$self->{timer} = Wx::Timer->new(
268
			$self,
269
			Padre::Wx::ID_TIMER_OUTLINE
270
		);
1.1.1 by Damyan Ivanov
Import upstream version 0.33
271
		Wx::Event::EVT_TIMER(
272
			$self,
1 by Damyan Ivanov
Import upstream version 0.27.ds1
273
			Padre::Wx::ID_TIMER_OUTLINE,
274
			sub {
1.1.1 by Damyan Ivanov
Import upstream version 0.33
275
				$self->on_timer( $_[1], $_[2] );
1 by Damyan Ivanov
Import upstream version 0.27.ds1
276
			},
277
		);
278
	}
1.1.1 by Damyan Ivanov
Import upstream version 0.33
279
	$self->{timer}->Start(1000);
1 by Damyan Ivanov
Import upstream version 0.27.ds1
280
	$self->on_timer( undef, 1 );
281
1.1.3 by Damyan Ivanov
Import upstream version 0.35
282
	return ();
1 by Damyan Ivanov
Import upstream version 0.27.ds1
283
}
284
285
sub stop {
286
	my $self = shift;
287
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
288
	TRACE("stopping Outline") if DEBUG;
1.1.2 by Damyan Ivanov
Import upstream version 0.34
289
1 by Damyan Ivanov
Import upstream version 0.27.ds1
290
	# Stop the timer
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
291
	if ( Params::Util::_INSTANCE( $self->{timer}, 'Wx::Timer' ) ) {
1 by Damyan Ivanov
Import upstream version 0.27.ds1
292
		$self->{timer}->Stop if $self->{timer}->IsRunning;
293
	}
294
295
	$self->clear;
296
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
297
	# TO DO: GUI on-stop cleanup here
1 by Damyan Ivanov
Import upstream version 0.27.ds1
298
1.1.3 by Damyan Ivanov
Import upstream version 0.35
299
	return ();
1 by Damyan Ivanov
Import upstream version 0.27.ds1
300
}
301
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
302
sub refresh {
303
	my $self = shift;
1.2.2 by Damyan Ivanov
Import upstream version 0.61.ds1
304
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
305
	$self->clear;
1.2.2 by Damyan Ivanov
Import upstream version 0.61.ds1
306
307
	my $filename         = Padre::Current->filename;
308
	my $outline_data_ref = $self->get_from_cache($filename);
309
	if ( defined $outline_data_ref ) {
310
		my ( $outline_data, $right_click_handler ) = @$outline_data_ref;
311
		$self->update_data( $outline_data, $filename, $right_click_handler );
312
	}
313
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
314
	$self->force_next(1);
315
}
316
1 by Damyan Ivanov
Import upstream version 0.27.ds1
317
sub running {
1.1.1 by Damyan Ivanov
Import upstream version 0.33
318
	!!( $_[0]->{timer} and $_[0]->{timer}->IsRunning );
1 by Damyan Ivanov
Import upstream version 0.27.ds1
319
}
320
321
#####################################################################
322
# Event Handlers
323
1.1.2 by Damyan Ivanov
Import upstream version 0.34
324
sub on_tree_item_set_focus {
1.1.1 by Damyan Ivanov
Import upstream version 0.33
325
	my ( $self, $event ) = @_;
1.1.6 by Damyan Ivanov
Import upstream version 0.48.ds2
326
	my $main      = Padre::Current->main($self);
327
	my $page      = $main->current->editor;
328
	my $selection = $self->GetSelection();
329
	if ( $selection and $selection->IsOk ) {
330
		my $item = $self->GetPlData($selection);
331
		if ( defined $item ) {
332
			$self->select_line_in_editor( $item->{line} );
333
		}
1.1.2 by Damyan Ivanov
Import upstream version 0.34
334
	}
335
	return;
336
}
1 by Damyan Ivanov
Import upstream version 0.27.ds1
337
1.1.5 by Damyan Ivanov
Import upstream version 0.42
338
sub on_tree_item_activated {
339
	on_tree_item_set_focus(@_);
1.1.2 by Damyan Ivanov
Import upstream version 0.34
340
	return;
341
}
342
343
sub select_line_in_editor {
344
	my ( $self, $line_number ) = @_;
1.1.5 by Damyan Ivanov
Import upstream version 0.42
345
	my $main = Padre::Current->main($self);
346
	my $page = $main->current->editor;
1.1.2 by Damyan Ivanov
Import upstream version 0.34
347
	if (   defined $line_number
348
		&& ( $line_number =~ /^\d+$/o )
349
		&& ( defined $page )
350
		&& ( $line_number <= $page->GetLineCount ) )
351
	{
352
		$line_number--;
353
		$page->EnsureVisible($line_number);
354
		$page->goto_pos_centerize( $page->GetLineIndentPosition($line_number) );
355
		$page->SetFocus;
356
	}
1 by Damyan Ivanov
Import upstream version 0.27.ds1
357
	return;
358
}
359
360
sub on_timer {
361
	my ( $self, $event, $force ) = @_;
362
1.1.5 by Damyan Ivanov
Import upstream version 0.42
363
	### NOTE:
364
	# floating windows, when undocked (err... "floating"), will
365
	# return Wx::AuiFloatingFrame as their parent. So floating
366
	# windows should always get their "main" from Padre::Current->main
367
	# and -not- from $self->main.
368
	my $main = Padre::Current->main($self);
369
370
	my $document = $main->current->document or return;
1 by Damyan Ivanov
Import upstream version 0.27.ds1
371
372
	unless ( $document->can('get_outline') ) {
373
		$self->clear;
374
		return;
375
	}
376
377
	if ( $self->force_next ) {
378
		$force = 1;
379
		$self->force_next(0);
380
	}
381
1.1.1 by Damyan Ivanov
Import upstream version 0.33
382
	$document->get_outline( force => $force );
1 by Damyan Ivanov
Import upstream version 0.27.ds1
383
384
	if ( defined($event) ) {
385
		$event->Skip(0);
386
	}
387
388
	return;
389
}
390
391
1;
392
1.1.8 by Damyan Ivanov
Import upstream version 0.59.ds1
393
# Copyright 2008-2010 The Padre development team as listed in Padre.pm.
1 by Damyan Ivanov
Import upstream version 0.27.ds1
394
# LICENSE
395
# This program is free software; you can redistribute it and/or
396
# modify it under the same terms as Perl 5 itself.