GROMMIER Sébastien > Text-Editor-Easy > Text::Editor::Easy::Events

Download:
Text-Editor-Easy-0.49.tar.gz

Dependencies

Annotate this POD

CPAN RT

New  1
Open  0
View/Report Bugs
Module Version: 0.49   Source  

NAME ^

Text::Editor::Easy::Events - Manage events linked to user code : specific code is referenced and called here.

VERSION ^

Version 0.49

INTRODUCTION ^

'Editor' instances will stand for 'Text::Editor::Easy' instances.

'Editor' instances have already a default management for a few events : mouse clic (set new cursor position), key press (insert or delete text), mouse drag (select text), resize.... What you may want to do when you define your special code in response to events must be explained :

As you see, event management is a nightmare. What could be the interface that would enable all this and would still be usable ?

As usual, easy things should be done lazily but difficult tasks should always be possible with, of course, a little more options to learn.

EASY THINGS ^

 my $editor = Text::Editor::Easy->new( 
    {                                     # start of editor new options
        'file'   => 'my_file.t3d',        # option 'file' has nothing to do with event management
        'events' => {                     # events declaration
            'clic' => {                   # first specific management, 'clic' event
                'sub' => 'my_clic_sub',
            },                            # end of clic event
            'motion' => {                 # second specific management, 'motion' event
                'sub' => 'my_motion_sub',
                'use' => 'My_module',     # as in perl 'use My_module' : without .pm extension
            },                            # end of motion event
        }                                 # end of events declaration
    }                                     # end of editor new options
 );                                       # end of new
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $clic_info_ref ) = @_;
 
     [...]
 }

'events' option

You can link your subs to events during the 'editor' instance creation with the 'events' option. This option takes a hash as a value. The keys of this hash are the name of the events : in the example, 'clic' and 'motion' events are managed. So, the first thing you have to know is the name of the events :

'sub', 'use' and 'package' options of one particular event in 'events' option

For each event managed, you have another hash which will contain, at least, the 'sub' option. Yes, this makes quite a lot of hashes, but they are the best way to make easy interfaces : you don't have to learn arbitrary positions (just think of other major langages), if the key names are well chosen, you learn the interface just reading an example and your code is auto-documented. I wonder how can other langages still exist without hashes...

Now, if you give nothing more than the 'sub' option, your sub should be visible and in the 'main' package. This point could be explained further. For simple things, you write your 'sub' in the same perl program file that makes the 'use Text::Editor::Easy;' call and you don't use perl package instruction.

If your program is more complex with more than one file, you can add the 'use' option which should indicate the name of a module that contains your sub. Be careful ! The default package is now assumed to have the same value as the module. If this is not true, you'll have to add 'package' option too :

 'motion' => {                     # 'motion' event of 'events' option
     'sub'     => 'my_motion_sub',
     'use'     => 'My_module',
     'package' => 'My_package',    # sub 'my_motion_sub' of 'My_module' is
                                   #     after a 'package My_package;' declaration
 },                                # end of 'motion' event

If you have used the perl 'package' instruction in your main program, you may use only the 'package' option without the 'use' option (in order not to have the 'main' default package assumed).

What about your specific 'sub' ?

Here are the 2 remaining things that have to be known :

received information

 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;             # $info_ref is a hash reference
 
     $editor->insert(
         ' useless text ',
         {
             'line' => $info_ref->{'line'},      # The insertion point will be
             'pos'  => $info_ref->{'pos'},       # the mouse clic position
         }
     );                                          # End of insert call
 }

You always receive 2 parameters :

Of course, you can't expect the information to be the same for a key press and for a mouse motion. The number and names of the hash keys will then depend on the event itself. All keys are explained for each event here. But it's easier to see all the possibilities for the keys and guess what you'll get for your event :

return value, 'action' option introduction

For easy things, your return value is not used. After your specific sub has been executed, the default management will be done (if any) with the same event information ($info_ref hash) that you have received.

But if you want your sub to be the last thing to be done in response to the event, you can add the 'action' option with the 'exit' value :

 my $editor = Text::Editor::Easy->new(
     {
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'exit',            # nothing will be done after 'my_clic_sub'
             },
         }
     }
 );

In this case, your sub will always be the last executed action. Sometimes, you would like to decide, according to the event information (so dynamically), if you want to go on or not. See here for dynamic exit.

In a more vicious way, you may want to change the values of the event information in order to change the data on which the default management will work. Again, the 'action' option gives you the power to lie.

A good easy thing would be that if 'action' option (with 'exit value) is present without the 'sub' option, then nothing is executed, just an exit is made :

 'events' => {
     'clic' => {
         'action' => 'exit',         # nothing will be done, no 'sub' option
     },
 }                                   # end of events declaration

As you see, 'sub' option is, in fact, not mandatory.

easy things conclusion

Nothing has been said about threads and dynamic event linking, but this is quite normal for the easy part of the interface. Still you can do half of what has been introduced at the beginning.

In an easy way, all events are done synchronously by the 'Graphic' thread : the 'Graphic' thread will have to complete your sub. So you may feel desperate if you have a huge task to do in response to a user event and if you still want your application to remain responsive : which seems incompatible, but...

EVENT INTERFACE SUM UP ^

Instance creation

There are 2 options in the 'new' method that deals with events :

Instance update

In order to update event management, here are the methods :

Information on events

In order to inquire the event management, here are the methods :

EVENT NAMES AND SEQUENCE ^

sequence of a 'true event', 'labels'

For each 'true event', a sequence of actions is done. Each action can be either an event action (= user action = private label) or a default action, named with a 'label'. For instance, the 'clic' sequence of the 'clic' 'true event' contains the following actions :

So there is a big difference between a 'true event' (the 'clic' made by the user) and an 'event action' (code linked to an event but at a precise moment in the sequence). In the previous 'clic' example, you have to choose between 5 event names to place your user action in the sequence.

Each user action is linked using the label of the generated event. For instance, if you want to use the 'after_clic' event instead of 'clic' event, you just have to write :

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'after_clic' => {             # 'after_clic' event, done after default
                'sub' => 'my_sub',        # management (generated 'clic' event is done before)
            },
        }
    }
 );

Still, the 'after_clic' is not a 'true event', it's just an action in the sequence. The difference is important because you can define your own sequence but you'll have to use the 'true event' name to do that.

In the sequence, you can easily guess which is an event and which is a default action thanks to the beginning : everything that begins with '_' is a default action otherwise, it's a generated event.

A generated event is just a predefined private label : the label is already defined in the default sequence but you have to link your action to that label (no action by default).

When you create your own sequence, everything that does not start with a '_' is considered as a private label and private code linked to that label is searched.

So event names used with the 'events' option are just private labels contained in a 'true event' sequence.

In conclusion, by default, the 'true event' 'clic' is linked to a sequence that contains 5 possible private labels (named 'clic', 'after_clic', ...).

modifier keys

The modifier keys, which are 'alt', 'ctrl' or 'shift', bring complexity in a too simple event management. In the following explanations 'modifier keys' will be called 'meta keys'...because it's shorter.

As you may know, these keys or any combination of these keys can be associated with a standard key press, mouse clic, mouse motion, ... and you could think of that in 2 ways :

As a programmer, the first approach can lead to single sub managing different events (which is not very clear), but the second one can lead to multi-declaration pointing to the same sub, which is too verbose (and not very clear in the end).

So let's have the 2 possible ways : it's your business to choose the one that is the more efficient according to your wish. For any combination of 'meta-keys', you'll have to add any_ prefix. For a particular combination, you'll have to add the 'combination string' as prefix, for instance alt_ or ctrl_shift_. Note that in the 'combination string', 'meta-keys' are listed in alphabetic order : 'alt', 'ctrl' and then 'shift'.

As an example, you could press 'alt' when making a 'clic' with your mouse. The 'true event' name will be 'alt_clic' and the sequence will be the following :

In conclusion, the 'true event' 'alt_clic' is linked to a sequence that contains 4 private labels (named 'any_clic', 'alt_clic', ...).

The 'any_clic' is then done for a simple 'clic' and for any combination of meta-keys (including 'alt').

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'alt_clic' => {               # 'alt_clic' event, 'alt' key pressed
                'sub' => 'my_alt_sub',
            },
            'any_clic' => {               # 'any_clic' event, whatever meta-keys
                'sub' => 'my_any_sub',
            },
        }
    }
 );

about 'any_any_clic'

'clic' is in fact the left simple clic. Right simple clic and left double clic are often used too. For instance, the true event 'shift right simple clic', named 'shift_right_clic', has the following sequence :

any_any_clic is then done whatever the 'clic' (simple, double or right) and whatever the meta-keys.

changing the sequence

'sequence' option in a 'true event'

During instance creation, if you want a different sequence from the default one, you can define your own with the 'sequence' option in the 'true event' declaration.

In the sequence, each label that does not start with '_' is considered as an event (= private label).

So you can create your own events in your sequence. But this is not a real event creation, because your event depends on a 'true event' to be generated. Creating a 'true event' will be another matter.

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'alt_clic' => {
                'sequence' => [            # changing 'alt_clic' sequence
                    '_calc_line_pos',      # getting 'line' and 'pos'
                    'my_foo_clic',         # private label (declared in this sequence)
                    '_set_cursor',         # not done by default 'alt_clic'
                ],
            },
            'my_foo_clic' => {             # 'my_foo_clic' label definition, whatever the sequence
                'sub'     => 'foo_sub',    # 
                'thread'  => 'Foo',        # complete interface possible as any predefined event
            },
        }
    }
 );

Defining such a sequence is somewhat 'static'. For each 'alt_clic' event of this instance, this sequence will be used. You may want to have very specific sequences from time to time, and use the 'default' one or your 'static' one the rest of the time. These dynamic sequences are possible thanks to the 'action' option.

'sequences' key of the 'new' method

The previous example could have been written like this as there is just a sequence defined for the 'alt_clic' event (no action is really done) :

 my $editor = Text::Editor::Easy->new( 
    {
        'sequences' => {
            'alt_clic' => [                # changing 'alt_clic' sequence
                '_calc_line_pos',
                'my_foo_clic',
                '_set_cursor',
            ],
        },                                 # End of 'sequences' declaration
        'events' => {
            'my_foo_clic' => {             # 'my_foo_clic' label definition
                'sub'     => 'foo_sub',
                'thread'  => 'Foo',
            },
        }
    }
 );

'sequences' key is at the same level as 'events' key. You can change sequences without writing event code.

With this syntax, you can change as many sequences as you wish : the keys of the 'sequences' hash are the true event names, and the values are array references representing the sequences.

Note that if a sequence is defined both in a 'sequence' of a 'true event' and as a sub-key of the 'sequences' key, the 'sequences' definition is taken.

'ACTION' OPTION ^

For each event, if 'action' option is used, its value must be one of the following.

With 'change', 'jump' and 'reentrant' values, the return value of your specific sub is used.

'exit' value

As seen in the easy part of the interface, you can put an 'exit' action alone or with a 'sub' option. In the second case, your sub will be the last action executed in the event sequence.

'change' value

 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'change',     # event information can be changed by 'my_clic_sub'
             },
         }
     }
 );                                        # end of new
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     $info_ref->{'pos'} = 0;  # setting position to the beginning of the line
     return $info_ref;        # Returning a hash reference with the same keys,
                              #     'pos' value probably changed (was perhaps already 0)
 }

With 'change' value you can modify if you wish the values of the hash reference $info_ref which contains your event information. The following labels of the sequence (including yours) will use your new values (if there are no more change action).

Any hash reference returned will be considered as the new "info", any other return value will be ignored : no change will be assumed. If you want to exit dynamically, you have to use 'jump' value.

'jump' value

 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'jump',                     # a jump can be done
             },
         }
     }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     my $line = $info_ref->{'line'};
     if ( $line->text ) ) {
         $info_ref->{'pos'} = 0;
         return $info_ref;                               # no jump, values changed
     }
     while ( ! $line->text ) {
        $line = $line->next;
        if ( ! defined $line ) {
            $line = $editor->last;
            last;
        }
     }
     my %new_info = ( 
         'line' => $line,
         'pos'  => 0 ),
     );
                                                         # jump to '_set_cursor' label
     return [ '_set_cursor', \%new_info ];               # providing the hash required
                                                         # here, only '_test_resize' has been jumped
 }

You may return from your specific sub managing a 'jump' action in 3 different ways :

smallest possible 'jump'

As you may link more than one sub to one event, the smallest possible jump is done when you give the label following your own one. In this case, what you have jumped are the possible other subs that were linked to the same label as yours, these subs should have been done after your own one (if no jump has been made).

about info keys and labels

If the labels that you've jumped would have added keys to the information hash and if these keys are needed for the labels you jump to, you may have to provide these keys yourself.

'reentrant' value

 my $editor = Text::Editor::Easy->new( 
     {
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'reentrant',     # dynamic sequence enabled
             },
             # Defining private labels
             'search_error'      => { 'sub' => 'search_error' },
             'highlight_error'   => { 'sub' => 'h_err', 'action' => 'reentrant' },
             'error_information' => { 'sub' => 's_info', 'thread' => 'Info' },
         }
     }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;

     if ( $info_ref->{'line'}->text =~ /error/i ) {
         return [ [ 'seach_error', 'highlight_error', 'error_information', '_exit' ] ];
     }
 }

'reentrant' value for action option includes all the 'jump' possibilities plus the dynamic sequence.

A dynamic sequence is just a sequence of labels (default or private ones). Note that this sequence is done after your sub, then the normal sequence is going on (that's why the 'reentrant' name has been given). You can make endless loops if a reentrant label calls itself.

If you want the 'dynamic sequence' functionality without the 'reentrant' one, you can add an '_exit' label at the end of you dynamic sequence (last action to be done).

Action made according to 'reentrant' return value sub :

'nop' value

This special value should always be used with the 'thread' option (read further, THREADS CONTRIBUTION).

If a sub executed by a thread is huge, you may write it in an interruptible way (writing instructions like return if anything_for_me; from time to time). A 'nop' action just sends a false task to your thread (there is something for it and it's ... nothing !) in order to interrupt its long task.

THREADS CONTRIBUTION ^

Just imagine the future : computers with more than one CPU... No sorry, that's just present : as for me, I have a dual core. But imagine that, as a programmer, you could use your 2 (or maybe more) CPU very easily : for instance, just using threads...

As you can add threads to manage new methods with 'Text::Editor::Easy' objects, you can use (or create) as many threads as you want to manage events. Of course, dividing a precisely defined job into more pieces than you have CPU won't be more efficient. But very often, with interactive applications, we don't have a precise job to do : tasks to be done change sometimes so fast, depending on user actions, that what was interesting to do at one moment could be useless just a few milliseconds later. With a multi-thread application, you not only give yourself the power to use all of your CPU, but you also give yourself the power to interrupt useless tasks.

When you use the 'Graphic' default thread, your event sub is synchronous, that is the code of your event will freeze the user interface : you should not use it for heavy tasks. For little tasks, this freeze won't be noticed. If you have a huge task to do in response to an event, you can use another thread than the 'Graphic' one. In this case, the 'Graphic' thread still receive the initial event (you can't change that !) but as soon as enough information has been collected, your thread is called asynchronously by the 'Graphic' thread (the 'Graphic' thread won't wait for your thread response). And here, if you make a heavy task, the user interface won't be freezed.

Still, with any thread, you should work in an interruptible way rather than make a huge task at once. Why ? Because the principle of events is that you can't know when they occur and how many. Suppose your code responds to the mouse motion event : when the user moves his mouse from left to right of your editor, you can have more than 10 mouse motion events generated in one second. And a perfect response to the first event can be useless as soon as the second event has occured. Moreover, the 'Graphic' thread will send all asynchronous events to your thread, even if it is busy working. Events will stack in a queue if your thread can't manage them quickly. If your code makes, in the end, a graphical action visible by the user, there could be a long delay between the last event and its visible action. And the user would consider your code as very slow. On the contrary, if you work in an interruptible way, that is, if you insert, from time to time, little code like that :

 return if (anything_for_me);       # "me" stands for your thread executing your code

the user could have the feeling that your code is very fast : this is because you empty your thread queue more quickly and thus decrease the delay between the last event and its answer. But you should add this line (that is, check your thread queue) when you are in a proper state : just imagine that there is really something for you and that your code will be stopped and executed another time from the start.

The conclusion is : "A good way to be fast is to give up useless tasks" and using more than one thread allows you to give up, so don't hesitate to give up. This is the very power of multi-threaded applications : the ability to make huge tasks while remaining responsive. This does not mean that programmers can still be worse than they are now (me included !) : they have to know where to interrupt.

'thread' option

 my $tid = Text::Editor::Easy->create_new_server(
     {
         ... # see Text::Editor::Easy::Comm for mandatory options
         'name' => 'My_thread_name',
     }
 );
 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'thread' => 'My_thread_name',    # $tid could have been used
                                                  # instead of 'My_thread_name'
             },
         }
     }
 );

The value of the 'thread' option is the name of the thread you have chosen (should contain at least one letter), or the 'tid' (thread identification in perl ithread mecanism) that the program has chosen for you (it's an integer).

Note that by default, if you give the 'thread' option, an asynchronous call is assumed. The 'Graphic' thread asks your thread to execute your sub but doesn't wait for its response.

'create' option

In the 'thread' option example, the thread had already been created, but if you use 'thread' option with a name that is unknown, a new thread will be created on the fly and will be named accordingly.

On the contrary, if you have written a bad name by mistake, you may want to prevent this auto-creation. The 'create' options has 3 possible values :

Maybe you feel that the 'create' option should have been used to enable creation not to prevent it. But you are a perl programmer and should feel responsible : the more irresponsible the languages assume the programmer is, the more verbose your programs have to be and the less accessible the languages are. Langages should definitely consider programmers as responsible persons.

So you don't have to use the 'create' option if you want an auto-creation and that could be called lazyness or responsability.

'sync' option

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread_name',
         'sync'   => 'true',              # A 'true' value : the call will be synchronous
     },
 }

Well, the benefit of threads seems to be brought only by asynchronous calls, but there is a reason why you could wish a synchronous call. You may want to initialize data in your thread while changing values for the default event management. And the initialized data will be used after the default management in an asynchronous way. So you don't have to share variables between threads just because you want some events to be synchronous : variable 'scope' can then be limited. Read carefully the deadlock possibility if you use this option.

This point is easier to understand when you know that, for instance, for a single mouse clic, you can manage up to 3 different events.

Now what about the 'sync' option with a 'false' value ? You will force an asynchronous call and this could be used ... for the 'Graphic' thread ! This trick won't prevent you from freezing the user interface if your code is huge, but if you know what you are doing...

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'sync'   => 'false',           # the 'Graphic' thread 
                                        # will execute 'my_clic_sub' asynchronously
     },
 }

The 'Graphic' thread asks a task to itself (puts it in its thread queue) but doesn't execute it immediately : first it has to end this event management. Once finished, it will execute its tasks in the order they have been queued... as well as manage other possible user events. Yes, the 'Graphic' thread is very active and it's very difficult to know when it will have a little time for you.

You would get the same result with this :

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'Graphic',         # 'thread' option present => asynchronous call assumed
     },
 }

'sync', 'thread' and 'action' incompatibilities

When you work in an asynchronous way, the 'Graphic' thread doesn't wait for your answer. Then it can't receive a label for a 'jump' action or a return value for a 'change' action. So, only the 'exit' value of 'action' option is valid with asynchronous call.

deadlocks, 'pseudo' value for 'sync' option

When you use a thread in a synchronous way in an event management, you should understand what the 2 implied threads are doing :

So there is a very bad thing that could happen : if your thread asks for a service that is managed by the 'Graphic' thread... you know what follows... As the 'Graphic' thread is waiting for your answer (synchronous call), it can't serve your request so your thread waits endlessly for the 'Graphic' thread response and the 'Graphic' thread waits endlessly for your thread response. Everything is freezed forever (in fact some other threads are still working, but the 'Graphic' thread is the visual one and the only one versus the window manager).

So, synchronous calls initiated by the 'Graphic' thread can't use a 'Graphic' service. This is quite limiting but you could have to work with this limitation and find a solution : in the end, this is your job as a programmer.

Note that this is a general problem of multi-thread programmation : when a server thread asks another thread for a synchronous task, the executing thread can't use any service of the calling thread. The limitation is not linked to graphics.

tracking deadlock

That kind of deadlocks could be checked and will be managed like that :

...but it's not yet managed. So in case you have a permanent freeze, you'll have to guess from which call the deadlock was introduced. Of course such a management will not be provided as a solution but as a help during development : this situation reveals a problem in conception.

'pseudo' value for 'sync' option

There is already a solution that could suit you : the 'Graphic' thread can make a 'pseudo-synchronous' call. It calls your thread asynchronously getting the 'call_id' (see Text::Editor::Easy::Comm). Then the 'Graphic' thread enters a loop where it checks 2 things at the same time :

In order to have such a permissive management, you just have to use the 'pseudo' value for the 'sync' option :

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
         'sync'   => 'pseudo',            # 'My_thread' can make calls to the 'Graphic' thread
         'action' => 'change',            # ... and can return a value to the 'Graphic' thread
     },
 }

So why should we keep the 'true' synchronous call ? Because with a pseudo-synchronous call, there is quite a big indetermination in the order of execution of the different tasks and maybe there is a chance that your deterministic program produces, sometimes, chaotic results. So 'pseudo' value for 'sync' option is provided but may lead, from time to time, to unexpected results, you are warned.

Note that chaotic reponses can be obtained with asynchronous calls too. Maybe a good thing to do is to change 'common data' thanks to synchronous calls and only. Asynchronous calls should only be used for displaying common data or changing private data (private to a thread). So using $editor->display or $line->select in an asynchronous called sub is OK but using $editor->insert or $line->set can lead to a race condition with unpredictible result, see "Thread Pitfalls: Races" in perlthrtut. In fact, 'Text::Editor::Easy' manages editor data in a private way (only 'File_manager' thread knows about the file being edited) but as methods can be called by any thread, these data should be considered as shared : if the cursor position is not the same when the event occurs as when you make your $editor->insert in your event sub (because an other thread have changed this position between the event and your asynchronous sub), the result may look funny (still worse if a delete has removed the line you were expecting to work on !).

DYNAMIC CONTRIBUTION ^

updating events and sequences

Suppose that your program have several 'Text::Editor::Easy' instances running. We've seen that you can add new instances with specific event management. Thanks to 'dynamic contribution', you can change event management of already running instances. A generalization of this 'dynamic contribution' is to have a default set of events used for future created instances.

The dynamic interface let you modify :

'Modify' event management should be understood as one of these possibilities :

Moreover, as you can define your own sequences during instance creation, you can :

set_event method

In order to change a single event, you have to use the 'set_event' method.

 Text::Editor::Easy->set_event( 
     'clic',                           # first parameter
     {                                 # second parameter, hash
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
     },
 };

In the previous example, the 'clic' event of 'all instances' (class call) will be changed.

The 'set_event' method accepts from 1 to 3 parameters :

set_events method

The final 's' of 'set_events' method makes all the difference : here you redefine all the events at once (like 'events' option during instance creation)

 $editor->set_events( 
     {
         'clic', {
             'sub'    => 'my_clic_sub',
             'thread' => 'My_thread',
         },
         'motion', {
             'sub'    => 'my_motion_sub',
         },
     }
 };

In the previous example, all specific event management have been re-set for the existing instance $editor. Of course, only 'clic' and 'motion' events are defined here, but if the 'drag' or 'change' events were linked to specific subs, these old links are cut. If you want to keep an old specific management with 'set_events' method, you'll have to repeat it in order not to erase it.

The 'set_events' method accepts 1 parameter which exactly corresponds to the 'events' option used during the instance creation. For class call of 'set_events', an optional second parameter is possible.

Calling 'set_events' with no parameter (or an empty hash) will delete any specific event management.

set_sequence method

 # instance call
 $editor->set_sequence(
     {
         'clic' => [ 'my_action1', 'my_action2', '_set_cursor' ],
     }
 );
 
 # class call
 Text::Editor::Easy->set_sequence( 
     {
         'clic'   => [ 'my_action1', 'my_action2', '_set_cursor' ],
         'motion' => [ 'my_action2', 'motion', 'my_action3' ],
         'drag'   => [],
     },
     {
         'name'    => qr/\.pod$/,
     }
 );

You can change sequences after instance creation. There is no 's' at the end of 'set_sequence' : you can give more than one sequence to change in the first hash parameter but only the keys that you've given will be changed (different from 'set_events' principle).

Defining 'all instances', options for class calls

If you've read carefully the 2 previous examples, you already know that an instance call changes only one instance and a class call changes 'all instances'.

But 'all instances' is not very clear : only existing instances, only the ones that will be created from now, both, ... ?

Here, we're talking of class calls of 'set_event', 'set_events' and 'set_sequence' methods and we want to precise the subset of instances to which the changes will apply.

 # 'set_event' class call example with options
 
 Text::Editor::Easy->set_event( 
     'clic',                             # First parameter
     {                                   # Second parameter
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
     },
     {                                   # Third optional parameter
         'instances' => 'future',
         'values'    => 'undefined',
     }                                   # End of third parameter
 };
 
 
 # 'set_events' class call example with options
 
 Text::Editor::Easy->set_events( 
     { 'clic' =>                         # First parameter
         {
             'sub'    => 'my_clic_sub',
             'thread' => 'My_thread',
         },
     }
     {                                   # Second parameter
         'names' => qr/\.pl$/,           # 'Regexp' object
     }                                   # End of second parameter
 };

You can add a third parameter to 'set_event' method or a second parameter to 'set_events' and 'set_sequence' methods. This last parameter is an optional hash with the following keys :

'values' options with instance call

You can use the 'values' option with an instance call if you are too lazy to check what you've done before.

endless complexity with future instances

If you have made several class calls with 'set_event', 'set_events' and / or 'set_sequence' methods that affects new created instances, what will happen when a new instance will be created ? Which tests will be made, in which order ...?

The answer is : all tests will be done and in the order you have made the calls. As a joke, if you make calls from different threads, the order will be undefined !

This is a very complex mecanism of default event management. The interface to get all these default actions (done at each instance creation) or set or unset all these default actions in just one call follows.

events method

 my $events_ref = $editor->events;           # gets definition of all events in a single hash
                                             # (no parameter given)
 my $new_editor = Text::Editor::Easy->new;
 $new_editor->set_events( $events_ref );     # new editor with identical global event management
 
 
 my $event_ref = $editor->events('clic');    # gets definition of a single event (name given)
 my $new_editor = Text::Editor::Easy->new( {
     'events' => {
        'clic' => $event_ref,                # new editor with identical clic event management
     }
 } );

This method retrieves the event(s) definition of an instance (instance call). The first optional parameter is the name of an event.

With a class call, what you get is an array reference of the different force actions that are done during instance creation (ordered default event management with their options, according to class calls that you've made before : 'set_event', 'set_events' or 'set_sequence' class calls). For each element of the array, you've got :

sequences instance method

 my $hash_ref  = editor->sequences;                    # gets all specific sequences
 
 my $array_ref = editor->sequences('clic');            # gets 'clic' specific sequence

This instance method retrieves specific sequence(s) :

set_default class method

 my $default_ref = Text::Editor::Easy->events;       # gets all actions done by default
 
 Text::Editor::Easy->set_default( undef );           # deleting all default event management
 
 my $editor->Text::Editor::Easy->new;                # won't inherit former default event management
 
 Text::Editor::Easy->set_default( $default_ref );    # setting back default actions

You can make a list of 'set_event', 'set_events' and / or 'set_sequence' class calls that will be applied for future instances in just one call with 'set_default' method.

dynamic designing, 'code' option

Perl is dynamic : you can 'eval' new code during execution and in the context of the running program.

Suppose your program is (or contains) an editor, that sounds great ! Your program can ask you for new code to edit (or old one to change) and will go on running using this very code ! You can call that the way you want : a 'dynamic application', a limitless 'macro langage', the best configuration tool ever, or the most dangerous thing (it's true that powerful things put in bad hands are dangerous, but skilled people shouldn't be limited because of unskilled ones).

The 'code' option can replace 'sub', 'use' and 'package' options in standard and static event definition.

This option accepts a string that represents the code of the event.

Note that you must not start your code with 'sub { ...' : you should consider that you are already inside an unnamed sub.

 $editor->set_event( 
     'clic', { 
         'code' => 'print "Hello\n"';
     },
 );
 
 # is almost equivalent to
 
 $editor->set_event( 
     'clic', { 
         'sub' => 'hello';
     },
 );
 
 sub hello {
     print "Hello\n";
 }

About the differences :

On the paper, this 'code' option seems useless because you have to write the code anyway (some furious programmers could think of 'computer generated' code, though). But if the code is written after you have started your application ... and by the user himself : see 'demo12' provided with the 'Editor.pl' program to understand.

PELL-MELL ^

Multiple subs for same event

You can link more than one sub to a single event. This can be interesting if you want to mix synchronous and asynchronous responses or just if you have 2 very different things to do and don't want to hide them in a bad named sub.

 my $editor = Text::Editor::Easy->new( 
     {                                          # start of editor new options
         'events' =>                            # 'events' option
         {
             'clic' =>                          # 'clic' event management
             [                                  # array reference : more than one sub possible
                 {
                     'sub' => 'my_clic_sub_1',  # first sub to execute in response to 'clic' event
                 },
                 {
                     'sub' => 'my_clic_sub_2',  # second sub to execute in response to 'clic' event
                 },
             ],                                 # end of clic event
             'motion' =>
             {                                  # hash reference : single sub management
                 'sub' => 'my_motion_sub',
             },
         }                                 # end of 'events' option
     }                                     # end of editor new options
 );                                        # end of new

The sub declaration order is very important in your array : subs are called in this order. So, if you use the 'action' option in the first event, other events could work with modified event information or could just be jumped.

Sending more data to your event sub

'execute_sequence' method

Sequences are powerful, so why should we limit them to event management ?

 $editor->execute_sequence( 
     [ '_show_editor', '_key_default', 'my_label' ],
     {
         'text' => "Text insertion without focus\n",
         'meta_hash' => {'alt' => 0, 'ctrl' => '0'}
     }
 );

This instance method needs at least one parameter :

You could have done the first 2 actions of the example with the object interface :

 $editor->at_top;
 $editor->insert( "Text insertion without focus\n" );

saving event data

Suppose you've done complex things with the event management. Some events of a few instances are managed in a static way with subs written in different modules but other events are managed in a dynamic way with code in memory but saved nowhere...

This is a real mess but that costed you a lot to come to this ugly point and you wouldn't like to lose everything when your program will stop : either in a proper way or by a crash due to numerous bugs.

The session management should help you save everything of your instances in order to get the 'same' state that you've had before quitting.

'Text::Editor::Easy' module should be considered as a low-level module. It will never know anything about session. But there are enough possibilities to easily add this feature.

private keys

'events' options of the 'new' method and 'set_event(s)' method are insensitive to an addition of private keys :

 $editor->set_event( 
    'clic',
    {
        'sub'       => 'my_clic_sub',
        'duration'  => 'endless',       # private key, just ignored by 'set_event'
    }
 );

Limitations : the 2 following keys are used for internal optimisation and can't be private :

'events' method, saving configuration

In order to save event management, you just have to call the 'events' method for each editor and test your private key :

 use Data::Dump qw( dump );                # To save a hash in a file
 my $event_ref = $editor->events;
 while ( my ( $key, $value_ref ) = each %$event_ref ) {
     my $duration = $value_ref->{'duration'};
     if ( ! defined $duration or $duration ne 'endless' ) {
        delete $event_ref->{$key};         # session event : not to be saved
    }
 }
 print CFG dump( $event_ref );             # CFG <=> 'config.file'

loading configuration

 my $event_ref = {};
 if ( -f 'config.file' ) {
     $event_ref = do( 'config.file' );     # gets hash back from file
 }
 my $editor = Text::Editor::Easy->new(
     {
         'events' => $event_ref,
         ...
     }
 );

warning

You should only use names for editor instances and threads if you plan to save configuration. tids (thread identification) or references (address of memory location) have almost no chance to remain the same after a restart.

defining your own label (own default action)

You can create sequences as you wish (static ones or dynamic ones). In these sequences, every label that does not start with '_' is considered as an event or a private label.

creating a private label ('set_event' method)

 # Instance label (known by only one instance)
 $editor->set_event( 'my_action',                    # label 'my_action'
     {
         'sub'    => 'my_sub',
         'action' =>  'change,
     }
 }
 
 # Class label (known by all instances)
 Text::Editor::Easy->set_event( 'my_class_action',   # label 'my_class_action'
     {
         'sub'    => 'my_other_sub',
         'thread' =>  'Foo',
     }
 }

using a private label

All you have to do is to define a sequence that contains one of your labels :

 $editor->set_sequence( {
    'shift_motion' => [ '_show_editor', 'my_action', 'my_class_action' ],
} );

The true event 'shift_motion' will then execute a predefined label (default action '_show_editor') and your 2 private labels (these 2 newly defined labels can also be called : "generated events that were not predefined").

simulating events...

Events can be simulated with method calls.

Here are examples :

 # true clic simulation
 $editor->clic( {'x' => 27, 'y' => 5 } );
 
 # true 'shift_motion' simulation
 $editor->motion( {'x' => 27, 'y' => 5, 'meta' => 'shift_' } );
 
 # true 'ctrl_drag' simulation
 $editor->drag( {'x' => 27, 'y' => 5, 'meta' => 'ctrl_' } );

Here are the usable methods :

There is a little difference in the information that you get. The 'caller' key won't be starting with a 'U_' but with an integer (the tid of the thread that made the simulation event call).

...and forcing the sequence

All the previous methods accepts one parameter (the information hash) and an optional sequence.

 # true clic simulation with a private sequence (just for this simulated event)
 $editor->clic( {'x' => 27, 'y' => 5 }, [ '_set_cursor', 'my_foo_clic' ] );

This sequence is 'stronger' than any other (default or overloaded) but doesn't last after the method call.

EVENT LIST ^

For each 'true event', sequences are explained.

For each event, the hash information you receive as second parameter in your sub is explained.

Labels defining a default action are explained here.

Lots of events are not yet managed ('save', 'resize', 'destroy', 'lost_focus', 'get_focus', ...).

general prefixes

Thanks to modifier keys, events can be very numerous. See modifier keys for explanations.

Here are the 9 usable prefixes with most events :

general keys of info_ref hash

Whatever the event, the second parameter of your event sub is a hash containing, at least, the following keys :

'meta_hash' and 'meta' can be interesting for 'any_.*' events :

 my $editor = Text::Editor::Easy->new( 
    {
        'file'   => 'my_file.t3d',
        'events' => {
            'any_clic' => {                   # clic for any modifier key combination
                'sub'    => 'my_clic_sub',
            },                         
        }
    }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     if ( $info_ref->{'meta_hash'}{'alt'} ) {
         print "You pressed the alt key during the clic\n";
     }
     print "Meta combination string is ", $info_ref->{'meta'}, "\n";
 }

clic subset

There are 27 predefined 'clic' event labels (3 x 9) given the 3 following suffixes :

Here are examples of event names that you could use as keys of the 'events' hash during editor instance creation :

 clic
 ctrl_clic
 alt_shift_right_clic
 any_right_clic
 alt_ctrl_shift_double_clic   # for a hidden functionality !

There are also 3 more 'clic' events :

information received in clic subset events

The information received by your sub is a hash containing the following keys :

'clic' sequence

The sequence depends on the meta-keys. With no meta-keys pressed :

With meta-keys pressed, here is the sequence :

Scalar $meta represents one of the following 7 possibilities :

 'alt_'
 'ctrl_'
 'shift_'
 'alt_ctrl_'
 'alt_shift_'
 'ctrl_shift'
 'alt_ctrl_shift'

As you can see, there are no default action if a meta-key is pressed ('_calc_line_pos' adds just 'pos' and 'line' keys to the information hash).

'right_clic' sequence

There is just one sequence (no real default actions) :

$meta represents one of the following 8 possibilities :

 ''
 'alt_'
 'ctrl_'
 'shift_'
 'alt_ctrl_'
 'alt_shift_'
 'ctrl_shift'
 'alt_ctrl_shift'

'double_clic' sequence

There is just one sequence (no real default actions) :

Be careful, a double clic is... 2 clics !

motion subset

motion information

The information received by your sub is a hash containing the same keys as clic events :

motion sequence

In fact, it's said in the introduction that the motion event is not managed by default, and this is wrong.

Without meta-keys pressed, here is the sequence :

With, meta-keys, it's true that there is no default management :

So you have a possible of 11 motion events.

drag subset

drag information

drag sequence

As there are default actions for the drag, there are 2 different sequences according to meta-keys. With no meta-keys pressed, the sequence is :

With any meta-keys, the sequence is :

key subset

key information

Don't expect too much about these informations. If there are all right, it's just that you have an american keyboard.

key sequence

'any_any_key' happens whatever the key pressed and whatever meta_keys (similar to 'any_any_clic').

wheel subset

wheel information

wheel sequence

There are 2 sequences depending on meta-keys. With no meta-key pressed, the sequence is :

With any meta-key pressed, the sequence is :

change event

At present, this is rather an 'after_change' because it happens once changes have been applied. This is just a start. Meta-keys can't be used to create numerous 'change' events.

No sequence is done (will belong to other sequences : it is not a true event).

cursor_set event

As change event, this is rather an 'after_cursor_set' event. Meta-keys have no sense too.

No sequence is done (will belong to other sequences : it is not a true event).

LABEL DETAILS (DEFAULT ACTIONS) ^

All these actions can be used in your specific sequences. Every label that does not start with a '_' is considered as a generated event (= private label).

For each default action, keys needed in the information hash are explained as well as keys added.

_calc_line_pos

This sub needs 'x' and 'y' keys.

Adds 'line' and 'pos' keys to the information hash.

This label could be used in a user sequence where an event changes 'x' and 'y' coordinates and wants to have 'line' and 'pos' keys coherent with the new 'x' and 'y'.

_test_resize

This sub needs 'x' and 'y' keys.

Adds 'resize' key.

If the cursor has a double arrow shape, then the clic is considered as a resize command (rather than a "set cursor" one) : in this case, the 'resize' key is added to the information hash with a true value (value is 1) and '_set_cursor' label is jumped (label is set to 'any_after_clic' in case there are 'after_clic' events).

If the clic is not considered as a resize command, then the 'resize' key is still added but with a false value (value is 0). Of course, there is no jump.

_set_cursor

This sub needs 'pos' and 'line' keys.

4 little actions are done :

_update_cursor

Needs 'x' and 'y' keys.

Sets mouse cursor shape according to its position versus the editor borders (in order to initiate a resize command, double arrow, or a cursor set command, simple arrow).

_show_editor

This sub does not need any key.

It places the editor on top of all the other ones (in case, some parts of the editor is under other ones).

_zone_resize

Needs keys 'shape', 'x' and 'y'.

According to 'shape' key, resizes editor.

_drag_select

Needs 'line' and 'pos' keys.

Selects text.

_wheel_move

Needs 'move' key.

Scrolls the editor.

_key_code

Needs 'key_code' key.

There are in fact 2 different systems of event management for key pressed events. The general one, using 'events' option (or 'set_event(s)' methods) and another one using method 'bind_key' (with a class call, for a global binding, or an instance call).

Threads and code are usable only with the general event management. With 'bind_key' method, you can use 'sub_ref' option (a sub reference) but you have to call the 'bind_key' method from the thread with tid 0 : 'bind_key' user subs are always executed by thread 0.

Maybe these 2 systems of key event management will go on living together...

The '_key_code' action is just a call to the good 'bind_key' sub : first, a specific (to the instance) 'bind_key' sub is looked for. If no specific binding is found, a global binding is looked for.

_key_default

Needs 'text' and 'meta_hash' keys.

Insert 'text' provided that no 'ctrl' or 'alt' key is pressed.

_exit

No key needed.

Could be used in a dynamic sequence (in order to avoid other following actions).

_jump

'jump' key needed.

Could be used in a dynamic sequence to make a dynamic jump according to previous actions. (The 'jump' value is just the next label to be done, see jump action).

EVENTS LINKED TO A ZONE INSTANCE ^

Events can be linked to a 'zone' instance rather than to a 'Text::Editor::Easy' instance.

2 events are acessible for a 'zone' instance :

CONCLUSION ^

Maybe the interface seems a little complex in the end, still complexity have not been added freely. If this interface is adapted to programs that change, then the goal will be reached :

COPYRIGHT & LICENSE ^

Copyright 2008 - 2009 Sebastien Grommier, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

syntax highlighting: