Published 2010-02-17 11:10:00

As part of my, "What to do while looking for a Job" project, I'm exploring the Gtk/Gobject introspection bindings in seed.

It's one of those fun, not documented anywhere, and you have to dig around all the source to understand how to do things projects. And part of a grander plan to develop a generic application builder loosely based around the RooJs builder application I blogged about previously.

The idea is that using Gtk, GtkSourceView, and the concepts I developed in the RooJS Builder, I would be able to create Dynamic Web applications (and eventually Gtk ones) very rapidly due to the ability to closely tie the action code to the interface design.

The core reasons why it speeds up development are:
- Syntax checking is feasible when creating code.
- Adding, editing and selecting properties and event handlers is faster, less error prone, and access to documentation is instant. 
- Visually changing and seeing the effect of a change (the change - reload - test cycle) is reduced to milliseconds rather than tens of seconds.

So far most of the application is mocked up, (and relivant patches have been submitted to seeds' bugzilla for review). However one of the key components I was looking at over the Chinese New Year Break was drag and drop of Palete Items onto the Rendered View of the application (Webkit embedded), (and eventually the tree that makes it up). To do this involved seriously testing the Gobject introspection bindings and how the interact with the drag and drop methods in Gtk.

So Read on if you are interested in how it all comes together.

The starting point for all this is the Gnome tutorial on drag and drop in C  http://live.gnome.org/GnomeLove/DragNDropTutorial, this gives you an idea of how it's done natively, however as you will see, not all of this is possible or relivant to how Seed/JS does it.


1. Setting up the drag/drop potential


To enable drag and drop to work, you basically have to mark both the SOURCE and TARGET widgets telling Gtk, that they are 'DnD' enabled.
   // some globally accessable constant.
    Builder.atoms = {
       "STRING" : Gdk.atom_intern("STRING")
    }
SOURCE: 
  
  
    Gtk.drag_source_set (
        this.el,            /* widget will be drag-able */
        Gdk.ModifierType.BUTTON1_MASK,       /* modifier that will start a drag */
        null,            /* lists of target to support */
        0,              /* size of list */
        Gdk.DragAction.COPY         /* what to do with data after dropped */
     );
     targets = new Gtk.TargetList();
     targets.add( Builder.atoms["STRING"], 0, 0);
     Gtk.drag_source_set_target_list(this.el, targets);

     Gtk.drag_source_add_text_targets(this.el); 

This is the code for setting up the source, note that we send null as the list of target's initially to the first call, this is due to the in-ability of Seed to create a list of targets (as unlike traditional bindings we are slightly restricted about how we can interface with C types.)

The second part of this is to generate a TargetEntry item using the TargetList API, - I've not actually been able to make this work correctly, however, it does enable the callbacks later to function.

The last part is optional, add_text_targets, which enables you to later use set_text(), on the widget, so that you can drop text into other applications (or the GtkSourceView) 

TARGET:

      
        Gtk.drag_dest_set
        (
                this.el,              /* widget that will accept a drop */
                Gtk.DestDefaults.MOTION  | Gtk.DestDefaults.HIGHLIGHT,
                null,            /* lists of target to support */
                0,              /* size of list */
                Gdk.DragAction.COPY         /* what to do with data after dropped */
        );
        targets = new Gtk.TargetList();
        targets.add( Builder.atoms["STRING"], 0, 0);
        Gtk.drag_dest_set_target_list(this.el, targets);
        Gtk.drag_dest_add_text_targets(this.el);


Again, this does pretty much what the source does in terms of creating target lists etc. 

2. hooking up all the signals..

SOURCE: 

 'drag-begin' : function (w, ctx, ud) 
                                    {
            // we could fill this in now...
            Seed.print('SOURCE: drag-begin');
             
            // find what is selected in our tree...
            var iter = new Gtk.TreeIter();
            var s = this.selection;
            s.get_selected(_model, iter);

            // set some properties of the tree for use by the dropped element.
            var value = new GObject.Value('');
            _model.el.get_value(iter, 0, value);
            this.el.dragData = value.value;
            this.el.dropList = _model.provider.getDropList(value.value);


            // make the drag icon a picture of the node that was selected
            var path = _model.el.get_path(iter);
            var pix = this.el.create_row_drag_icon ( path);
            Gtk.drag_set_icon_pixmap (ctx,
                pix.get_colormap(),
                pix,
                null,
                -10,
                -10);
            
            return true;
        },
       'drag-end' : function () 
        {
            Seed.print('SOURCE: drag-end');
            this.el.dragData = false;
            this.el.dropList = false;
            return true;
        },


Note, The syntax here is based on some wrapper code that maps listeners and adds the automatically to the object.signal[SIGNAL].connect(...) call.

In this example, the drag begin, adds some extra properties to the GtkTreeView Javascript wrapper, this is mainly due to the fact I could not get the GtkDataSelection code to pass data correctly. so 'dragData' and 'dropList' are data that are usefull to the target.

The later part also makes a copy of the tree node row, so it can be dragged around. (found that code in the GtkTreeView source)

The second handler, just clears the data after drag has ended (either successfully or not)

3. add a hook to return some data when a drop asks for it.


SOURCE

 
        'drag-data-get' : function (w, ctx, selection_data, target_type,  time, ud) 
        {
            
            Seed.print('Palete: drag-data-get: ' + target_type);
            selection_data.set_text(this.dragData ,this.dragData.length);
            
            return true;
        },

This method will get caled when a drop occurs, (see a bit later), I could not do much with the selection_data, and my user defined GtkTargetEntry, However, using the set_data call I could make it drop some text into widgets that have been designed to handle textual drop data (eg. an editor, or even you desktop)

4. handle dragging over on the target


TARGET:

    'drag-motion' : function (w, ctx,  x,   y,   time, ud) 
    {
        
 
        var src = Gtk.drag_get_source_widget(ctx);

        // a drag from  elsewhere...- prevent drop..
        if (!src.dragData) {
            Gdk.drag_status(ctx, 0, time);
            return true;
        }
        
        Gdk.drag_status(ctx, Gdk.DragAction.COPY,time);
         
        return true;
    },

In this example, we can find out the source widget, and test if it has the dragData property, my real code does a few more check to determine whether the selected item could actually be droped, based on the generated dropList. It then either changes the icon to allow or deny dropping using the drag_status call (setting to 0 to block drops), or Gdk.DragAction.COPY to allow.


5. handle the drop action

'drag-drop'  : function (w, ctx, x, y,time, ud) 
    {
                
        Seed.print("TARGET: drag-drop");
       
        Gtk.drag_get_data
        (
                w,         /* will receive 'drag-data-received' signal */
                ctx,        /* represents the current state of the DnD */
                Builder.atoms["STRING"],    /* the target type we want */
                time            /* time stamp */
        );
        
        var source = Gtk.drag_get_source_widget(ctx);
        Seed.print("Browser: source.DRAGDATA? " + source.dragData);
                
        /* No target offered by source => error */
       

        return  true;
        

    }
   'drag-data-received' : function (w, ctx,  x,  y, sel_data,  target_type,  time, ud) 
        {
            Seed.print("Browser: drag-data-received");
            delete_selection_data = false;
            dnd_success = false;
            /* Deal with what we are given from source */
            if( sel_data && sel_data.length ) {
                
                if (ctx.action == Gdk.DragAction.ASK)  {
                    /* Ask the user to move or copy, then set the ctx action. */
                }

                if (ctx.action == Gdk.DragAction.MOVE) {
                    delete_selection_data = true;
                }
                var source = Gtk.drag_get_source_widget(ctx);
                // we can send stuff to souce here...

                dnd_success = true;

            }

            if (dnd_success == false)
            {
                    Seed.print ("DnD data transfer failed!\n");
            }

            Gtk.drag_finish (ctx, dnd_success, delete_selection_data, time);
            return true;
        },

drag-drop is the first event that occurs when you drop the element, The call drag_get_data, triggers the signal 'drag-data-get' on the SOURCE element (as detailed above), which in turn triggers the drag-data-received signal on the TARGET.

In our example above drag-data-received, does very little, other than call drag_finish(). with details about success or failure.

Finally after all that the SOURCE: drag-end signal is triggered.

And there you go.. - you can handle drag and drop. Obviously there is more to do on this, as I need to handle tree node dropping, rather than using the built in tree drag support. (which is rather difficult to extend in Seed.)



Mentioned By:
www.abcphp.com : abcphp.com - Drag and Drop with seed (Javascript) and Gtk (532 referals)
phpcamp.net : PHPCamp - Toolbar-Drag and Drop with seed (Javascript) and Gtk (68 referals)
www.planet-php.net : Planet PHP (29 referals)
google.com : seed javascript (17 referals)
google.com : javascript gtk (12 referals)
mail.gnome.org : libseed-list Drag Drop tutorial (10 referals)
google.com : "$(this).data("draggable") is null" (9 referals)
www.phpeye.com : Drag and Drop with seed (Javascript) and Gtk - Alan Knowles - PHP教程|PHP5|PEAR|框架 - Powered by HappyCMS (7 referals)
google.com : drag and drop javascript (7 referals)
google.com : gtk javascript (6 referals)
google.com : "a(this).data("draggable") is null" (5 referals)
google.com : drag drop javascript (4 referals)
google.com : gtk seed javascript (4 referals)
google.com : javascript dragging (4 referals)
google.com : javascript print drag and drop (4 referals)
google.com : php drag and drop (4 referals)
phpcamp.net : Drag and Drop with seed (Javascript) and Gtk | PHPCamp (3 referals)
google.com : drag (3 referals)
google.com : gtk drag and drop example (3 referals)
google.com : gtk drag drop example (3 referals)

Comments

iter api
the examples use the iter api which has changed slightly in gtk3
#0 - Alan Knowles ( Link) on 2011-11-22 07:32:18 Delete Comment

Add Your Comment