The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

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

VERSION

Version 0.47

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 :

  • Link code to an event not managed by default (for instance, the mouse motion)

  • Add an action to an already managed event

  • Inhibit the default action and make your own instead

  • Just inhibit the default action (here you don't write code)

  • Have your specific code executed in an asynchronous way (by a specific thread) in order to make a non freezing huge task

  • Have you specific code executed by a specific thread in a synchronous way

  • Have all these possibilities defined during the 'editor' instance creation or later.

  • Link more code to an already linked event (more than one specific sub for only one event) ...

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 :

  • 'clic', modifier keys (alt, ctrl and/or shift) can be used

  • 'motion', mouse move, modifier keys can be used

  • 'drag', mouse move with left mouse button pressed, modifier keys can be used

  • 'change', happens when the text of the editor is changed (text added, deleted or replaced).

  • 'cursor_set', happens when the cursor position is changed.

For complex things, here is the complete list but read first "EVENT NAMES AND LABELS".

'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 'Text::Editor::Easy' calls 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 :

  • What will your sub receive ?

  • What should return your sub ?

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 :

  • The 'editor' instance that has received the event.

  • A hash reference that contains information relative to the event.

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 :

  • 'line' : a 'line' instance.

  • 'pos' : a position in that line

  • 'x' : an absisse (for 'hard' events)

  • 'y' : an ordinate (for 'hard' events)

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 you 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 most 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 NAMES AND LABELS

For a single user event, there can be lots of 'Text::Editor::Easy' events generated. Let's take the 'clic' example. For a single mouse clic, you have 'basically' the 3 following 'Text::Editor::Easy' events :

  • hard_clic (send 'x' and 'y' coordinates to your specific sub)

  • clic (send 'line' and 'pos' to your specific sub : an editor point of view of 'x' and 'y')

  • after_clic (cursor has been moved to 'line' and 'pos' by default management, still send 'line' and 'pos' to your sub)

Now this is true only if no 'meta-key' was pressed during the clic : 'alt', 'ctrl' or 'shift'. 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 :

  • the event mixed with a meta-key is the same, there is just more information

  • the event mixed with a meta-key is another one and should be named differently

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'.

Let's sum up. The 'hard_clic' event is, in fact, the first 'Text::Editor::Easy' clic event (returning 'x' and 'y') with no 'meta-key' pressed. The 'any_hard_clic' event is the first 'Text::Editor::Easy' clic event whatever the 'meta-keys' combination (none pressed or more). For a given combination of 'meta-keys' (let's take 'shift_'), there are 6 'Text::Editor::Easy' events generated in the following order :

  • any_hard_clic

  • shift_hard_clic

  • any_clic

  • shift_clic

  • any_after_clic

  • shift_after_clic

Each of these names corresponds to :

  • an event name usable in the 'events' option of the new method

  • a 'label', which is a precise moment of the 'clic' management

When a default management is defined for a label, the order of execution is, first, the event associated with that label (if any), then, the default management.

MORE ON 'ACTION' OPTION

We've seen the 'exit' value of 'action' option that ends the event management. There are 2 other possible values :

  • 'change' to change event values or exit dynamically

  • 'jump' to go straight to a precise event label

With any of these 2 values, the return value of your specific sub is used and is very important !

'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 default management and the possible following events will use your new values. If you don't provide a hash with exactly the same keys as you have received, then an exit will be done. This can be used to exit dynamically : all you have to do in order to exit is a simple "return;" ('undef' value is not a hash...) as long as you have the 'action' options set to 'change'.

'jump' value

 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'hard_clic' => {
                 'sub'    => 'my_hard_clic_sub',
                 'action' => 'jump',             # a jump can be done
             },
         }
     }
 );
 
 [...]
 
 sub my_hard_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     if ( $info_ref->{'x'} < ( $editor->width / 2 ) ) {
         return $info_ref;                               # no jump, values unchanged
     }
     my %new_info = ( 
         'line' => $editor->first,
         'pos'  => int( 20 * $info_ref->{'y'} / $editor->height ),
     );
                                                         # jump to 'clic' label
     return [ 'clic', \%new_info ];                      # providing the hash required
 }

The difficult point is that, at a precise label, default management or other events expect to find a precise $info_ref hash with precise keys in order to work. So if you want to make a jump to a particular label, you have to provide yourself this $info_ref hash.

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

  • returning undef will exit the event management : 'dynamic exit' remains possible with a jump

  • returning a hash reference will just make a 'change' action with no jump. The hash given will be taken as the new <$info_ref> hash (for default management...) : a 'jump' action can encapsulate a 'change' action

  • returning an array reference will make a jump : the first position of the array is the label where to jump, the second one is the new $info_ref hash needed at that label.

If the 'jump' action includes the 'change' action, why should we keep the 'change' action ? In fact, at first, I didn't think of the 'action' option. I just wanted to analyse the return value of the specific sub. But it's not very clear. Complex mecanism are hidden in specific code. With the 'action' option, you have a warning reading the code of the instance creation and this warning can be saved as dynamic configuration and inquired later. Keeping 'jump' with 'change' gives this warning different levels : I think it's a little better.

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 (here the information hash should contain the same keys as those you have received). In this case, what you have jumped are the possible other subs that were linked to the same label event as yours, these subs should have been done after your own one (if no jump has been made).

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 :

  • 'warning' : if the thread does not exist yet, the thread is created but a display is made on STDERR

  • 'unlink' : if the thread does not exist yet, the thread is not created, the event is not linked to your sub but the 'editor' instance is still created

  • 'error' (or any value different from 'warning' and 'unlink') : if the thread does not exist, the thread is not created, the 'editor' instance is not created

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 :

  • the 'Graphic' thread is pending, waiting for your thread response

  • your thread is working hard, trying to slow down the least it can the 'Graphic' thread

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 :

  • a warning message, including the stack call, will be printed on STDERR

  • the executing thread will receive undef as an anwser : of course nothing will have been done by the initial calling server thread.

...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 :

  • Is there any task for me in the queue ? If true, then I execute it. These queued tasks do not include other user events coming from the window manager which will still remain pending.

  • Is the asynchronous call that I have asked for ended ? Which means : is your event sub ended ? If true, I exit the loop, get the answer and go on with the event management.

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 !).

'MULTI-SUB' DECLARATION

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.

DYNAMIC CONTRIBUTION, part 1, updating events

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 :

  • A single event management of a single instance (instance call)

  • All events of a single instance (instance call)

  • A single event of a all instances (class call)

  • All events of all instances (class call)

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

  • Adding an new event (no management before)

  • Changing an old event

  • Deleting an old event

Single event versus all events

There are 2 distinct methods :

  • set_event will change only one event

  • set_events will change all the events, note that the last "s" makes all the difference.

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 :

  • The first is the name of the event to be changed

  • The second contains the information that should have been given during the instance creation : the interface is the same (you can create threads, eval a new module, ...).

    This second parameter can then be a hash reference (for a single action linked to this event) or an array reference (for multiple actions).

    If there is no second parameter (or an undef value), the event will be deleted.

  • A third optional parameter can add conditions to define if the event should be changed or not. These conditions should be used with class calls.

set_events method

    $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.

Single instance versus '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' and 'set_events' 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' method. This last parameter is an optional hash with the following keys :

  • 'instances'

  • 'values'

  • 'names'

'instances' key

Possible values are :

  • 'existing', will affect only existing instances but not the ones to come.

  • 'future', will affect only instances to come but not existing ones.

  • 'all', will affect all instances, this is the default option.

'values' option

Possible values are :

  • 'undefined', will affect only undefined event(s) : won't override existing management

  • 'defined', will affect only defined event(s) : replace existing management, but don't add management where there wasn't any.

  • 'all', no matter if event(s) were defined before : this is the default option

With a 'set_event' call (that is, when you define a particular named event, for example, 'clic'), 'undefined' value means that, for each instance, a test will be done : if a 'clic' event already exists for the tested instance, it won't be updated.

With a 'set_events' call (when you want to set all events at once), 'undefined' value means that there is not a single event managed in a specific way. You don't have used 'events' option during creation, or you have deleted all events afterwards.

'names' option

This option accepts a 'Regexp' object that you can obtain with the qr// syntax.

If the name of an 'Editor instance' matches the regexp, the change will apply.

'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 optional parameters and new instances

If you have made several class calls with 'set_event' and / or 'set_events' 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 (to 'set_event' and / or 'set_events' methods) 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 is not provided.

DYNAMIC CONTRIBUTION, part 2, dynamic designing

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.

'code' option

This option will be used to replace 'sub', 'use' and 'package' options in standard and static event definition.

This option will accept a string that represents the code of the event.

DYNAMIC CONTRIBUTION, part 3, saving

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 will help you save everything of your instances in order to get the 'same' state (at least, we'll try) that you've had before quitting.

EVENT LIST

At present, only the 'clic' subset is integrated in the new event management.

'clic' subset

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

  • hard_clic

  • clic

  • after_clic

and the 9 possible prefixes :

  • no prefix ('' = empty string = q{})

  • any_

  • alt_

  • ctrl_

  • shift_

  • alt_ctrl_

  • alt_shift_

  • ctrl_shift_

  • alt_ctrl_shift_ (applications designed for pianists only !)

The suffix is associated to a precise moment in the clic sequence, the prefix to the modifier keys pressed during the clic. Here are 3 examples of event label among the 27 possible :

  • any_hard_clic

  • shift_clic

  • alt_ctrl_after_clic

A true mouse clic event generates 6 events as explained in "EVENT NAMES AND LABELS". In fact, if we include the default actions, there are 9 steps in a complete clic sequence :

  • 'any_hard_clic' event sub(s)

  • '${meta}hard_clic' event sub(s)

  • 'hard_clic' default management (if no modifier key pressed or if label is 'hard_clic') : drag initiation for resize management

  • '${meta}hard_clic' default transform : transform 'x' and 'y' coordinates to 'line' and 'pos'

  • 'any_clic' event sub(s)

  • '${meta}clic' event sub(s)

  • 'clic' default management (if no modifier key pressed or if label is 'clic') : set cursor position

  • 'any_after_clic' event sub(s)

  • '${meta}after_clic' event sub(s)

These 9 steps should be understood if you plan to use a 'jump' action. For instance, if you are in a "${meta}hard_clic" event sub, the smallest jump you could do would be to give the label 'hard_clic' (you will have the default 'hard_clic' management executed even if a modifier key is pressed). But you could end your sub giving the same label of your event, "${meta}hard_clic" : in this case, the default 'hard_clic' management will be done as usual (that is, only if no modifier key was pressed), then the default ${meta}hard_clic transformation will be done, followed by 'any_clic' events and so on...

Parameters received by your specific sub and needed at specific labels depends only on the suffix. So only suffixes are described.

'hard_clic' suffix

First couple of events generated in the 'clic' sequence. There is a default management (when no modifier key is pressed) which initiates a drag sequence if the cursor shape is a resize arrow (in this particular case, nothing is done afterwards : no 'clic' and 'after_clic' management).

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

  • 'x', abscisse of the clic

  • 'y', ordinate of the clic

  • 'meta_hash', hash containing the keys 'alt', 'ctrl' and 'shift' : values are true if the corresponding modifier key was pressed.

  • 'meta', string containing the modifier key combination ('', 'shift_', 'alt_ctrl_', ...)

'meta_hash' and 'meta' are interesting only for 'any_hard_clic' event :

 my $editor = Text::Editor::Easy->new( 
    {
        'file'   => 'my_file.t3d',
        'events' => {
            'any_hard_clic' => {              # hard_clic for any modifier key combination
                'sub'    => 'my_hard_clic_sub',
            },                         
        }                                     # end of events declaration
    }
 );
 
 [...]
 
 sub my_hard_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' suffix

Second couple of events generated in the 'clic' sequence. There is a default management (when no modifier key is pressed) which makes the following actions :

  • sets the cursor to the position pointed by the mouse clic

  • deselects any previously selected area

  • set focus to the editor which has received the clic event

  • a little visual adjustment is also possible if the new cursor position is on the first or last displayed line

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

  • 'line', the line instance where the clic was done

  • 'pos', the position in that line

  • 'meta_hash', hash containing the keys 'alt', 'ctrl' and 'shift' : values are true if the corresponding modifier key was pressed : interesting for 'any_clic' event

  • 'meta', string containing the modifier key combination ('', 'shift_', 'alt_ctrl_', ...) : interesting for 'any_clic' event

'after_clic' suffix

Third and last couple of events generated in the 'clic' sequence. There is no default management. The information received by your sub is a hash containing the following keys (the same keys as the 'clic' event) :

  • 'line', the line instance where the clic was done

  • 'pos', the position in that line

  • 'meta_hash', hash containing the keys 'alt', 'ctrl' and 'shift' : values are true if the corresponding modifier key was pressed (for 'any_after_clic')

  • 'meta', string containing the modifier key combination ('', 'shift_', 'alt_ctrl_', ...), for 'any_after_clic'

'motion' subset

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 :

  • top_editor_change : happens each time a new 'editor' instance is on top of the zone.

  • editor_destroy : happens each time a 'editor' instance belonging to the zone is destroyed.

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 :

  • first, you use synchronous events executed by the 'Graphic' thread

  • second, your application is growing (and your event code too), becoming more interesting and ... slower

  • Then you add threads where you feel it would help, keeping your code and your declarations but adding a few 'thread' options and and a few return if anything_for_me; instructions.

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.