File Coverage

blib/lib/App/Asciio.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1              
2             package App::Asciio ;
3              
4             $|++ ;
5              
6 1     1   851 use strict;
  1         1  
  1         38  
7 1     1   5 use warnings;
  1         2  
  1         33  
8              
9 1     1   830 use Data::TreeDumper ;
  1         67953  
  1         69  
10 1     1   628 use Clone;
  1         590  
  1         41  
11 1     1   6 use List::Util qw(min max first) ;
  1         1  
  1         88  
12 1     1   492 use List::MoreUtils qw(any minmax first_value) ;
  1         11293  
  1         8  
13              
14 1     1   1254 use App::Asciio::Setup ;
  0            
  0            
15             use App::Asciio::Dialogs ;
16             use App::Asciio::Elements ;
17             use App::Asciio::Menues ;
18             use App::Asciio::Actions ;
19             use App::Asciio::Undo ;
20             use App::Asciio::Io ;
21             use App::Asciio::Ascii ;
22             use App::Asciio::Options ;
23              
24             #-----------------------------------------------------------------------------
25              
26             our $VERSION = '1.51' ;
27              
28             #-----------------------------------------------------------------------------
29              
30             =head1 NAME
31            
32             App::Asciio - Plain ASCII diagram
33            
34             | | | |
35             | | | | | |
36             | | | | | |
37             v | v | v |
38             v v v
39             _____ _____
40             /\ _ \ /\ __ \
41             \ \ \_\ \ ___ ___ _ _\ \ \ \ \
42             -----> \ \ __ \ / __\ / ___\/\ \/\ \ \ \ \ \ ----->
43             \ \ \ \ \/\__, \/\ \___' \ \ \ \ \ \_\ \
44             \ \_\ \_\/\____/\ \____/\ \_\ \_\ \_____\
45             \/_/\/_/\/___/ \/___/ \/_/\/_/\/_____/
46            
47             | | | |
48             | | | | | | |
49             v | | | v | |
50             | v | | |
51             v | | v
52             v v
53             (\_/)
54             (O.o) ASCII world domination is near!
55             (> <)
56            
57             =head1 SYNOPSIS
58            
59             $> perl asciio.pl
60            
61             =head1 DESCRIPTION
62            
63             It has always been painful to do ASCII diagrams by hand. This perl application allows
64             you to draw ASCII diagrams in a modern (but simple) graphical interface.
65            
66             The ASCII graphs can be saved as ASCII or in a format that allows you to modify them later.
67            
68             Special thanks go to the Muppet and the gtk-perl group, Gábor Szabó for his help and advices.
69            
70             Adam Kennedy coined the cool name.
71            
72             =head1 DOCUMENTATION
73            
74             =head2 Asciio user interface
75            
76            
77             .-----------------------------------------------------------------.
78             | Asciio |
79             |-----------------------------------------------------------------|
80             | ............................................................... |
81             | ..............-------------..------------..--------------...... |
82             | .............| stencils > || asciio > || box |..... |
83             | .............| Rulers > || computer > || text |..... |
84             | .............| File > || people > || wirl_arrow |..... |
85             grid---------->.......'-------------'| divers > || axis |..... |
86             | ......................^.....'------------'| boxes > |..... |
87             | ......................|...................| rulers > |..... |
88             | ......................|...................'--------------'..... |
89             | ......................|........................................ |
90             | ......................|........................................ |
91             | ......................|........................................ |
92             | ......................|........................................ |
93             '-----------------------|-----------------------------------------'
94             |
95             |
96             context menu
97            
98            
99             Press 'F1' for help.
100            
101             =head2 context menu
102            
103             The context menu allows to access to B<Asciio> commands.
104            
105             =head2 keyboard shortcuts
106            
107             All the keyboad commands definitions can be found under I<asciio/setup/actions/>. Among the commands
108             implemented are:
109            
110             =over 2
111            
112             =item * select all
113            
114             =item * delete
115            
116             =item * undo
117            
118             =item * group/ungroup
119            
120             =item * open / save
121            
122             =item * local clipboard operations
123            
124             =item * send to front/back
125            
126             =item * insert arrow, boxes, text
127            
128             =item * ...
129            
130             =back
131            
132             The available commands are displayed if you press B<K>.
133            
134             =head2 elements
135            
136             There are a few elements implemented at the moment.
137            
138             =head3 wirl arrow
139            
140             An arrow that tries to do what you want. Try rotating the end clockwise then counter clockwise to see how it acts
141            
142             ^
143             |
144             | --------.
145             | |
146             '------- |
147             |
148             O-------------X / |
149             / |
150             / |
151             / v
152             /
153             /
154             v
155            
156            
157             =head3 multi section wirl arrow
158            
159             A set of whirl arrows connected to each other
160            
161             .----------. .
162             | | \ / \
163             .-------' ^ \ / \
164             | \ \ / \
165             | .-----------> \ ' .
166             | '----. \ /
167             | | \ /
168             '--------' '-------'
169            
170            
171            
172             =head3 angled arrow and axis
173            
174             -------. .-------
175             \ /
176             \ /
177             \ /
178            
179             / \
180             / \
181             / \
182             ------' '-------
183            
184             ^
185             ^ | ^
186             \ | /
187             \ | /
188             \ | /
189             <-------- -------->
190             / |\
191             / | \
192             / | \
193             v | v
194             v
195            
196            
197             =head3 box and text
198            
199             .----------.
200             | title |
201             .----------. |----------| ************
202             | | | body 1 | * *
203             '----------' | body 2 | ************
204             '----------'
205             anything in a box
206             (\_/) |
207             edit_me (O.o) <------------'
208             (> <)
209            
210            
211             You can also use the 'External commands in box' to direct an external command output to a box. Default shortcuts are 'x' and CTL + 'x'.
212            
213             =head3 "if" box and "process" box
214            
215             ____________
216             .--------------. \ \
217             / a == b \ \ \ __________
218             ( && ) ) process ) \ \
219             \ 'string' ne '' / / / ) process )
220             '--------------' /___________/ /_________/
221            
222            
223             =head3 your own stencils
224            
225             Take a look at I<setup/stencils/computer> for a stencil example. Stencils listed in I<setup/setup.ini> will
226             be loaded when B<Asciio> starts.
227            
228             =head3 your own element type
229            
230             For simple elements, put your design in a box. That should cover 90% of anyone's needs. You can look in
231             I<lib/stripes> for element implementation examples.
232            
233             =head2 exporting to ASCII
234            
235             You can export to a file in ASCII format but using the B<.txt> extension.
236            
237             Exporting to the clipboard is done with B<ctl + e>.
238            
239             =head1 EXAMPLES
240            
241            
242             User code ^ ^ OS code
243             \ /
244             \ /
245             \ /
246             User code <----Mode----->OS code
247             / \
248             / \
249             / \
250             User code v v OS code
251            
252            
253             .---. .---. .---. .---. .---. .---.
254             OS API '---' '---' '---' '---' '---' '---'
255             | | | | | |
256             v v | v | v
257             .------------. | .-----------. | .-----.
258             | Filesystem | | | Scheduler | | | MMU |
259             '------------' | '-----------' | '-----'
260             | | | |
261             v | | v
262             .----. | | .---------.
263             | IO |<----' | | Network |
264             '----' | '---------'
265             | | |
266             v v v
267             .---------------------------------------.
268             | HAL |
269             '---------------------------------------'
270            
271            
272            
273            
274             .---------. .---------.
275             | State 1 | | State 2 |
276             '---------' '---------'
277             ^ \ ^ \
278             / \ / \
279             / \ / \
280             / \ / \
281             / \ / \
282             / v v
283             ****** ****** ******
284             * T1 * * T2 * * T3 *
285             ****** ****** ******
286             ^ ^ /
287             \ \ /
288             \ \ /
289             \ \ / stimuli
290             \ \ /
291             \ \ v
292             \ .---------.
293             '--------| State 3 |
294             '---------'
295            
296            
297             .--Base::Class::Derived_A
298             /
299             .----Base::Class::Derived_B
300             Something--------. / \
301             \ / '---Base::Class::Derived::More
302             Something::else \ / \
303             \ \ / '-Base::Class::Derived::Deeper
304             \ \ /
305             \ \ .-----------Base::Class::Derived_C
306             \ \ /
307             '-------Base::Class
308             / \ \ \
309             ' \ \ \
310             | \ \ '---The::Latest
311             /| \ \ \
312             With::Some::fantasy' ' \ \ '----The::Latest::Greatest
313             /| \ \
314             More::Stuff' ' \ '-I::Am::Running::Out::Of::Ideas
315             /| \
316             More::Stuff' ' \
317             / '---Last::One
318             More::Stuff'
319            
320            
321             ____[]
322             | ___ |
323             || || device
324             ||___|| loads
325             | ooo |------------------------------------------------------------.
326             | ooo | | | |
327             | ooo | | | |
328             '_____' | | |
329             | | |
330             v v v
331             .-------------------. .---------------------------. .-------------------.
332             | Loadable module C | | Loadable module A | | Loadable module B |
333             '-------------------' |---------------------------| | (instrumented) |
334             | | .-----. | '-------------------'
335             '--------------------->| A.o | | |
336             calls | '-----' | |
337             | .------------------. | |
338             | | A.instrumented.o |<-----------------'
339             | '------------------' | calls
340             '---------------------------'
341            
342            
343             =cut
344              
345             sub new
346             {
347             my ($class) = @_ ;
348              
349             my $self =
350             bless 
351             {
352             ELEMENT_TYPES => [],
353             ELEMENTS => [],
354             CONNECTIONS => [],
355             CLIPBOARD => {},
356             FONT_FAMILY => 'Monospace',
357             FONT_SIZE => '10',
358             TAB_AS_SPACES => ' ',
359             OPAQUE_ELEMENTS => 1,
360             DISPLAY_GRID => 1,
361            
362             PREVIOUS_X => -1, PREVIOUS_Y => -1,
363             MOUSE_X => 0, MOUSE_Y => 0,
364             DRAGGING => '',
365             SELECTION_RECTANGLE =>{START_X => 0, START_Y => 0},
366            
367             KEYS => {K =>{}, C =>{},},
368             ACTIONS => {},
369             VALID_SELECT_ACTION => { map {$_, 1} qw(resize move)},
370            
371             COPY_OFFSET_X => 3,
372             COPY_OFFSET_Y => 3,
373             COLORS =>
374             {
375             background => [255, 255, 255],
376             grid => [229, 235, 255],
377             ruler_line => [85, 155, 225],
378             selected_element_background => [180, 244, 255],
379             element_background => [251, 251, 254],
380             element_foreground => [0, 0, 0] ,
381             selection_rectangle => [255, 0, 255],
382             test => [0, 255, 255],
383            
384             group_colors =>
385             [
386             [[250, 221, 190], [250, 245, 239]],
387             [[182, 250, 182], [241, 250, 241]],
388             [[185, 219, 250], [244, 247, 250]],
389             [[137, 250, 250], [235, 250, 250]],
390             [[198, 229, 198], [239, 243, 239]],
391             ],
392            
393             connection => 'Chocolate',
394             connection_point => [230, 198, 133],
395             connector_point => 'DodgerBlue',
396             new_connection => 'red' ,
397             extra_point => [230, 198, 133],
398             },
399            
400             NEXT_GROUP_COLOR => 0,
401            
402             WORK_DIRECTORY => '.asciio_work_dir',
403             CREATE_BACKUP => 1,
404             MODIFIED => 0,
405            
406             DO_STACK_POINTER => 0,
407             DO_STACK => [] ,
408             }, $class ;
409              
410             return($self) ;
411             }
412              
413             #-----------------------------------------------------------------------------
414              
415             sub event_options_changed
416             {
417             my ($self) = @_;
418              
419             my $number_of_group_colors = scalar(@{$self->{COLORS}{group_colors}}) ;
420             $self->{GROUP_COLORS} = [0 .. $number_of_group_colors - 1] ,
421              
422             $self->{CURRENT_ACTIONS} = $self->{ACTIONS} ;
423              
424             $self->set_font($self->{FONT_FAMILY}, $self->{FONT_SIZE});
425             }
426              
427             #-----------------------------------------------------------------------------
428              
429             sub set_title
430             {
431             my ($self, $title) = @_;
432              
433             if(defined $title)
434             {
435             $self->{TITLE} = $title ;
436             }
437             }
438              
439             sub get_title
440             {
441             my ($self) = @_;
442             $self->{TITLE} ;
443             }
444              
445             #-----------------------------------------------------------------------------
446              
447             sub set_font
448             {
449             my ($self, $font_family, $font_size) = @_;
450              
451             $self->{FONT_FAMILY} = $font_family || 'Monospace';
452             $self->{FONT_SIZE} = $font_size || 10 ;
453             }
454              
455             sub get_font
456             {
457             my ($self) = @_;
458              
459             return($self->{FONT_FAMILY},  $self->{FONT_SIZE}) ;
460             }
461              
462             #-----------------------------------------------------------------------------
463              
464             sub update_display
465             {
466             my ($self) = @_;
467              
468             $self->call_hook('CANONIZE_CONNECTIONS', $self->{CONNECTIONS}, $self->get_character_size()) ;
469             }
470              
471             sub get_grid_usage
472             {
473             my ($self) = @_;
474              
475             my %cost_map ;
476              
477             # todo: keep previous cost map and update only changed elements
478              
479             for my $element (@{$self->{ELEMENTS}})
480             {
481             for my $mask_and_element_strip ($element->get_mask_and_element_stripes())
482             {
483             my $x_offset = $element->{X} + $mask_and_element_strip->{X_OFFSET} ;
484             my $y_offset = $element->{Y} + $mask_and_element_strip->{Y_OFFSET} ;
485            
486             my $string_offset = 0 ;
487            
488             for my $string ( split /\n/, $mask_and_element_strip->{TEXT})
489             {
490             for (0 .. length($string) - 1 )
491             {
492             my $x_offset_line = $x_offset + $_ ;
493             my $y_offset_line = ($y_offset + $string_offset) ;
494            
495             $cost_map{"$x_offset_line.$y_offset_line"} = 0 ;
496             }
497            
498             $string_offset++ ;
499             }
500             }
501             }
502              
503             return \%cost_map ;
504             }
505              
506             #-----------------------------------------------------------------------------
507              
508             sub call_hook
509             {
510             my ($self, $hook_name, @arguments) = @_;
511              
512             $self->{HOOKS}{$hook_name}->(@arguments)  if (exists $self->{HOOKS}{$hook_name}) ;
513             }
514              
515              
516             #-----------------------------------------------------------------------------
517              
518             sub button_release_event
519             {
520             my ($self, $event) = @_ ;
521              
522             my $modifiers = $event->{MODIFIERS} ;
523              
524             if($self->exists_action("${modifiers}-button_release"))
525             {
526             $self->run_actions(["${modifiers}-button_release", $event]) ;
527             return 1 ;
528             }
529              
530             if(defined $self->{MODIFIED_INDEX} && defined $self->{MODIFIED} && $self->{MODIFIED_INDEX} == $self->{MODIFIED})
531             {
532             $self->pop_undo_buffer(1) ; # no changes
533             }
534              
535             $self->update_display();
536             }
537              
538             #-----------------------------------------------------------------------------
539              
540             sub button_press_event
541             {
542             #~ print "button_press_event\n" ;
543             my ($self, $event, $wrapper_event) = @_ ;
544              
545             $self->{DRAGGING} = '' ;
546             delete $self->{RESIZE_CONNECTOR_NAME} ;
547              
548             $self->create_undo_snapshot() ;
549             $self->{MODIFIED_INDEX} = $self->{MODIFIED} ;
550              
551             my $modifiers = $event->{MODIFIERS} ;
552             my $button = $event->{BUTTON} ;
553              
554             if($self->exists_action("${modifiers}-button_press-$button"))
555             {
556             $self->run_actions(["${modifiers}-button_press-$button", $event]) ;
557             return 1 ;
558             }
559              
560             my($x, $y) = @{$event->{COORDINATES}} ;
561              
562             if($event->{TYPE} eq '2button-press')
563             {
564             my @element_over = grep { $self->is_over_element($_, $x, $y) } reverse @{$self->{ELEMENTS}} ;
565            
566             if(@element_over)
567             {
568             my $selected_element = $element_over[0] ;
569             $self->edit_element($selected_element) ;
570             $self->update_display();
571             }
572            
573             return 1 ;
574             }
575              
576             if($event->{BUTTON} == 1)
577             {
578             my $modifiers = $event->{MODIFIERS} ;
579            
580             my ($first_element) = first_value {$self->is_over_element($_, $x, $y)} reverse @{$self->{ELEMENTS}} ;
581            
582             if ($modifiers eq 'C00')
583             {
584             if(defined $first_element)
585             {
586             $self->run_actions_by_name('Copy to clipboard', ['Insert from clipboard', 0, 0]) ;
587             }
588             }
589             else
590             {
591             if(defined $first_element)
592             {
593             if ($modifiers eq '00S')
594             {
595             $self->select_elements_flip($first_element) ;
596             }
597             else
598             {
599             unless($self->is_element_selected($first_element))
600             {
601             # make the element under cursor the only selected element
602             $self->select_elements(0, @{$self->{ELEMENTS}}) ;
603             $self->select_elements(1, $first_element) ;
604             }
605             }
606             }
607             else
608             {
609             # deselect all
610             $self->deselect_all_elements()  if ($modifiers eq '000') ;
611             }
612             }
613            
614             $self->{SELECTION_RECTANGLE} = {START_X => $x , START_Y => $y} ;
615            
616             $self->update_display();
617             }
618            
619             if($event->{BUTTON} == 2)
620             {
621             $self->{SELECTION_RECTANGLE} = {START_X => $x , START_Y => $y} ;
622            
623             $self->update_display();
624             }
625               
626             if($event->{BUTTON} == 3)
627             {
628             $self->display_popup_menu($wrapper_event) ; # display_popup_menu is handled by derived Asciio
629             }
630               
631             return 1;
632             }
633              
634             #-----------------------------------------------------------------------------
635              
636             sub motion_notify_event
637             {
638             my ($self, $event) = @_ ;
639              
640             my($x, $y) = @{$event->{COORDINATES}} ;
641             my $modifiers = $event->{MODIFIERS} ;
642             my $button = $event->{BUTTON} ;
643              
644             if($self->exists_action("${modifiers}motion_notify"))
645             {
646             $self->run_actions(["${modifiers}-motion_notify", $event]) ;
647             return 1 ;
648             }
649              
650             if($self->{PREVIOUS_X} != $x || $self->{PREVIOUS_Y} != $y)
651             {
652             ($self->{MOUSE_X}, $self->{MOUSE_Y}) = ($x, $y) ;
653             $self->update_display() ;
654             }
655            
656             if ($event->{STATE} >= "button1-mask")
657             {
658             if($self->{DRAGGING} ne '')
659             {
660             if      ($self->{DRAGGING} eq 'move') { $self->move_elements_event($x, $y) ; }
661             elsif ($self->{DRAGGING}eq 'resize') { $self->resize_element_event($x, $y) ; }
662             elsif ($self->{DRAGGING}eq 'select') { $self->select_element_event($x, $y) ; }
663             }
664             else
665             {
666             my @selected_elements = $self->get_selected_elements(1) ;
667             my ($first_element) = first_value {$self->is_over_element($_, $x, $y)} reverse @selected_elements ;
668            
669             if(@selected_elements > 1)
670             {
671             if(defined $first_element)
672             {
673             $self->{DRAGGING} = 'move' ;
674             }
675             else
676             {
677             $self->{DRAGGING} = 'select' ;
678             }
679             }
680             else
681             {
682             if(defined $first_element)
683             {
684             $self->{DRAGGING} = $first_element->get_selection_action
685             (
686             $x - $first_element->{X},
687             $y - $first_element->{Y},
688             );
689            
690             $self->{DRAGGING} ='' unless exists $self->{VALID_SELECT_ACTION}{$self->{DRAGGING}} ;
691             }
692             else
693             {
694             $self->{DRAGGING} = 'select' ;
695             }
696             }
697            
698             ($self->{PREVIOUS_X}, $self->{PREVIOUS_Y}) = ($x, $y) ;
699             }
700             }
701              
702             if ($event->{STATE} >= "button2-mask")
703             {
704             $self->select_element_event($x, $y, $self->{MIDDLE_BUTTON_SELECTION_FILTER} || sub{1}) ;
705             }
706            
707             return 1;
708             }
709              
710             #-----------------------------------------------------------------------------
711              
712             sub select_element_event
713             {
714             my ($self, $x, $y, $filter) = @_ ;
715              
716             my ($x_offset, $y_offset) = ($x - $self->{PREVIOUS_X}, $y - $self->{PREVIOUS_Y}) ;
717            
718             if($x_offset != 0 || $y_offset != 0)
719             {
720             $self->{SELECTION_RECTANGLE}{END_X} = $x ;
721             $self->{SELECTION_RECTANGLE}{END_Y} = $y ;
722            
723             $filter = sub {1} unless defined $filter ;
724            
725             $self->select_elements
726             (
727             1,
728             grep
729             { $filter->($_) }
730             grep # elements within selection rectangle
731             {
732             $self->element_completely_within_rectangle
733             (
734             $_,
735             $self->{SELECTION_RECTANGLE},
736             )
737             } @{$self->{ELEMENTS}}
738             )  ;
739            
740             $self->update_display();
741            
742             ($self->{PREVIOUS_X}, $self->{PREVIOUS_Y}) = ($x, $y) ;
743             }
744             }
745              
746             #-----------------------------------------------------------------------------
747              
748             sub move_elements_event
749             {
750             my ($self, $x, $y) = @_;
751              
752             my ($x_offset, $y_offset) = ($x - $self->{PREVIOUS_X}, $y - $self->{PREVIOUS_Y}) ;
753              
754             if($x_offset != 0 || $y_offset != 0)
755             {
756             my @selected_elements = $self->get_selected_elements(1) ;
757            
758             $self->move_elements($x_offset, $y_offset, @selected_elements) ;
759             $self->update_display();
760            
761             ($self->{PREVIOUS_X}, $self->{PREVIOUS_Y}) = ($x, $y) ;
762             }
763             }
764              
765             #-----------------------------------------------------------------------------
766              
767             sub resize_element_event
768             {
769             my ($self, $x, $y) = @_ ;
770              
771             my ($x_offset, $y_offset) = ($x - $self->{PREVIOUS_X}, $y - $self->{PREVIOUS_Y}) ;
772              
773             if($x_offset != 0 || $y_offset != 0)
774             {
775             my ($selected_element) = $self->get_selected_elements(1) ;
776            
777             $self->{RESIZE_CONNECTOR_NAME} =
778             $self->resize_element
779             (
780             $self->{PREVIOUS_X} - $selected_element->{X}, $self->{PREVIOUS_Y} - $selected_element->{Y} ,
781             $x - $selected_element->{X}, $y - $selected_element->{Y} ,
782             $selected_element,
783             $self->{RESIZE_CONNECTOR_NAME},
784             ) ;
785            
786             $self->update_display();
787              
788             ($self->{PREVIOUS_X}, $self->{PREVIOUS_Y}) = ($x, $y) ;
789             }
790             }
791            
792             #-----------------------------------------------------------------------------
793              
794             sub key_press_event
795             {
796             my ($self, $event)= @_;
797              
798             my $modifiers = $event->{MODIFIERS} ;
799             my $key = $self->{KEYS}{C}{$event->{KEY_VALUE}} ;
800              
801             $self->run_actions("$modifiers-$key") ;
802              
803             return 0 ;
804             }
805              
806             #-----------------------------------------------------------------------------
807              
808             sub get_character_size
809             {
810             my ($self) = @_ ;
811            
812             if(exists $self->{USER_CHARACTER_WIDTH})
813             {
814             return ($self->{USER_CHARACTER_WIDTH}, $self->{USER_CHARACTER_HEIGHT}) ;
815             }
816             else
817             {
818             return (8, 16) ;
819             }
820             }
821              
822             #-----------------------------------------------------------------------------
823              
824             sub set_character_size
825             {
826             my ($self, $width, $height) = @_ ;
827            
828             ($self->{USER_CHARACTER_WIDTH}, $self->{USER_CHARACTER_HEIGHT}) = ($width, $height) ;
829             }
830              
831             #-----------------------------------------------------------------------------
832              
833             sub get_color
834             {
835             my ($self, $name) = @_;
836              
837             unless (exists $self->{ALLOCATED_COLORS}{$name})
838             {
839             my $color = [255, 0, 0];
840            
841             $self->{ALLOCATED_COLORS}{$name} = $color ;
842             }
843            
844             return($self->{ALLOCATED_COLORS}{$name}) ;
845             }
846              
847             #-----------------------------------------------------------------------------
848              
849             sub flush_color_cache
850             {
851             my ($self) = @_ ;
852             delete $self->{ALLOCATED_COLORS} ;
853             }
854              
855             #-----------------------------------------------------------------------------
856              
857              
858             =head1 DEPENDENCIES
859            
860             gnome libraries, gtk, gtk-perl for the gtk version
861            
862             =head1 BUGS AND LIMITATIONS
863            
864             Undoubtedly many as I wrote this as a fun little project where I used no design nor 'methodic' whatsoever.
865            
866             =head1 AUTHOR
867            
868             Khemir Nadim ibn Hamouda
869             CPAN ID: NKH
870             mailto:nadim@khemir.net
871            
872             =head1 LICENSE AND COPYRIGHT
873            
874             This program is free software; you can redistribute
875             it and/or modify it under the same terms as Perl itself.
876            
877             =head1 SUPPORTED OSes
878            
879             =head2 Gentoo
880            
881             I run gentoo, packages to install gtk-perl exist. Install Asciio with cpan.
882            
883             =head2 FreeBSD
884            
885             FreeBSD users can now install asciio either by package:
886            
887             $ pkg_add -r asciio
888            
889             or from source (out of the ports system) by:
890            
891             $ cd /usr/ports/graphics/asciio
892             $ make install clean
893            
894             Thanks to Emanuel Haupt.
895            
896             =head2 Ubuntu and Debian
897            
898             Ports are on the way.
899            
900             =head2 Windows
901            
902             B<Asciio> is part of the B<camelbox> distribution and can be found here: L<http://code.google.com/p/camelbox/>. Install, run AsciiO from the 'bin' directory.
903            
904             .-------------------------------.
905             / /|
906             / camelbox for win32 / |
907             / / |
908             / / |
909             .-------------------------------. |
910             | ______\\_, | |
911             | (_. _ o_ _/ | |
912             | '-' \_. / | |
913             | / / | |
914             | / / .--. .--. | |
915             | ( ( / '' \/ '' \ " | |
916             | \ \_.' \ ) | |
917             | || _ './ | |
918             | |\ \ ___.'\ / | |
919             | '-./ .' \ |/ | |
920             | \| / )|\ | |
921             | |/ // \\ | .
922             | |\ __// \\__ | /
923             | //\\ /__/ mrf\__| | /
924             | .--_/ \_--. | /
925             | /__/ \__\ |/
926             '-------------------------------'
927            
928             B<camelbox> is a great distribution for windows. I hope it will merge with X-berry series of Perl distributions.
929            
930             =head1 Mac OsX
931            
932             This works too (and I have screenshots to prove it :). I don't own a mac and the mac user hasn't send me how to do it yet.
933            
934             =head1 other unices
935            
936             YMMV, install gtk-perl and AsciiO from cpan.
937            
938             =head1 SEE ALSO
939            
940             http://www.jave.de
941             http://search.cpan.org/~osfameron/Text-JavE-0.0.2/JavE.pm
942             http://ditaa.sourceforge.net/
943             http://www.codeproject.com/KB/macros/codeplotter.aspx
944             http://search.cpan.org/~jpierce/Text-FIGlet-1.06/FIGlet.pm
945             http://www.fossildraw.com/?gclid=CLanxZXxoJECFRYYEAodnBS8Dg (doesn't always respond)
946            
947             http://www.ascii-art.de (used some entries as base for the network stencil)
948             http://c2.com/cgi/wiki?UmlAsciiArt
949             http://www.textfiles.com/art/
950             http://www2.b3ta.com/_bunny/texbunny.gif
951            
952            
953             *\o_ _o/*
954             / * * \
955             <\ *\o/* />
956             )
957             o/* / > *\o
958             <\ />
959             __o */\ /\* o__
960             * /> <\ *
961             /\* __o_ _o__ */\
962             * / * * \ *
963             <\ />
964             *\o/*
965             ejm97 __)__
966            
967             =cut
968              
969             #------------------------------------------------------------------------------------------------------
970              
971             "ASCII world domination!"  ;
972              
973