Greg Ercolano wrote:
>>> - Greg's tweak to handle file:// prefixes also segfaults.
>>      [..] seems odd is that extra slashes would be any kind of cause
>>      of a segfault.
> 
>     I just ran some OSX tests.
>     Looks like a few things are at issue under OSX:

OK, here's a version of the example that works on OSX and Linux.

On OSX, tested OK with:

        Safari: dragged urls viewing local jpgs eg. file:///var/tmp/foo.jpg
        Finder: dragged images from the finder browser and viewer
        Desktop: dragged images from desktop
        Firefox: dragged urls viewing local jpgs from browse or viewing mode

On Linux, tested OK with:

        Konqueror: browser and image viewer
        Firefox: browser and image viewer
        eog (eye of gnome): browser and image viewer

I did not test with windows.. will try that a bit later. I hate turning
on my windows box. It's always.. aggravating.

To handle the random behavior on OSX due to the timer, I added a few variables
to the class so that it makes a copy of the paste buffer and length so that when
the timer goes off, the dnd_open() function is working with a local copy of the
paste buffer.

Definitely NO PROBLEMS with multiple slashes in pathnames on OSX or linux.
Where they /could/ cause problems is under windows, where they might be
interpreted as a UNC path unexpectedly. Will need to do some tests on
windows to see if that's an issue.

I think crashing was entirely due to NULL strings being sent;
put in a trap for that, and also that issue probably won't happen
now that local copies are made of the paste buffer before timer.

I'm pasting the whole thing, because it involved more than a few
changes. Comments welcome. (Sorry Alvin this has turned into such
a large thread. No good deed goes unpunished ;)

Watch out for word wrap. I have word wrap turned off on my end,
but if your newsreader auto wraps, you can probably prevent wrapping
by just resizing the window really wide.

I left DEBUG stubs in for testing.

* * *


// A trival example illustration how to load an image
// using Drag and Drop events.
//
// * Compile using: flt-config --use-images --compile dndtest0.cxx
//
// * Drag an image from your favourite file browser onto the
//         marked box.

#include <string.h>             // memcpy
////DEBUG #include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Shared_Image.H> // Image I/O
#include <FL/fl_ask.H>          // fl_alert, fl_choice
#include <FL/filename.H>        // fl_filename_name
#include <string.h>             // strncmp, strlen, etc.

class Fl_DND_Box : public Fl_Box
{
public:
    // Local copy of paste buffer
    char *paste_buf;
    int paste_len;

    Fl_DND_Box(int X, int Y, int W, int H, const char *L = 0) : 
Fl_Box(X,Y,W,H,L), evt(FL_NO_EVENT)
    {
        labeltype(FL_NO_LABEL);
        box(FL_NO_BOX);
        clear_visible_focus();
        paste_len = 0;
        paste_buf = 0;
    }

    virtual ~Fl_DND_Box() {}

    int event()
        { return evt; }

    static void timed_callback(void *v)
    {
        Fl_DND_Box *w = (Fl_DND_Box*)v;
        w->do_callback();
        Fl::remove_timeout(Fl_DND_Box::timed_callback, v);
    }

    int handle(int e)
    {
        evt = e;

        switch(e)
        {
            case FL_DND_ENTER:
            case FL_DND_RELEASE:
            case FL_DND_LEAVE:
            case FL_DND_DRAG:
                return 1;

            case FL_PASTE:
                // Make a local copy of paste buffer
                if ( paste_buf ) { delete [] paste_buf; paste_buf = 0; 
paste_len = 0; }
                if ( Fl::event_text() && Fl::event_length() > 0 )
                {
                    // Local copy of dnd buffer
                    //    We fire off handling of drop with timer, so to prevent
                    //    Fl::event_text() from becoming invalid, we make a 
copy here.
                    //    We double-null terminate the string in case it 
contains
                    //    wide chars (firefox/linux). On OSX, experiments show 
that
                    //    Fl::event_text() sometimes isn't null terminated.
                    //
                    ////DEBUG fprintf(stderr, "*** RAW PASTE BUF='%s' 
LEN=%d\n", Fl::event_text(), Fl::event_length());
                    paste_len = Fl::event_length();
                    paste_buf = new char [paste_len + 2];       // +2 needed 
for double-null
                    memcpy(paste_buf, Fl::event_text(), Fl::event_length());
                    paste_buf[paste_len+0] = 0;
                    paste_buf[paste_len+1] = 0;
                }
                // If there is a callback registered, call it.
                // The callback must access Fl::event_text() to
                // get the string or file path that was dropped.
                // Note that do_callback() is not called directly.
                // Instead it will be executed by the FLTK main-loop
                // once we have finished handling the DND event.
                // This allows caller to popup a window or change widget focus.
                if (callback() && ((when() & FL_WHEN_RELEASE) || (when() & 
FL_WHEN_CHANGED)))
                    Fl::add_timeout(0.0, Fl_DND_Box::timed_callback, 
(void*)this);
                return 1;
        }
        return Fl_Box::handle(e);
    }

protected:
    // The event which caused Fl_DND_Box to execute its callback
    int evt;
};

// Widget that displays the image
Fl_Box *box = (Fl_Box*)0;

void file_close()
{
    Fl_Shared_Image *img = (Fl_Shared_Image*)box->image();

    if (!img)
        return; // no image displayed

    box->image(0);

    // The image is shared, release until no more references
    while(img->refcount())
        img->release();
}

void file_open(const char *fpath)
{
    file_close();

    Fl_Shared_Image *img = Fl_Shared_Image::get(fpath);

    if (!img)
    {
        fl_alert("Failed to load image: %s", fpath);
        return;
    }

    box->size(img->w(), img->h());
    box->image(img);

    Fl::redraw();
}

// Assume pairs of bytes: [char] [nul] [char] [nul] .. [nul] [nul]
void multibyte_convert(const char *in, char *out)
{
     for ( *out = *in; *in; )
        { out++; in += 2; *out = *in; }
}

// Parse the 'dropped' string passed to us
// Notes:
//      linux/firefox: firefox sends multibyte strings (every other byte nul)
//    linux/konqueror: sends crlf termination for each file, even if single 
file dropped
//         osx/finder: no crlfs if single file dropped
//         osx/safari: no crlfs if url dropped
//            windows: sends crlf termination for each file, even if single 
file dropped
//
void dnd_open(const char *urls, size_t length)
{
    ////DEBUG fprintf(stderr, "DND OPEN: URLS='%s' LEN=%d\n", urls, length);

    // Check for zero (OSX)
    if ( urls == 0 || length == 0 ) return;

    // Make local copy of path that we can mess with
    char *fpath = new char [length+1];

    // Check for multibyte chars from Firefox/linux.
    //    Length will be >1 and second byte will be zero.
    //
    if ( strlen(urls) != length && length > 1 && urls[1] == 0 )
        { multibyte_convert(urls, fpath); }
    else
        { strcpy(fpath, urls); }

    // Count number of files, truncate at first CRLF
    int cnt = 0;
    for ( char *c = fpath; *c; c++ )
    {
        if ( *c == '\n' ) { *c = 0; cnt++; }
        if ( *c == '\r' ) { *c = 0; }
    }

    if (cnt > 1)
    {
        fl_alert("Invalid number of files being dropped: %d.\nDrop 1 file 
only.", cnt);
        delete [] fpath;
        return;
    }

    // Handle file: prefix
    if ( strncmp(fpath, "file:", 5) == 0 )
        { strcpy(fpath, fpath+5); }

    // NOTE: fpath may contain URL escape codes which we
    // really should decode. For this example, they are ignored.
    //
    if (fl_choice("Do you really want to open image: %s?", "&No", "&Yes", 0, 
fl_filename_name(fpath)) == 1)
        { file_open(fpath); }

    delete [] fpath;
}

void dnd_cb(Fl_Widget *o, void *v)
{
    Fl_DND_Box *dnd = (Fl_DND_Box*)o;
    if (dnd->event() == FL_PASTE)
        dnd_open(dnd->paste_buf, dnd->paste_len);
}

int main(int argc, char *argv[])
{
    Fl::visual(FL_DOUBLE | FL_INDEX);
    Fl::get_system_colors();
    fl_register_images();
    Fl::scheme("gtk+");

    Fl_Double_Window *wnd = new Fl_Double_Window(10, 10, 500, 400, "DND 
Example");
    {
        {
            Fl_Box *o = new Fl_Box(15, 15, 470, 45, "Drag an image from your 
favourite file browser onto the area below.");
            o->box(FL_ROUNDED_BOX);
            o->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP| FL_ALIGN_CENTER);
            o->color((Fl_Color)215);
            o->labelfont(FL_HELVETICA_BOLD);
        }

        { // Fl_Group is necessary so that the Fl_DND_Box will resize along 
with Fl_Scroll
            Fl_Group *o = new Fl_Group(15, 70, 470, 320, 0);
            o->box(FL_NO_BOX);
            {
                {
                    Fl_Scroll *o = new Fl_Scroll(15, 70, 470, 320, 0);
                    o->box(FL_ENGRAVED_BOX);
                        box = new Fl_Box(17, 72, 466, 316, 0);
                        box->box(FL_FLAT_BOX);
                        box->align(FL_ALIGN_INSIDE | FL_ALIGN_CENTER);
                    o->end();
                }

                { // Fl_DND_Box is constructed with the same dimensions and at 
the same position as Fl_Scroll
                    Fl_DND_Box *o = new Fl_DND_Box(17, 72, 466, 316, 0);
                    o->callback(dnd_cb, (void*)o);
                }
            }
            o->end();
            Fl_Group::current()->resizable(o);
        }
    }
    wnd->end();
    wnd->show(argc, argv);

    return Fl::run();
}
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to