Merge lp:~and471/inkscape/ocal-dialog-improvements into lp:~inkscape.dev/inkscape/trunk

Proposed by jazzynico
Status: Merged
Merged at revision: 11027
Proposed branch: lp:~and471/inkscape/ocal-dialog-improvements
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 2095 lines (+1171/-481)
9 files modified
share/icons/Makefile.am (+2/-1)
src/file.cpp (+28/-45)
src/file.h (+6/-0)
src/io/sys.cpp (+9/-0)
src/io/sys.h (+2/-0)
src/menus-skeleton.h (+0/-2)
src/ui/dialog/ocaldialogs.cpp (+892/-350)
src/ui/dialog/ocaldialogs.h (+229/-81)
src/verbs.cpp (+3/-2)
To merge this branch: bzr merge lp:~and471/inkscape/ocal-dialog-improvements
Reviewer Review Type Date Requested Status
Alex Valavanis Approve
Andrew (community) Needs Resubmitting
jazzynico (community) Needs Fixing
Review via email: mp+56234@code.launchpad.net
To post a comment you must log in.
Revision history for this message
jazzynico (jazzynico) wrote :
Download full text (3.3 KiB)

Patched and compiled with no problem on Ubuntu 10.10 with the current trunk (10145).
Seems to work very well. Nice dialog design, image search is rather fast and download time very short compared to the older (current...) implementation. I really like it!

But a crasher, when selecting an image in the result list while another one is still downloading. Backtrace:

-----
Program received signal SIGSEGV, Segmentation fault.
0x007b6545 in Gio::Cancellable::is_cancelled() const () from /usr/lib/libgiomm-2.4.so.1
(gdb) bt
#0 0x007b6545 in Gio::Cancellable::is_cancelled() const () from /usr/lib/libgiomm-2.4.so.1
#1 0x08639326 in Inkscape::UI::Dialog::OCAL::ImportDialog::on_thumbnail_downloaded (this=0xa9edc10, path=..., success=false)
    at ui/dialog/ocaldialogs.cpp:822
#2 0x08639411 in Inkscape::UI::Dialog::OCAL::ImportDialog::on_resource_downloaded (this=0xa9edc10, result=..., file_remote=..., path=...,
    resource=Inkscape::UI::Dialog::OCAL::TYPE_THUMBNAIL) at ui/dialog/ocaldialogs.cpp:788
#3 0x08640a4d in operator() (rep=0xaa097c0, a_1=...) at /usr/include/sigc++-2.0/sigc++/functors/mem_fun.h:2055
#4 operator()<Glib::RefPtr<Gio::AsyncResult>&, Glib::RefPtr<Gio::File>&, Glib::ustring&, Inkscape::UI::Dialog::OCAL::ResourceType&> (rep=0xaa097c0,
    a_1=...) at /usr/include/sigc++-2.0/sigc++/adaptors/adaptor_trait.h:144
#5 operator()<Glib::RefPtr<Gio::AsyncResult>&> (rep=0xaa097c0, a_1=...) at /usr/include/sigc++-2.0/sigc++/adaptors/bind.h:1523
#6 sigc::internal::slot_call1<sigc::bind_functor<-0x00000000000000001, sigc::bound_mem_functor4<void, Inkscape::UI::Dialog::OCAL::ImportDialog, Glib::RefPtr<Gio::AsyncResult> const&, Glib::RefPtr<Gio::File>, Glib::ustring, Inkscape::UI::Dialog::OCAL::ResourceType>, Glib::RefPtr<Gio::File>, Glib::ustring, Inkscape::UI::Dialog::OCAL::ResourceType, sigc::nil, sigc::nil, sigc::nil, sigc::nil>, void, Glib::RefPtr<Gio::AsyncResult>&>::call_it (rep=0xaa097c0, a_1=...)
    at /usr/include/sigc++-2.0/sigc++/functors/slot.h:137
#7 0x007ffa01 in Gio::SignalProxy_async_callback(_GObject*, _GAsyncResult*, void*) () from /usr/lib/libgiomm-2.4.so.1
#8 0x00d1857f in g_simple_async_result_complete (simple=0xaa3bc18) at /build/buildd/glib2.0-2.26.1/gio/gsimpleasyncresult.c:692
#9 0x00d185ed in complete_in_idle_cb (data=0xaa3bc18) at /build/buildd/glib2.0-2.26.1/gio/gsimpleasyncresult.c:702
#10 0x01d3e841 in g_idle_dispatch (source=0xa9dcf68, callback=0, user_data=0xaa3bc18) at /build/buildd/glib2.0-2.26.1/glib/gmain.c:4254
#11 0x01d40855 in g_main_dispatch (context=0x8c7e7e8) at /build/buildd/glib2.0-2.26.1/glib/gmain.c:2149
#12 g_main_context_dispatch (context=0x8c7e7e8) at /build/buildd/glib2.0-2.26.1/glib/gmain.c:2702
#13 0x01d44668 in g_main_context_iterate (context=0x8c7e7e8, block=<value optimised out>, dispatch=1, self=0x8c2c9b8)
    at /build/buildd/glib2.0-2.26.1/glib/gmain.c:2780
#14 0x01d44ba7 in g_main_loop_run (loop=0x994a330) at /build/buildd/glib2.0-2.26.1/glib/gmain.c:2988
#15 0x00a051d9 in IA__gtk_main () at /build/buildd/gtk+2.0-2.22.0/gtk/gtkmain.c:1237
#16 0x005593b7 in Gtk::Main::run_impl() () from /usr/lib/libgtkmm-2.4.so.1
#17 0x005591b2 in Gtk::Main::run() () from /usr/lib/libgtkmm-2.4.s...

Read more...

review: Needs Fixing
Revision history for this message
su_v (suv-lp) wrote :

Testing the branch on OS X 10.5.8 (i386), I get this error when trying to search:

(inkscape:54403): glibmm-CRITICAL **:
unhandled exception (type Glib::Error) in signal handler:
domain: g-io-error-quark
code : 15
what : Operation not supported

Dependencies:
  glib2 @2.28.4_0 (active)
  glibmm @2.24.2_0 (active)
  gtk2 @2.24.3_0+x11 (active)
  gtkmm @2.22.0_1 (active)

Revision history for this message
su_v (suv-lp) wrote :

Clarification: on OS X 10.5.8, the dialog is not functional (no search). Do I need to install additional dependencies, or are there other configure options needed for either Inkscape or the external libraries?

Revision history for this message
Andrew (and471) wrote :

> Clarification: on OS X 10.5.8, the dialog is not functional (no search). Do I
> need to install additional dependencies, or are there other configure options
> needed for either Inkscape or the external libraries?

I am not entirely sure. The new dialog uses GIO for its file handling stuff, that is what you see in the message, and the only operations it uses are copy and load_contents.

Revision history for this message
Andrew (and471) wrote :

Possibly you need GVFS (for remote file stuff)? That is the only other thing I can think of

Revision history for this message
jazzynico (jazzynico) wrote :

On Windows XP, I get some make errors:

-----
src/ui/dialog/ocaldialogs.h:82: warning: unused parameter 'parent'
src/ui/dialog/ocaldialogs.h:292: error: 'uint' does not name a type
src/ui/dialog/ocaldialogs.h:334: error: ISO C++ forbids declaration of 'Spinner' with no type
src/ui/dialog/ocaldialogs.h:334: error: invalid use of '::'
src/ui/dialog/ocaldialogs.h:334: error: expected ';' before '*' token
-----

Note that Gtk::Spinner is not available in the current Windows devlibs (gtk 2.18...).

review: Needs Fixing
Revision history for this message
jazzynico (jazzynico) wrote :

With the spinner related code commented out, uint (ocaldialogs.h:292) replaced with unsigned int and the OCAL import menu entry uncommented in menus-skeleton.h, it compiles correctly on Windows XP. More tests later...

Revision history for this message
su_v (suv-lp) wrote :

> Possibly you need GVFS (for remote file stuff)?
> That is the only other thing I can think of

I did not uninstall gvfs (1.6.6) as I need it for the regular builds of lp:inkscape (and it does get linked to inkscape if present).

Could it be related to having gconf configured/installed with '--disable-defaults-service' (i.e. no policykit installed)? This is specific to my local setup/build environment - otherwise I can't install the required dependencies (everything that depends on gconf in MacPorts) without having made system-wide changes as root (for policykit) (which I avoid, as long as possible, to still have a system comparable to the 'normal' user on Mac OS X).

Possibly using Gio requires additional configuration on Mac OS X (maybe Michael can investigate, once the branch has been merged into trunk), or I definitely have to give up on avoiding to install additional (gnome or glib/gtk2-based) system-wide services as root - I'm wondering though how that will impact packaging as osxapp.

Revision history for this message
su_v (suv-lp) wrote :

Correction (I do have gnome-vfs installed, not gvfs):

- gvfs (1.6.6)
+ gnome-vfs @2.24.4_0

Revision history for this message
jazzynico (jazzynico) wrote :

Good news, it works on Windows XP (conditions mentioned earlier...). Without the spinner, but at least I can search an import images.

Revision history for this message
Andrew (and471) wrote :

On 5 April 2011 11:37, ~suv <email address hidden> wrote:
> Correction (I do have gnome-vfs installed, not gvfs):
>
> - gvfs (1.6.6)
> + gnome-vfs @2.24.4_0
> --
> https://code.launchpad.net/~and471/inkscape/ocal-dialog-improvements/+merge/56234
> You are the owner of lp:~and471/inkscape/ocal-dialog-improvements.
>

Ah, they are not the same though. GNOMEVFS is depreciated and has just
been removed.

--
Andrew

Revision history for this message
Jon A. Cruz (jon-joncruz) wrote :

On Apr 5, 2011, at 7:39 AM, Andrew wrote:

> On 5 April 2011 11:37, ~suv <email address hidden> wrote:
>> Correction (I do have gnome-vfs installed, not gvfs):
>>
>> - gvfs (1.6.6)
>> + gnome-vfs @2.24.4_0
>> --
>> https://code.launchpad.net/~and471/inkscape/ocal-dialog-improvements/+merge/56234
>> You are the owner of lp:~and471/inkscape/ocal-dialog-improvements.
>>
>
> Ah, they are not the same though. GNOMEVFS is depreciated and has just
> been removed.

We probably need to note and track that dependency

http://wiki.inkscape.org/wiki/index.php/Tracking_Dependencies#Functions_Used

http://wiki.inkscape.org/wiki/index.php/Tracking_Dependencies#Distros

Revision history for this message
su_v (suv-lp) wrote :

> Possibly you need GVFS (for remote file stuff)?

Are you sure about gvfs as required dependency? It is not part of the Windows devlibs either [1], only libgsf (which I already had installed from the beginning).

From the README of gvfs:

<quote>
gvfs is a userspace virtual filesystem designed to work with the i/o
abstractions of gio (a library availible in glib >= 2.15.1). It
installs several modules that are automatically used by applications
using the APIs of libgio. There is also fuse support that allows
applications not using gio to access the gvfs filesystems.

The gvfs model differs from e.g. gnome-vfs in that filesystems must
be mounted before they are used. There is a central daemon (gvfsd)
that handles coordinting mounts, and then each mount is (typically)
in its own daemon process (although mounts can share daemon process).

</quote>

I did try and installed gvfs (without gnome support, because I don't run gnome), and rebuilt your branch - with no improvement: same error as before.

(with regard to gvfs (gnome-vfs): note that I don't have the daemon running. Also, it seems an odd dependency to me for the ocal dialog because the README mainly talks about mounting a file system before using it... we don't mount the ocal site, do we?)

[1] <http://bazaar.launchpad.net/~inkscape.dev/inkscape-devlibs/trunk/view/head:/readme.txt>

Revision history for this message
Andrew (and471) wrote :

I am not sure about this, can anyone else shed some light on it?

Revision history for this message
Andrew (and471) wrote :

Related to OSX:
  After speaking with su-v on IRC, I was able to find out that the issue was with the load_contents_async method, however something like copy_async still works. Therefore I will continue to look at this.

Revision history for this message
su_v (suv-lp) wrote :

JFTR (since it briefly came up on #inkscape): today glibmm was updated in MacPorts to 2.28.0 (from 2.24.2). I rebuilt the ocal-dialog-improvements branch, but still get the same error.

Revision history for this message
su_v (suv-lp) wrote :

As was reported on #inkscape: 'src/menus-skeleton.h' still checks for gnome-vfs as requirement for the OCAL import dialog (#ifdef WITH_GNOME_VFS). As far as I understand, one of the goals for the refactoring is to remove the dependency on gnome-vfs?

Revision history for this message
Splex (splex) wrote :

Yes, please remove the gnome dependency.

I just tried building inkscape with this diff applied, and get the following errors:

ui/dialog/ocaldialogs.cpp: In member function ‘bool Inkscape::UI::Dialog::OCAL::LoadingBox::_on_expose_event(GdkEventExpose*)’:
ui/dialog/ocaldialogs.cpp:317:49: error: cannot call constructor ‘Glib::ustring::ustring’ directly
ui/dialog/ocaldialogs.cpp:317:49: error: for a function-style cast, remove the redundant ‘::ustring’
ui/dialog/ocaldialogs.cpp: In member function ‘void Inkscape::UI::Dialog::OCAL::LoadingBox::start()’:
ui/dialog/ocaldialogs.cpp:337:47: warning: converting to non-pointer type ‘long int’ from NULL
ui/dialog/ocaldialogs.cpp: In member function ‘bool Inkscape::UI::Dialog::OCAL::BaseBox::_on_expose_event(GdkEventExpose*)’:
ui/dialog/ocaldialogs.cpp:570:49: error: cannot call constructor ‘Glib::ustring::ustring’ directly
ui/dialog/ocaldialogs.cpp:570:49: error: for a function-style cast, remove the redundant ‘::ustring’
make[3]: *** [ui/dialog/ocaldialogs.o] Error 1

10122. By Andrew <email address hidden>

uint > unsigned int (so windows builds work)

10123. By Andrew <email address hidden>

Remove some GNOME_VFS checks
Try to use read_async instead of load_contents_async, not working at the moment :(

10124. By Andrew <email address hidden>

Revert changing to read_async

10125. By Andrew <email address hidden>

Correct ustring syntax

Revision history for this message
Andrew (and471) wrote :

Hi can I get a merge now? Otherwise I fear that this will never get merged.

Revision history for this message
Andrew (and471) :
review: Needs Resubmitting
Revision history for this message
ScislaC (scislac) wrote :

Have you been able to address all issues brought up? Trust me, we want it merged just as bad as you. :)

Revision history for this message
jazzynico (jazzynico) wrote :

Now that the devlibs are updated, the Gtk::Spinner use is no longer a problem on Windows.

But as for now, it compiles correctly on Ubuntu 10.10, inkscape trunk revision 10371 (except that I had to download the OCAL.png manually). And I still have the crash reported in the very first comment, when selecting an image in the result list while the preview of another one is still loading.

review: Needs Fixing
Revision history for this message
su_v (suv-lp) wrote :

I did update the checkout of the ocal branch (r10125) and rebuilt it with the same configure and compile options as I use for current trunk (and 0.48.x): at least in my local builds of the Mac OS X port of Inkscape, the new ocal dialog is a major regression i.e. it completely fails, whereas the current version (in stable and trunk) allows to search for and import images from openclipart.org. [1]

I will not vote against the merge (Inkscape users on Mac OS X are still a far smaller number than Windows & Linux), but it might be appreciated if - for the next major release - e.g. the old version could be maintained ifdef'd for darwin/osx (unless of course the migration to gtk+ 3 as well as supporting the upcoming release of Lion will prohibit maintaining parts of the old code base or offer an easier fix/port of GIO-based implementations - upstream or in Inkscape.)

[1] tested on Mac OS X 10.5.8 Leopard (i386)

10126. By Andrew <email address hidden>

Fixed issues with cancelling downloading of thumbnails and images, all should be well now :)

Revision history for this message
Andrew (and471) wrote :

Okay I have fixed the problem JazzyNico described. All should be ready to merge now.

review: Needs Resubmitting
Revision history for this message
Andrew (and471) wrote :

Just one issue is there, that the Ocal.png needs to be put into the Makefile so it is installed, however I am not experienced with Make so could someone do this when they merge?

Revision history for this message
jazzynico (jazzynico) wrote :

It would be more consistent to add the icon in the general share/icons/icons.svg file as we do with all the other icons. Would you be willing to take a look?

Revision history for this message
ScislaC (scislac) wrote :

If there is any modification to the icons.svg file, please be sure to read this first:

http://wiki.inkscape.org/wiki/index.php/Icons#Themes

Revision history for this message
jazzynico (jazzynico) wrote :

Hum, please forget my previous stupid comment. I just didn't remember the OCAL.png file is not an icon...

Revision history for this message
jazzynico (jazzynico) wrote :

Tested again on Ubuntu 10.10, Inkscape trunk revision 10495.
Crash fix confirmed. Thanks!
Tests on Windows XP planned later...

Ah, just a small diff error with verbs.cpp. Could you please fix it?

Revision history for this message
jazzynico (jazzynico) wrote :

Found another crasher, reproduced on Ubuntu 10.10 and Windows XP.

Steps:
1. Launch OCAL import.
2. Search something (used "skull").
3. Select the first result in the search list.
4. Search something else (used "pirate").

Backtrace:
Program received signal SIGSEGV, Segmentation fault.
0x005d3430 in Gtk::TreePath::get_indices() const () from /usr/lib/libgtkmm-2.4.so.1
(gdb) bt
#0 0x005d3430 in Gtk::TreePath::get_indices() const () from /usr/lib/libgtkmm-2.4.so.1
#1 0x0853b32f in Inkscape::UI::Dialog::OCAL::ImportDialog::on_list_results_selection_changed() ()
#2 0x08547ce7 in sigc::bound_mem_functor0<void, Inkscape::UI::Dialog::OCAL::ImportDialog>::operator()() const ()
#3 0x085472c9 in sigc::adaptor_functor<sigc::bound_mem_functor0<void, Inkscape::UI::Dialog::OCAL::ImportDialog> >::operator()() const ()
#4 0x0854617b in sigc::internal::slot_call0<sigc::bound_mem_functor0<void, Inkscape::UI::Dialog::OCAL::ImportDialog>, void>::call_it(sigc::internal::slot_rep*) ()
#5 0x00c6d19b in Glib::SignalProxyNormal::slot0_void_callback(_GObject*, void*) () from /usr/lib/libglibmm-2.4.so.1
#6 0x019fef2c in g_cclosure_marshal_VOID__VOID (closure=0xaa6ed88, return_value=0x0, n_param_values=1, param_values=0xab25508,
    invocation_hint=0xbfffe610, marshal_data=0x8083d10) at /build/buildd/glib2.0-2.26.1/gobject/gmarshal.c:79
#7 0x019ef412 in g_closure_invoke (closure=0xaa6ed88, return_value=0x0, n_param_values=1, param_values=0xab25508,
    invocation_hint=0xbfffe610) at /build/buildd/glib2.0-2.26.1/gobject/gclosure.c:766
#8 0x01a05f78 in signal_emit_unlocked_R (node=<value optimised out>, detail=<value optimised out>, instance=0xaa84220,
    emission_return=0x0, instance_and_params=0xab25508) at /build/buildd/glib2.0-2.26.1/gobject/gsignal.c:3322
#9 0x01a06fac in g_signal_emit_valist (instance=0xaa84220, signal_id=318, detail=0, var_args=0xbfffe848 "P\aA\n")
    at /build/buildd/glib2.0-2.26.1/gobject/gsignal.c:2983
#10 0x01a07275 in g_signal_emit_by_name (instance=0xaa84220, detailed_signal=0xbce996 "changed")
    at /build/buildd/glib2.0-2.26.1/gobject/gsignal.c:3077

review: Needs Fixing
10127. By Andrew

try to fix bug

10128. By Andrew

merged with trunk so I can build again...

10129. By Andrew

fixed crash!

Revision history for this message
Andrew (and471) wrote :

Hi, sorry for my long absense form this but I have just fixed the issue that JazzyNico reported above.

The branch is ready for merging into trunk. Please someone do it quickly! :)

Revision history for this message
Andrew (and471) :
review: Needs Resubmitting
Revision history for this message
su_v (suv-lp) wrote :

> The branch is ready for merging into trunk. Please someone do it quickly!

I repeat my concerns expressed earlier: if doable, please maintain the old dialog in Inkscape's sources for osx users: the new GIO-based dialog fails on OS X and merging the branch as is removes a long-supported feature from that port of Inkscape.

This comment is based on building inkscape from a local branch of 'ocal-dialog-improvements' with up-to-date MacPorts tree required for Inkscape's dependencies, on OS X 10.7.2 Lion (compiler: llvm-gcc-4.2.1):

Chillida:bin su_v$ ./inkscape -V
Inkscape 0.48+devel r10129 (Dec 28 2011)
Chillida:bin su_v$ ./inkscape
Hi!

(inkscape:40831): glibmm-CRITICAL **:
unhandled exception (type Glib::Error) in signal handler:
domain: g-io-error-quark
code : 15
what : Operation not supported

Chillida:bin su_v$ port installed glib2 glibmm gtk2 gtkmm
The following ports are currently installed:
  glib2 @2.30.2_2 (active)
  glibmm @2.28.2_0 (active)
  gtk2 @2.24.8_1+x11 (active)
  gtkmm @2.24.2_0 (active)
Chillida:bin su_v$

Revision history for this message
Andrew (and471) wrote :

I am not sure how to do the above/whether the main devs want that sort of thing. Decision please?

Revision history for this message
su_v (suv-lp) wrote :

@ScislaC - any comments?

Revision history for this message
ScislaC (scislac) wrote :

Andrew: For all intensive purposes, ~suv is as much of a main dev as the rest of us regular contributors. We have a long standing policy of not breaking features on a platform that has previously supported them if it can be avoided.

As ~suv mentioned, supporting the old dialog on osx is an option (make it conditional based on platform during compile time), however, this is still somewhat kludgy because different platforms will have different experiences... but nothing "breaks".

Either way, it's a good idea to find out why glibmm on OSX has this issue and to file an upstream bug report or add to one if it already exists. If we're possibly sitting on an unknown issue, we're only hurting ourselves. Likewise, if we're possibly another "me too", that could be extra motivation for upstream to fix it.

Revision history for this message
Andrew (and471) wrote :

Essentially I had given up on this for 6 months, as I didn't have the time for it, and I have finally managed to fix it for most platforms.

I have no idea how to fix the issue that ~suv describes and wouldn't even know where to begin. I did ask for help on the mailing list 6 months ago but got no help.

I don't have the time now to devote to fixing this and I really do fear that this will forever remain unmerged. If I am truly honest, this experience has made it seems like it is really hard to contribute to Inkscape, which sucks because I love the program and wanted to make it better.

Revision history for this message
su_v (suv-lp) wrote :

As mentioned earlier, "I will not vote against the merge", and I'm sorry if my tests have slowed down the process of getting highly appreciated new contributions merged into trunk.

I was speaking up a) because I was eager to test the new improvements ;) and b) because it might otherwise go unnoticed (and will potentially only get back on the radar with later efforts to migrate Inkscape from the deprecated (?) gnome-vfs to gio - bug #715215).

Revision history for this message
ScislaC (scislac) wrote :

Andrew: We definitely don't want you to have a bad experience contributing to the project. ~suv did bump the thread on the mailing list again today and if we're lucky, maybe we can get Jon Cruz to help us merge this in a way that is still OSX friendly. I'll be honest that this is a more unusual case than most contributions as it has issues that are platform specific, while most aspects of Inkscape are usually more platform agnostic. Please don't be discouraged by this, we will see what we can do to get this merged in the near future. :)

Revision history for this message
jazzynico (jazzynico) wrote :

Just tested the latest version, and the dialog no longer crashes on Ubuntu 11.04.
There are still two very minor "bugs":
1. Is printf("%s\n", "Hi!"); in ocaldialogs.cpp absolutely necessary? ;)
2. When testing multi-images import with Ctrl (that doesn't work, but it's not really a bug, just a missing feature), the preview window always show the n-1 image.

Thanks for your efforts. The new dialog is a real improvement. I'm sure we'll find a solution so that it's merged before 0.49 is out.

Revision history for this message
jazzynico (jazzynico) wrote :

Andrew, it would be very disappointing to see all your work lost just because you don't have time to investigate more. Would you mind giving write access to your branches (OCAL and gallery) to the Inkscape Developers group, so that we can work on it?

Revision history for this message
Andrew (and471) wrote :

I would love to but as I am not in that group, I cannot change it in Launchpad to be so

Revision history for this message
su_v (suv-lp) wrote :

> Andrew, it would be very disappointing to see all your work lost just because
> you don't have time to investigate more. Would you mind giving write access to
> your branches (OCAL and gallery) to the Inkscape Developers group, so that we
> can work on it?

As mentioned before, I'm not "voting against" the merge - I just want to make sure the issue on OS X is known.

Proposing to merge the ocal branch as is (as long as the two branches haven't diverged too much) and deal with the GIO issues on OS X in trunk (maybe disable the dialog on darwin platforms for the next release if not solved in time).

@ScislaC - would you agree to this?

For the gallery dialog, could the compromise to keep the version with the update button and not rely on monitoring the directory for changes (which failed on OS X, also due to features in GIO apparently not (yet) supported in GTK2 on Mac OS X) be agreed on?
<http://thread.gmane.org/gmane.comp.graphics.inkscape.devel/36401>

Revision history for this message
Alex Valavanis (valavanisalex) wrote :

OK, I've gone ahead and merged this. It seems like a massive improvement for linux, and it would be a shame to lose all this excellent work. I suggest that we thoroughly test the merged version and try to work out the remaining issues together in trunk as suv proposed.

If anyone disagrees strongly with this, we can always revert.

review: Approve
Revision history for this message
Andrew (and471) wrote :

Thankyou! :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'share/icons/Makefile.am'
2--- share/icons/Makefile.am 2010-02-05 18:41:32 +0000
3+++ share/icons/Makefile.am 2011-12-27 21:09:25 +0000
4@@ -38,7 +38,8 @@
5 feSpecularLighting-icon.png \
6 feSpecularLighting-icon.svg \
7 feTurbulence-icon.png \
8- feTurbulence-icon.svg
9+ feTurbulence-icon.svg \
10+ OCAL.png
11
12 icons_DATA = \
13 $(pixmaps) \
14
15=== added file 'share/icons/OCAL.png'
16Binary files share/icons/OCAL.png 1970-01-01 00:00:00 +0000 and share/icons/OCAL.png 2011-12-27 21:09:25 +0000 differ
17=== modified file 'src/file.cpp'
18--- src/file.cpp 2011-12-08 11:53:54 +0000
19+++ src/file.cpp 2011-12-27 21:09:25 +0000
20@@ -1125,7 +1125,7 @@
21 return;
22 }
23
24- //# Get file name and extension type
25+ // Get file name and extension type
26 Glib::ustring fileName = importDialogInstance->getFilename();
27 Inkscape::Extension::Extension *selection = importDialogInstance->getSelectionType();
28
29@@ -1340,8 +1340,8 @@
30 export_path = export_path_local;
31
32 // Show the Export To OCAL dialog
33- Inkscape::UI::Dialog::FileExportToOCALDialog *exportDialogInstance =
34- new Inkscape::UI::Dialog::FileExportToOCALDialog(
35+ Inkscape::UI::Dialog::OCAL:ExportDialog *exportDialogInstance =
36+ new Inkscape::UI::Dialog::OCAL:ExportDialog
37 parentWindow,
38 Inkscape::UI::Dialog::EXPORT_TYPES,
39 (char const *) _("Select file to export to")
40@@ -1452,11 +1452,23 @@
41 ## I M P O R T F R O M O C A L
42 ######################*/
43
44+Inkscape::UI::Dialog::OCAL::ImportDialog* import_ocal_dialog = NULL;
45+
46 /**
47- * Display an ImportToOcal Dialog, and the selected document from OCAL
48+ * Display an ImportFromOcal Dialog, and the selected document from OCAL
49 */
50+void on_import_from_ocal_response(Glib::ustring filename)
51+{
52+ SPDocument *doc = SP_ACTIVE_DOCUMENT;
53+
54+ if (!filename.empty()) {
55+ Inkscape::Extension::Extension *selection = import_ocal_dialog->get_selection_type();
56+ file_import(doc, filename, selection);
57+ }
58+}
59+
60 void
61-sp_file_import_from_ocal(Gtk::Window &parentWindow)
62+sp_file_import_from_ocal(Gtk::Window &parent_window)
63 {
64 static Glib::ustring import_path;
65
66@@ -1464,47 +1476,18 @@
67 if (!doc)
68 return;
69
70- Inkscape::UI::Dialog::FileImportFromOCALDialog *importDialogInstance = NULL;
71-
72- if (!importDialogInstance) {
73- importDialogInstance = new
74- Inkscape::UI::Dialog::FileImportFromOCALDialog(
75- parentWindow,
76- import_path,
77+ if (import_ocal_dialog == NULL) {
78+ import_ocal_dialog = new
79+ Inkscape::UI::Dialog::OCAL::ImportDialog(
80+ parent_window,
81 Inkscape::UI::Dialog::IMPORT_TYPES,
82- (char const *)_("Import From Open Clip Art Library"));
83- }
84-
85- bool success = importDialogInstance->show();
86- if (!success) {
87- delete importDialogInstance;
88- return;
89- }
90-
91- // Get file name and extension type
92- Glib::ustring fileName = importDialogInstance->getFilename();
93- Inkscape::Extension::Extension *selection = importDialogInstance->getSelectionType();
94-
95- delete importDialogInstance;
96- importDialogInstance = NULL;
97-
98- if (fileName.size() > 0) {
99-
100- Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
101-
102- if ( newFileName.size() > 0)
103- fileName = newFileName;
104- else
105- g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
106-
107- import_path = fileName;
108- if (import_path.size()>0)
109- import_path.append(G_DIR_SEPARATOR_S);
110-
111- file_import(doc, fileName, selection);
112- }
113-
114- return;
115+ (char const *)_("Import Clip Art"));
116+
117+ import_ocal_dialog->signal_response().connect(
118+ sigc::ptr_fun(&on_import_from_ocal_response));
119+ }
120+
121+ import_ocal_dialog->show_all();
122 }
123
124 /*######################
125
126=== modified file 'src/file.h'
127--- src/file.h 2011-12-08 11:53:54 +0000
128+++ src/file.h 2011-12-27 21:09:25 +0000
129@@ -20,6 +20,7 @@
130 #include <gtk/gtk.h>
131
132 #include "extension/system.h"
133+#include "ui/dialog/ocaldialogs.h"
134
135 struct SPDesktop;
136 struct SPDocument;
137@@ -166,6 +167,11 @@
138 /**
139 * Import a document from OCAL
140 */
141+void on_import_from_ocal_response(Glib::ustring filename);
142+
143+/**
144+ * Import a document from OCAL
145+ */
146 void sp_file_import_from_ocal (Gtk::Window &parentWindow );
147
148
149
150=== modified file 'src/io/sys.cpp'
151--- src/io/sys.cpp 2011-12-08 11:53:54 +0000
152+++ src/io/sys.cpp 2011-12-27 21:09:25 +0000
153@@ -355,6 +355,15 @@
154 return result;
155 }
156
157+/*
158+ * Returns the file extension of a path/filename
159+ */
160+Glib::ustring Inkscape::IO::get_file_extension(Glib::ustring path)
161+{
162+ Glib::ustring::size_type period_location = path.find_last_of(".");
163+ return path.substr(period_location);
164+}
165+
166 /*
167 Local Variables:
168 mode:c++
169
170=== modified file 'src/io/sys.h'
171--- src/io/sys.h 2011-12-08 11:53:54 +0000
172+++ src/io/sys.h 2011-12-27 21:09:25 +0000
173@@ -59,6 +59,8 @@
174 int* standard_output,
175 int* standard_error);
176
177+Glib::ustring get_file_extension(Glib::ustring path);
178+
179 }
180 }
181
182
183=== modified file 'src/menus-skeleton.h'
184--- src/menus-skeleton.h 2011-06-22 21:40:59 +0000
185+++ src/menus-skeleton.h 2011-12-27 21:09:25 +0000
186@@ -27,10 +27,8 @@
187 " <separator/>\n"
188 " <verb verb-id=\"FileImport\" />\n"
189 " <verb verb-id=\"FileExport\" />\n"
190-#ifdef WITH_GNOME_VFS
191 " <verb verb-id=\"FileImportFromOCAL\" />\n"
192 //" <verb verb-id=\"FileExportToOCAL\" />\n"
193-#endif
194 " <separator/>\n"
195 " <verb verb-id=\"FilePrint\" />\n"
196 " <separator/>\n"
197
198=== modified file 'src/ui/dialog/ocaldialogs.cpp'
199--- src/ui/dialog/ocaldialogs.cpp 2011-10-03 07:24:15 +0000
200+++ src/ui/dialog/ocaldialogs.cpp 2011-12-27 21:09:25 +0000
201@@ -5,6 +5,7 @@
202 /* Authors:
203 * Bruno Dilly
204 * Other dudes from The Inkscape Organization
205+ * Andrew Higginson
206 *
207 * Copyright (C) 2007 Bruno Dilly <bruno.dilly@gmail.com>
208 *
209@@ -20,6 +21,7 @@
210 #include <errno.h> // errno
211 #include <string.h> // strerror()
212
213+#include "path-prefix.h"
214 #include "ocaldialogs.h"
215 #include "filedialogimpl-gtkmm.h"
216 #include "interface.h"
217@@ -34,6 +36,8 @@
218 {
219 namespace Dialog
220 {
221+namespace OCAL
222+{
223
224 //########################################################################
225 //# F I L E E X P O R T T O O C A L
226@@ -43,7 +47,7 @@
227 * Callback for fileNameEntry widget
228 */
229 /*
230-void FileExportToOCALDialog::fileNameEntryChangedCallback()
231+void ExportDialog::fileNameEntryChangedCallback()
232 {
233 if (!fileNameEntry)
234 return;
235@@ -60,10 +64,10 @@
236 * Constructor
237 */
238 /*
239-FileExportToOCALDialog::FileExportToOCALDialog(Gtk::Window &parentWindow,
240+ExportDialog::ExportDialog(Gtk::Window &parentWindow,
241 FileDialogType fileTypes,
242 const Glib::ustring &title) :
243- FileDialogOCALBase(title, parentWindow)
244+ FileDialogBase(title, parentWindow)
245 {
246 */
247 /*
248@@ -103,7 +107,7 @@
249 //Catch when user hits [return] on the text field
250 fileNameEntry = entries[0];
251 fileNameEntry->signal_activate().connect(
252- sigc::mem_fun(*this, &FileExportToOCALDialog::fileNameEntryChangedCallback) );
253+ sigc::mem_fun(*this, &ExportDialog::fileNameEntryChangedCallback) );
254 }
255
256 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
257@@ -116,7 +120,7 @@
258 * Destructor
259 */
260 /*
261-FileExportToOCALDialog::~FileExportToOCALDialog()
262+ExportDialog::~ExportDialog()
263 {
264 }
265 */
266@@ -125,7 +129,7 @@
267 */
268 /*
269 bool
270-FileExportToOCALDialog::show()
271+ExportDialog::show()
272 {
273 set_modal (TRUE); //Window
274 sp_transientize((GtkWidget *)gobj()); //Make transient
275@@ -147,7 +151,7 @@
276 */
277 /*
278 Glib::ustring
279-FileExportToOCALDialog::getFilename()
280+ExportDialog::get_filename()
281 {
282 myFilename = fileNameEntry->get_text();
283 if (!Glib::get_charset()) //If we are not utf8
284@@ -158,7 +162,7 @@
285
286
287 void
288-FileExportToOCALDialog::change_title(const Glib::ustring& title)
289+ExportDialog::change_title(const Glib::ustring& title)
290 {
291 this->set_title(title);
292 }
293@@ -173,8 +177,8 @@
294 * Constructor
295 */
296 /*
297-FileExportToOCALPasswordDialog::FileExportToOCALPasswordDialog(Gtk::Window &parentWindow,
298- const Glib::ustring &title) : FileDialogOCALBase(title, parentWindow)
299+ExportPasswordDialog::ExportPasswordDialog(Gtk::Window &parentWindow,
300+ const Glib::ustring &title) : FileDialogBase(title, parentWindow)
301 {
302 */
303 /*
304@@ -221,7 +225,7 @@
305 * Destructor
306 */
307 /*
308-FileExportToOCALPasswordDialog::~FileExportToOCALPasswordDialog()
309+ExportPasswordDialog::~ExportPasswordDialog()
310 {
311 }
312 */
313@@ -230,7 +234,7 @@
314 */
315 /*
316 bool
317-FileExportToOCALPasswordDialog::show()
318+ExportPasswordDialog::show()
319 {
320 set_modal (TRUE); //Window
321 sp_transientize((GtkWidget *)gobj()); //Make transient
322@@ -252,7 +256,7 @@
323 */
324 /*
325 Glib::ustring
326-FileExportToOCALPasswordDialog::getUsername()
327+ExportPasswordDialog::getUsername()
328 {
329 myUsername = usernameEntry->get_text();
330 return myUsername;
331@@ -263,14 +267,14 @@
332 */
333 /*
334 Glib::ustring
335-FileExportToOCALPasswordDialog::getPassword()
336+ExportPasswordDialog::getPassword()
337 {
338 myPassword = passwordEntry->get_text();
339 return myPassword;
340 }
341
342 void
343-FileExportToOCALPasswordDialog::change_title(const Glib::ustring& title)
344+ExportPasswordDialog::change_title(const Glib::ustring& title)
345 {
346 this->set_title(title);
347 }
348@@ -280,408 +284,946 @@
349 //### F I L E I M P O R T F R O M O C A L
350 //#########################################################################
351
352+WrapLabel::WrapLabel() : Gtk::Label()
353+{
354+ signal_size_allocate().connect(sigc::mem_fun(*this, &WrapLabel::_on_size_allocate));
355+}
356+
357+void WrapLabel::_on_size_allocate(Gtk::Allocation& allocation)
358+{
359+ set_size_request(allocation.get_width(), -1);
360+}
361+
362+
363+LoadingBox::LoadingBox() : Gtk::EventBox()
364+{
365+ set_visible_window(false);
366+ draw_spinner = false;
367+ spinner_step = 0;
368+ signal_expose_event().connect(sigc::mem_fun(*this, &LoadingBox::_on_expose_event), false);
369+}
370+
371+bool LoadingBox::_on_expose_event(GdkEventExpose* event)
372+{
373+ Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
374+
375+ // Draw shadow
376+ int x = get_allocation().get_x();
377+ int y = get_allocation().get_y();
378+ int width = get_allocation().get_width();
379+ int height = get_allocation().get_height();
380+
381+ get_style()->paint_shadow(get_window(), get_state(), Gtk::SHADOW_IN,
382+ Gdk::Rectangle(x, y, width, height),
383+ *this, Glib::ustring("viewport"), x, y, width, height);
384+
385+ if (draw_spinner) {
386+ int spinner_size = 16;
387+ int spinner_x = x + (width - spinner_size) / 2;
388+ int spinner_y = y + (height - spinner_size) / 2;
389+
390+ // FIXME: Gtk::Style::paint_spinner not yet in gtkmm
391+ gtk_paint_spinner(gtk_widget_get_style(GTK_WIDGET(gobj())),
392+ gtk_widget_get_window(GTK_WIDGET(gobj())),
393+ gtk_widget_get_state(GTK_WIDGET(gobj())), NULL, GTK_WIDGET(gobj()),
394+ NULL, spinner_step, spinner_x, spinner_y, spinner_size, spinner_size);
395+ }
396+
397+ return false;
398+}
399+
400+void LoadingBox::start()
401+{
402+ // Timeout hasn't been stopped, so must be disconnected
403+ if ((draw_spinner != false) & (timeout != NULL)) {
404+ timeout.disconnect();
405+ }
406+
407+ draw_spinner = true;
408+ timeout = Glib::signal_timeout().connect(sigc::mem_fun(*this, &LoadingBox::on_timeout), 80);
409+}
410+
411+void LoadingBox::stop()
412+{
413+ draw_spinner = false;
414+}
415+
416+bool LoadingBox::on_timeout() {
417+ if (draw_spinner) {
418+
419+ if (spinner_step == 11) {
420+ spinner_step = 0;
421+ } else {
422+ spinner_step ++;
423+ }
424+
425+ queue_draw();
426+ return true;
427+ }
428+ return false;
429+}
430+
431+PreviewWidget::PreviewWidget() : Gtk::VBox(false, 12)
432+{
433+ box_loading = new LoadingBox();
434+ image = new Gtk::Image();
435+
436+ label_title = new WrapLabel();
437+ label_description = new WrapLabel();
438+ label_time = new WrapLabel();
439+
440+ pack_start(*box_loading, false, false);
441+ pack_start(*image, false, false);
442+ pack_start(*label_title, false, false);
443+ pack_start(*label_description, false, false);
444+ pack_start(*label_time, false, false);
445+
446+ label_title->set_line_wrap(true);
447+ label_title->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
448+ label_title->set_justify(Gtk::JUSTIFY_CENTER);
449+ label_description->set_line_wrap(true);
450+ label_description->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
451+ label_description->set_justify(Gtk::JUSTIFY_CENTER);
452+ label_time->set_line_wrap(true);
453+ label_time->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
454+ label_time->set_justify(Gtk::JUSTIFY_CENTER);
455+
456+ box_loading->set_no_show_all(true);
457+ image->set_no_show_all(true);
458+ label_title->set_size_request(90, -1);
459+ label_description->set_size_request(90, -1);
460+ label_time->set_size_request(90, -1);
461+ box_loading->set_size_request(90, 90);
462+ set_border_width(12);
463+
464+ signal_expose_event().connect(sigc::mem_fun(*this, &PreviewWidget::_on_expose_event), false);
465+
466+ clear();
467+}
468+
469+void PreviewWidget::set_metadata(Glib::ustring description, Glib::ustring creator,
470+ Glib::ustring time)
471+{
472+ label_title->set_markup(g_markup_printf_escaped("<b>%s</b>", description.c_str()));
473+ label_description->set_markup(g_markup_printf_escaped("%s", creator.c_str()));
474+ label_time->set_markup(g_markup_printf_escaped("<small>%s</small>", time.c_str()));
475+
476+ show_box_loading();
477+}
478+
479+void PreviewWidget::show_box_loading()
480+{
481+ box_loading->show();
482+ box_loading->start();
483+}
484+
485+void PreviewWidget::hide_box_loading()
486+{
487+ box_loading->hide();
488+ box_loading->stop();
489+}
490+
491+void PreviewWidget::set_image(std::string path)
492+{
493+ image->set(path);
494+ hide_box_loading();
495+ image->show();
496+}
497+
498+void PreviewWidget::clear()
499+{
500+ label_title->set_markup("");
501+ label_description->set_markup("");
502+ label_time->set_markup("");
503+
504+ box_loading->hide();
505+ image->hide();
506+}
507+
508+bool PreviewWidget::_on_expose_event(GdkEventExpose* event)
509+{
510+ Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
511+
512+ // Draw background
513+ int x = get_allocation().get_x();
514+ int y = get_allocation().get_y();
515+ int width = get_allocation().get_width();
516+ int height = get_allocation().get_height();
517+ Gdk::Color background_fill = get_style()->get_base(get_state());
518+
519+ cr->rectangle(x, y, width, height);
520+ Gdk::Cairo::set_source_color(cr, background_fill);
521+ cr->fill();
522+
523+ return false;
524+}
525+
526+StatusWidget::StatusWidget() : Gtk::HBox(false, 6)
527+{
528+ image = new Gtk::Image(Gtk::Stock::DIALOG_ERROR, Gtk::ICON_SIZE_MENU);
529+ spinner = new Gtk::Spinner();
530+ label = new Gtk::Label();
531+
532+ image->set_no_show_all(true);
533+ spinner->set_no_show_all(true);
534+ label->set_no_show_all(true);
535+
536+ pack_start(*image, false, false);
537+ pack_start(*spinner, false, false);
538+ pack_start(*label, false, false);
539+}
540+
541+void StatusWidget::clear()
542+{
543+ spinner->hide();
544+ image->hide();
545+ label->hide();
546+}
547+
548+void StatusWidget::set_info(Glib::ustring text)
549+{
550+ spinner->hide();
551+ image->show();
552+ label->show();
553+ image->set(Gtk::Stock::DIALOG_INFO, Gtk::ICON_SIZE_MENU);
554+ label->set_text(text);
555+}
556+
557+void StatusWidget::set_error(Glib::ustring text)
558+{
559+ spinner->hide();
560+ image->show();
561+ label->show();
562+ image->set(Gtk::Stock::DIALOG_ERROR, Gtk::ICON_SIZE_MENU);
563+ label->set_text(text);
564+}
565+
566+void StatusWidget::start_process(Glib::ustring text)
567+{
568+ image->hide();
569+ spinner->show();
570+ label->show();
571+ label->set_text(text);
572+ spinner->start();
573+ show_all();
574+}
575+
576+void StatusWidget::end_process()
577+{
578+ spinner->stop();
579+ spinner->hide();
580+ label->hide();
581+ clear();
582+}
583+
584+SearchEntry::SearchEntry() : Gtk::Entry()
585+{
586+ signal_changed().connect(sigc::mem_fun(*this, &SearchEntry::_on_changed));
587+ signal_icon_press().connect(sigc::mem_fun(*this, &SearchEntry::_on_icon_pressed));
588+
589+ set_icon_from_stock(Gtk::Stock::FIND, Gtk::ENTRY_ICON_PRIMARY);
590+ gtk_entry_set_icon_from_stock(gobj(), GTK_ENTRY_ICON_SECONDARY, NULL);
591+}
592+
593+void SearchEntry::_on_icon_pressed(Gtk::EntryIconPosition icon_position, const GdkEventButton* event)
594+{
595+ if (icon_position == Gtk::ENTRY_ICON_SECONDARY) {
596+ grab_focus();
597+ set_text("");
598+ } else if (icon_position == Gtk::ENTRY_ICON_PRIMARY) {
599+ select_region(0, -1);
600+ grab_focus();
601+ }
602+}
603+
604+void SearchEntry::_on_changed()
605+{
606+ if (get_text().empty()) {
607+ gtk_entry_set_icon_from_stock(gobj(), GTK_ENTRY_ICON_SECONDARY, NULL);
608+ } else {
609+ set_icon_from_stock(Gtk::Stock::CLEAR, Gtk::ENTRY_ICON_SECONDARY);
610+ }
611+}
612+
613+BaseBox::BaseBox() : Gtk::EventBox()
614+{
615+ signal_expose_event().connect(sigc::mem_fun(*this, &BaseBox::_on_expose_event), false);
616+ set_visible_window(false);
617+}
618+
619+bool BaseBox::_on_expose_event(GdkEventExpose* event)
620+{
621+ Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
622+
623+ // Draw background and shadow
624+ int x = get_allocation().get_x();
625+ int y = get_allocation().get_y();
626+ int width = get_allocation().get_width();
627+ int height = get_allocation().get_height();
628+ Gdk::Color background_fill = get_style()->get_base(get_state());
629+
630+ cr->rectangle(x, y, width, height);
631+ Gdk::Cairo::set_source_color(cr, background_fill);
632+ cr->fill();
633+
634+ get_style()->paint_shadow(get_window(), get_state(), Gtk::SHADOW_IN,
635+ Gdk::Rectangle(x, y, width, height),
636+ *this, Glib::ustring("viewport"), x, y, width, height);
637+
638+ return false;
639+}
640+
641+LogoArea::LogoArea() : Gtk::EventBox()
642+{
643+ // Try to load the OCAL logo, but if the file is not found, degrade gracefully
644+ try {
645+ std::string logo_path = Glib::build_filename(INKSCAPE_PIXMAPDIR, "OCAL.png");
646+ logo_mask = Cairo::ImageSurface::create_from_png(logo_path);
647+ draw_logo = true;
648+ } catch(Cairo::logic_error) {
649+ logo_mask = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, 1,1);
650+ draw_logo = false;
651+ }
652+ signal_expose_event().connect(sigc::mem_fun(*this, &LogoArea::_on_expose_event));
653+ set_visible_window(false);
654+}
655+
656+bool LogoArea::_on_expose_event(GdkEventExpose* event)
657+{
658+ if (draw_logo) {
659+ int x = get_allocation().get_x();
660+ int y = get_allocation().get_y();
661+ int width = get_allocation().get_width();
662+ int height = get_allocation().get_height();
663+ int x_logo = x + (width - 220) / 2;
664+ int y_logo = y + (height - 76) / 2;
665+
666+ Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
667+
668+ // Draw logo, we mask [read fill] it with the mid colour from the
669+ // user's GTK theme
670+ Gdk::Color logo_fill = get_style()->get_mid(get_state());
671+
672+ Gdk::Cairo::set_source_color(cr, logo_fill);
673+ cr->mask(logo_mask, x_logo, y_logo);
674+ }
675+
676+ return false;
677+}
678+
679+SearchResultList::SearchResultList(guint columns_count) : ListViewText(columns_count)
680+{
681+ set_headers_visible(false);
682+ set_column_title(RESULTS_COLUMN_MARKUP, _("Clipart found"));
683+
684+ Gtk::CellRenderer* cr_markup = get_column_cell_renderer(RESULTS_COLUMN_MARKUP);
685+ cr_markup->set_property("ellipsize", Pango::ELLIPSIZE_END);
686+ get_column(RESULTS_COLUMN_MARKUP)->clear_attributes(*cr_markup);
687+ get_column(RESULTS_COLUMN_MARKUP)->add_attribute(*cr_markup,
688+ "markup", RESULTS_COLUMN_MARKUP);
689+
690+ // Hide all columns except for the MARKUP column
691+ for (int i = 0; i < RESULTS_COLUMN_LENGTH; i++) {
692+ if (i != RESULTS_COLUMN_MARKUP) {
693+ get_column(i)->set_visible(false);
694+ }
695+ }
696+}
697+
698+void ImportDialog::on_list_results_selection_changed()
699+{
700+ std::vector<Gtk::TreeModel::Path> pathlist;
701+ pathlist = list_results->get_selection()->get_selected_rows();
702+ std::vector<int> posArray(1);
703+
704+ // If nothing is selected, then return
705+ if (((int) pathlist.size()) < 1) {
706+ return;
707+ }
708+ posArray = pathlist[0].get_indices();
709+ int row = posArray[0];
710+
711+ Glib::ustring guid = list_results->get_text(row, RESULTS_COLUMN_GUID);
712+
713+ bool item_selected = (!guid.empty());
714+ button_import->set_sensitive(item_selected);
715+}
716+
717+
718+void ImportDialog::on_button_import_clicked() {
719+ std::vector<Gtk::TreeModel::Path> pathlist;
720+ pathlist = list_results->get_selection()->get_selected_rows();
721+ std::vector<int> posArray(1);
722+
723+ // If nothing is selected, then return
724+ if (((int) pathlist.size()) < 1) {
725+ return;
726+ }
727+ posArray = pathlist[0].get_indices();
728+ int row = posArray[0];
729+
730+ button_import->set_sensitive(false);
731+ button_close->hide();
732+ button_cancel->show();
733+ widget_status->start_process(_("Downloading image..."));
734+ download_resource(TYPE_IMAGE, row);
735+}
736+
737 /*
738- * Calalback for cursor chage
739+ * Callback for cursor change
740 */
741-void FileListViewText::on_cursor_changed()
742+void ImportDialog::on_list_results_cursor_changed()
743 {
744 std::vector<Gtk::TreeModel::Path> pathlist;
745- pathlist = this->get_selection()->get_selected_rows();
746+ pathlist = list_results->get_selection()->get_selected_rows();
747 std::vector<int> posArray(1);
748+
749+ // If nothing is selected, then return
750+ if (((int) pathlist.size()) < 1) {
751+ return;
752+ }
753 posArray = pathlist[0].get_indices();
754-
755-#ifdef WITH_GNOME_VFS
756- gnome_vfs_init();
757- GnomeVFSHandle *from_handle = NULL;
758- GnomeVFSHandle *to_handle = NULL;
759- GnomeVFSFileSize bytes_read;
760- GnomeVFSFileSize bytes_written;
761- GnomeVFSResult result;
762- guint8 buffer[8192];
763- Glib::ustring fileUrl;
764-
765- // FIXME: this would be better as a per-user OCAL cache of files
766- // instead of filling /tmp with downloads.
767- //
768- // create file path
769- const std::string tmptemplate = "ocal-";
770- std::string tmpname;
771- int fd = Inkscape::IO::file_open_tmp(tmpname, tmptemplate);
772- if (fd<0) {
773- g_warning("Error creating temp file");
774- return;
775- }
776- close(fd);
777- // make sure we don't collide with other users on the same machine
778- myFilename = tmpname;
779- myFilename.append("-");
780- myFilename.append(get_text(posArray[0], 2));
781- // rename based on original image's name, retaining extension
782- if (rename(tmpname.c_str(),myFilename.c_str())<0) {
783- unlink(tmpname.c_str());
784- g_warning("Error creating destination file '%s': %s", myFilename.c_str(), strerror(errno));
785- goto failquit;
786- }
787-
788- //get file url
789- fileUrl = get_text(posArray[0], 1); //http url
790-
791- //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
792- //Glib::ustring fileUrl = "dav://"; //dav url
793- //fileUrl.append(prefs->getString("/options/ocalurl/str"));
794- //fileUrl.append("/dav.php/");
795- //fileUrl.append(get_text(posArray[0], 3)); //author dir
796- //fileUrl.append("/");
797- //fileUrl.append(get_text(posArray[0], 2)); //filename
798-
799- if (!Glib::get_charset()) //If we are not utf8
800- fileUrl = Glib::filename_to_utf8(fileUrl);
801-
802- {
803- // open the temp file to receive
804- result = gnome_vfs_open (&to_handle, myFilename.c_str(), GNOME_VFS_OPEN_WRITE);
805- if (result == GNOME_VFS_ERROR_NOT_FOUND){
806- result = gnome_vfs_create (&to_handle, myFilename.c_str(), GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL);
807- }
808- if (result != GNOME_VFS_OK) {
809- g_warning("Error creating temp file '%s': %s", myFilename.c_str(), gnome_vfs_result_to_string(result));
810- goto fail;
811- }
812- result = gnome_vfs_open (&from_handle, fileUrl.c_str(), GNOME_VFS_OPEN_READ);
813- if (result != GNOME_VFS_OK) {
814- g_warning("Could not find the file in Open Clip Art Library.");
815- goto fail;
816- }
817- // copy the file
818- while (1) {
819- result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read);
820- if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){
821- result = gnome_vfs_close (from_handle);
822- result = gnome_vfs_close (to_handle);
823- break;
824- }
825- if (result != GNOME_VFS_OK) {
826- g_warning("%s", gnome_vfs_result_to_string(result));
827- goto fail;
828- }
829- result = gnome_vfs_write (to_handle, buffer, bytes_read, &bytes_written);
830- if (result != GNOME_VFS_OK) {
831- g_warning("%s", gnome_vfs_result_to_string(result));
832- goto fail;
833- }
834- if (bytes_read != bytes_written){
835- g_warning("Bytes read not equal to bytes written");
836- goto fail;
837- }
838- }
839- }
840- myPreview->showImage(myFilename);
841- myLabel->set_text(get_text(posArray[0], 4));
842-#endif
843- return;
844-fail:
845- unlink(myFilename.c_str());
846-failquit:
847- myFilename = "";
848-}
849-
850+ int row = posArray[0];
851+
852+ if (downloading_thumbnail) {
853+ cancellable_thumbnail->cancel();
854+ cancelled_thumbnail = true;
855+ }
856+
857+ update_preview(row);
858+ downloading_thumbnail = true;
859+ download_resource(TYPE_THUMBNAIL, row);
860+}
861+void ImportDialog::update_preview(int row)
862+{
863+ Glib::ustring description = list_results->get_text(row, RESULTS_COLUMN_DESCRIPTION);
864+ Glib::ustring creator = list_results->get_text(row, RESULTS_COLUMN_CREATOR);
865+ Glib::ustring date = list_results->get_text(row, RESULTS_COLUMN_DATE);
866+
867+ preview_files->clear();
868+ preview_files->set_metadata(description, creator, date);
869+}
870+
871+
872+std::string ImportDialog::get_temporary_dir(ResourceType type)
873+{
874+ std::string ocal_tmp_dir = Glib::build_filename(Glib::get_tmp_dir(),
875+ "openclipart");
876+
877+ if (type == TYPE_THUMBNAIL) {
878+ return Glib::build_filename(ocal_tmp_dir, "thumbnails");
879+ } else {
880+ return Glib::build_filename(ocal_tmp_dir, "images");
881+ }
882+}
883+
884+void ImportDialog::create_temporary_dirs()
885+{
886+ // Make sure the temporary directories exists, if not, create them
887+ std::string ocal_tmp_thumbnail_dir = get_temporary_dir(TYPE_THUMBNAIL);
888+ std::string ocal_tmp_image_dir = get_temporary_dir(TYPE_IMAGE);
889+
890+ if (!Glib::file_test(ocal_tmp_thumbnail_dir, Glib::FILE_TEST_EXISTS)) {
891+ Glib::RefPtr<Gio::File> directory = Gio::File::create_for_path(ocal_tmp_thumbnail_dir);
892+ directory->make_directory_with_parents();
893+ }
894+
895+ if (!Glib::file_test(ocal_tmp_image_dir, Glib::FILE_TEST_EXISTS)) {
896+ Glib::RefPtr<Gio::File> directory = Gio::File::create_for_path(ocal_tmp_image_dir);
897+ directory->make_directory_with_parents();
898+ }
899+}
900+
901+void ImportDialog::download_resource(ResourceType type, int row)
902+{
903+ // Get Temporary Directory
904+ std::string ocal_tmp_dir = get_temporary_dir(type);
905+
906+ // Make a unique filename for the clipart, in the form 'GUID.extension'
907+ Glib::ustring guid = list_results->get_text(row, RESULTS_COLUMN_GUID);
908+ Glib::ustring original_filename;
909+
910+ if (type == TYPE_IMAGE) {
911+ original_filename = list_results->get_text(row, RESULTS_COLUMN_FILENAME);
912+ } else {
913+ original_filename = list_results->get_text(row, RESULTS_COLUMN_THUMBNAIL_FILENAME);
914+ }
915+ Glib::ustring extension = Inkscape::IO::get_file_extension(original_filename);
916+
917+ Glib::ustring filename = Glib::ustring::compose("%1%2", guid, extension);
918+ std::string path = Glib::build_filename(ocal_tmp_dir, filename.c_str());
919+ Glib::RefPtr<Gio::File> file_local = Gio::File::create_for_path(path);
920+
921+ // If the file has already been downloaded, use it
922+ if (Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
923+ if (type == TYPE_IMAGE) {
924+ on_image_downloaded(path, true);
925+ } else {
926+ on_thumbnail_downloaded(path, true);
927+ }
928+ return;
929+ }
930+
931+ // Get Remote File URL and get the respective cancellable object
932+ Glib::ustring url;
933+ Glib::RefPtr<Gio::Cancellable> cancellable;
934+
935+ if (type == TYPE_IMAGE) {
936+ url = list_results->get_text(row, RESULTS_COLUMN_URL);
937+ cancellable_image = Gio::Cancellable::create();
938+ cancellable = cancellable_image;
939+ } else {
940+ url = list_results->get_text(row, RESULTS_COLUMN_THUMBNAIL_URL);
941+ cancellable_thumbnail = Gio::Cancellable::create();
942+ cancellable = cancellable_thumbnail;
943+ }
944+
945+ Glib::RefPtr<Gio::File> file_remote = Gio::File::create_for_uri(url);
946+
947+ // Download it asynchronously
948+ file_remote->copy_async(file_local,
949+ sigc::bind<Glib::RefPtr<Gio::File>, Glib::ustring, ResourceType>(
950+ sigc::mem_fun(*this, &ImportDialog::on_resource_downloaded),
951+ file_remote, path, type), cancellable,
952+ Gio::FILE_COPY_OVERWRITE);
953+}
954+
955+void ImportDialog::on_resource_downloaded(const Glib::RefPtr<Gio::AsyncResult>& result,
956+ Glib::RefPtr<Gio::File> file_remote, Glib::ustring path, ResourceType resource)
957+{
958+ bool success;
959+
960+ try {
961+ success = file_remote->copy_finish(result);
962+ } catch(Glib::Error) {
963+ success = false;
964+ }
965+
966+ if (resource == TYPE_IMAGE) {
967+ on_image_downloaded(path, success);
968+ } else {
969+ on_thumbnail_downloaded(path, success);
970+ }
971+}
972+
973+void ImportDialog::on_image_downloaded(Glib::ustring path, bool success)
974+{
975+ button_import->set_sensitive(true);
976+ button_close->show();
977+ button_cancel->hide();
978+
979+ // If anything went wrong, show an error message if the user didn't do it
980+ if (!success && !cancelled_image) {
981+ widget_status->set_error(_("Could not download image"));
982+ }
983+ if (!success) {
984+ widget_status->clear();
985+ return;
986+ }
987+
988+ try {
989+ widget_status->clear();
990+ m_signal_response.emit(path);
991+ widget_status->set_info(_("Clipart downloaded successfully"));
992+ } catch(Glib::Error) {
993+ success = false;
994+ }
995+
996+ cancelled_image = false;
997+}
998+
999+void ImportDialog::on_thumbnail_downloaded(Glib::ustring path, bool success)
1000+{
1001+ downloading_thumbnail = false;
1002+
1003+ // If anything went wrong, show an error message if the user didn't do it
1004+ if (!success && !cancelled_thumbnail) {
1005+ widget_status->set_error(_("Could not download thumbnail file"));
1006+ return;
1007+ }
1008+ if (!success) {
1009+ widget_status->clear();
1010+ return;
1011+ }
1012+
1013+ try {
1014+ widget_status->clear();
1015+ preview_files->set_image(path);
1016+ } catch(Glib::Error) {
1017+ success = false;
1018+ }
1019+
1020+ cancelled_thumbnail = false;
1021+}
1022
1023 /*
1024 * Callback for row activated
1025 */
1026-void FileListViewText::on_row_activated(const Gtk::TreeModel::Path& /*path*/, Gtk::TreeViewColumn* /*column*/)
1027-{
1028- this->on_cursor_changed();
1029- myButton->activate();
1030-}
1031-
1032-
1033-/*
1034- * Returns the selected filename
1035- */
1036-Glib::ustring FileListViewText::getFilename()
1037-{
1038- return myFilename;
1039-}
1040-
1041-
1042-#ifdef WITH_GNOME_VFS
1043-/**
1044- * Read callback for xmlReadIO(), used below
1045- */
1046-static int vfs_read_callback (GnomeVFSHandle *handle, char* buf, int nb)
1047-{
1048- GnomeVFSFileSize ndone;
1049- GnomeVFSResult result;
1050-
1051- result = gnome_vfs_read (handle, buf, nb, &ndone);
1052-
1053- if (result == GNOME_VFS_OK) {
1054- return (int)ndone;
1055- } else {
1056- if (result != GNOME_VFS_ERROR_EOF) {
1057- sp_ui_error_dialog(_("Error while reading the Open Clip Art RSS feed"));
1058- g_warning("%s\n", gnome_vfs_result_to_string(result));
1059- }
1060- return -1;
1061- }
1062-}
1063-#endif
1064-
1065-
1066-/**
1067- * Callback for user input into searchTagEntry
1068- */
1069-void FileImportFromOCALDialog::searchTagEntryChangedCallback()
1070-{
1071- if (!searchTagEntry)
1072- return;
1073-
1074- notFoundLabel->hide();
1075- descriptionLabel->set_text("");
1076- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1077-
1078- Glib::ustring searchTag = searchTagEntry->get_text();
1079- // create the ocal uri to get rss feed
1080- Glib::ustring uri = "http://";
1081- uri.append(prefs->getString("/options/ocalurl/str"));
1082- uri.append("/media/feed/rss/");
1083- uri.append(searchTag);
1084- if (!Glib::get_charset()) //If we are not utf8
1085- uri = Glib::filename_to_utf8(uri);
1086-
1087-#ifdef WITH_GNOME_VFS
1088-
1089- // open the rss feed
1090- gnome_vfs_init();
1091- GnomeVFSHandle *from_handle = NULL;
1092- GnomeVFSResult result;
1093-
1094- result = gnome_vfs_open (&from_handle, uri.c_str(), GNOME_VFS_OPEN_READ);
1095- if (result != GNOME_VFS_OK) {
1096- sp_ui_error_dialog(_("Failed to receive the Open Clip Art Library RSS feed. Verify if the server name is correct in Configuration->Import/Export (e.g.: openclipart.org)"));
1097- return;
1098- }
1099-
1100- // create the resulting xml document tree
1101- // this initialize the library and test mistakes between compiled and shared library used
1102- LIBXML_TEST_VERSION
1103- xmlDoc *doc = NULL;
1104- xmlNode *root_element = NULL;
1105-
1106- doc = xmlReadIO ((xmlInputReadCallback) vfs_read_callback,
1107- (xmlInputCloseCallback) gnome_vfs_close, from_handle, uri.c_str(), NULL,
1108- XML_PARSE_RECOVER + XML_PARSE_NOWARNING + XML_PARSE_NOERROR);
1109- if (doc == NULL) {
1110- sp_ui_error_dialog(_("Server supplied malformed Clip Art feed"));
1111- g_warning("Failed to parse %s\n", uri.c_str());
1112- return;
1113- }
1114-
1115- // get the root element node
1116- root_element = xmlDocGetRootElement(doc);
1117-
1118- // clear the fileslist
1119- filesList->clear_items();
1120- filesList->set_sensitive(false);
1121-
1122- // print all xml the element names
1123- print_xml_element_names(root_element);
1124-
1125- if (filesList->size() == 0)
1126- {
1127- notFoundLabel->show();
1128- filesList->set_sensitive(false);
1129- }
1130- else
1131- filesList->set_sensitive(true);
1132-
1133- // free the document
1134- xmlFreeDoc(doc);
1135- // free the global variables that may have been allocated by the parser
1136- xmlCleanupParser();
1137- return;
1138-#endif
1139-}
1140-
1141+void ImportDialog::on_list_results_row_activated(const Gtk::TreeModel::Path& path,
1142+ Gtk::TreeViewColumn* column)
1143+{
1144+ on_list_results_cursor_changed();
1145+ button_import->signal_clicked();
1146+}
1147
1148 /**
1149 * Prints the names of the all the xml elements
1150 * that are siblings or children of a given xml node
1151 */
1152-void FileImportFromOCALDialog::print_xml_element_names(xmlNode * a_node)
1153+void SearchResultList::populate_from_xml(xmlNode * a_node)
1154 {
1155 xmlNode *cur_node = NULL;
1156 guint row_num = 0;
1157+
1158 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
1159- // get itens information
1160- if (strcmp((const char*)cur_node->name, "rss")) //avoid the root
1161+ // Get items information
1162+ if (strcmp((const char*)cur_node->name, "rss")) // Avoid the root
1163 if (cur_node->type == XML_ELEMENT_NODE && !strcmp((const char*)cur_node->parent->name, "item"))
1164 {
1165 if (!strcmp((const char*)cur_node->name, "title"))
1166 {
1167- xmlChar *title = xmlNodeGetContent(cur_node);
1168- row_num = filesList->append_text((const char*)title);
1169+ row_num = append_text("");
1170+ xmlChar *xml_title = xmlNodeGetContent(cur_node);
1171+ char* title = (char*) xml_title;
1172+
1173+ set_text(row_num, RESULTS_COLUMN_TITLE, title);
1174 xmlFree(title);
1175 }
1176-#ifdef WITH_GNOME_VFS
1177- else if (!strcmp((const char*)cur_node->name, "enclosure"))
1178+ else if (!strcmp((const char*)cur_node->name, "pubDate"))
1179 {
1180- xmlChar *urlattribute = xmlGetProp(cur_node, (xmlChar*)"url");
1181- filesList->set_text(row_num, 1, (const char*)urlattribute);
1182- gchar *tmp_file;
1183- tmp_file = gnome_vfs_uri_extract_short_path_name(gnome_vfs_uri_new((const char*)urlattribute));
1184- filesList->set_text(row_num, 2, (const char*)tmp_file);
1185- xmlFree(urlattribute);
1186+ xmlChar *xml_date = xmlNodeGetContent(cur_node);
1187+ char* date = (char*) xml_date;
1188+
1189+ set_text(row_num, RESULTS_COLUMN_DATE, date);
1190+ xmlFree(xml_date);
1191 }
1192 else if (!strcmp((const char*)cur_node->name, "creator"))
1193 {
1194- filesList->set_text(row_num, 3, (const char*)xmlNodeGetContent(cur_node));
1195+ xmlChar *xml_creator = xmlNodeGetContent(cur_node);
1196+ char* creator = (char*) xml_creator;
1197+
1198+ set_text(row_num, RESULTS_COLUMN_CREATOR, creator);
1199+ xmlFree(xml_creator);
1200 }
1201 else if (!strcmp((const char*)cur_node->name, "description"))
1202 {
1203- filesList->set_text(row_num, 4, (const char*)xmlNodeGetContent(cur_node));
1204- }
1205-#endif
1206+ xmlChar *xml_description = xmlNodeGetContent(cur_node);
1207+ //char* final_description;
1208+ char* stripped_description = g_strstrip((char*) xml_description);
1209+
1210+ if (!strcmp(stripped_description, "")) {
1211+ stripped_description = _("No description");
1212+ }
1213+
1214+ //GRegex* regex = g_regex_new(g_regex_escape_string(stripped_description, -1));
1215+ //final_description = g_regex_replace_literal(regex, "\n", -1, 0, " ");
1216+
1217+ set_text(row_num, RESULTS_COLUMN_DESCRIPTION, stripped_description);
1218+ xmlFree(xml_description);
1219+ }
1220+ else if (!strcmp((const char*)cur_node->name, "enclosure"))
1221+ {
1222+ xmlChar *xml_url = xmlGetProp(cur_node, (xmlChar*) "url");
1223+ char* url = (char*) xml_url;
1224+ char* filename = g_path_get_basename(url);
1225+
1226+ set_text(row_num, RESULTS_COLUMN_URL, url);
1227+ set_text(row_num, RESULTS_COLUMN_FILENAME, filename);
1228+ xmlFree(xml_url);
1229+ }
1230+ else if (!strcmp((const char*)cur_node->name, "thumbnail"))
1231+ {
1232+ xmlChar *xml_thumbnail_url = xmlGetProp(cur_node, (xmlChar*) "url");
1233+ char* thumbnail_url = (char*) xml_thumbnail_url;
1234+ char* thumbnail_filename = g_path_get_basename(thumbnail_url);
1235+
1236+ set_text(row_num, RESULTS_COLUMN_THUMBNAIL_URL, thumbnail_url);
1237+ set_text(row_num, RESULTS_COLUMN_THUMBNAIL_FILENAME, thumbnail_filename);
1238+ xmlFree(xml_thumbnail_url);
1239+ }
1240+ else if (!strcmp((const char*)cur_node->name, "guid"))
1241+ {
1242+ xmlChar *xml_guid = xmlNodeGetContent(cur_node);
1243+ char* guid_url = (char*) xml_guid;
1244+ char* guid = g_path_get_basename(guid_url);
1245+
1246+ set_text(row_num, RESULTS_COLUMN_GUID, guid);
1247+ xmlFree(xml_guid);
1248+ }
1249 }
1250- print_xml_element_names(cur_node->children);
1251- }
1252+ populate_from_xml(cur_node->children);
1253+ }
1254+}
1255+
1256+/**
1257+ * Callback for user input into entry_search
1258+ */
1259+void ImportDialog::on_button_search_clicked()
1260+{
1261+ on_entry_search_activated();
1262+}
1263+
1264+void ImportDialog::on_button_close_clicked()
1265+{
1266+ hide();
1267+}
1268+
1269+void ImportDialog::on_button_cancel_clicked()
1270+{
1271+ cancellable_image->cancel();
1272+ cancelled_image = true;
1273+}
1274+
1275+/**
1276+ * Callback for user input into entry_search
1277+ */
1278+void ImportDialog::on_entry_search_activated()
1279+{
1280+ preview_files->clear();
1281+ widget_status->start_process(_("Searching clipart..."));
1282+
1283+ notebook_content->set_current_page(NOTEBOOK_PAGE_LOGO);
1284+
1285+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1286+
1287+ Glib::ustring search_keywords = entry_search->get_text();
1288+
1289+ // Create the URI to the OCAL RSS feed
1290+ Glib::ustring xml_uri = Glib::ustring::compose("http://%1/media/feed/rss/%2",
1291+ prefs->getString("/options/ocalurl/str"), search_keywords);
1292+ // If we are not UTF8
1293+ if (!Glib::get_charset()) {
1294+ xml_uri = Glib::filename_to_utf8(xml_uri);
1295+ }
1296+
1297+ // Open the RSS feed
1298+ Glib::RefPtr<Gio::File> xml_file = Gio::File::create_for_uri(xml_uri);
1299+
1300+ xml_file->load_contents_async(
1301+ sigc::bind<Glib::RefPtr<Gio::File> , Glib::ustring>(
1302+ sigc::mem_fun(*this, &ImportDialog::on_xml_file_read),
1303+ xml_file, xml_uri)
1304+ );
1305+}
1306+
1307+void ImportDialog::on_xml_file_read(const Glib::RefPtr<Gio::AsyncResult>& result,
1308+ Glib::RefPtr<Gio::File> xml_file, Glib::ustring xml_uri)
1309+{
1310+ widget_status->end_process();
1311+
1312+ char* data;
1313+ gsize length;
1314+
1315+ bool sucess = xml_file->load_contents_finish(result, data, length);
1316+ if (!sucess) {
1317+ widget_status->set_error(_("Could not connect to the Open Clip Art Library"));
1318+ return;
1319+ }
1320+
1321+ // Create the resulting xml document tree
1322+ // Initialize libxml and test mistakes between compiled and shared library used
1323+ LIBXML_TEST_VERSION
1324+ xmlDoc *doc = NULL;
1325+ xmlNode *root_element = NULL;
1326+
1327+ doc = xmlReadMemory(data, (int) length, xml_uri.c_str(), NULL,
1328+ XML_PARSE_RECOVER + XML_PARSE_NOWARNING + XML_PARSE_NOERROR);
1329+
1330+ if (doc == NULL) {
1331+ // If nothing is returned, no results could be found
1332+ if (length == 0) {
1333+ notebook_content->set_current_page(NOTEBOOK_PAGE_NOT_FOUND);
1334+ update_label_no_search_results();
1335+ } else {
1336+ widget_status->set_error(_("Could not parse search results"));
1337+ }
1338+ return;
1339+ }
1340+
1341+ // Get the root element node
1342+ root_element = xmlDocGetRootElement(doc);
1343+
1344+ // Clear and populate the list_results
1345+ list_results->clear_items();
1346+ list_results->populate_from_xml(root_element);
1347+
1348+ // Populate the MARKUP column with the title & description of the clipart
1349+ for (guint i = 0; i <= list_results->size() - 1; i++) {
1350+ Glib::ustring title = list_results->get_text(i, RESULTS_COLUMN_TITLE);
1351+ Glib::ustring description = list_results->get_text(i, RESULTS_COLUMN_DESCRIPTION);
1352+ char* markup = g_markup_printf_escaped("<b>%s</b>\n<span size=\"small\">%s</span>",
1353+ title.c_str(), description.c_str());
1354+ list_results->set_text(i, RESULTS_COLUMN_MARKUP, markup);
1355+ }
1356+ notebook_content->set_current_page(NOTEBOOK_PAGE_RESULTS);
1357+
1358+ // free the document
1359+ xmlFreeDoc(doc);
1360+ // free the global variables that may have been allocated by the parser
1361+ xmlCleanupParser();
1362+}
1363+
1364+
1365+void ImportDialog::update_label_no_search_results()
1366+{
1367+ Glib::ustring keywords = Glib::Markup::escape_text(entry_search->get_text());
1368+ Gdk::Color grey = entry_search->get_style()->get_text_aa(entry_search->get_state());
1369+
1370+ Glib::ustring markup = Glib::ustring::compose(
1371+ "<span size=\"large\">%1 <b>%2</b> %3</span>\n<span color=\"%4\">%5</span>",
1372+ _("No clipart named"), keywords, _("was found."), grey.to_string(),
1373+ _("Please make sure all keywords are spelled correctly, or try again with different keywords."));
1374+
1375+ label_not_found->set_markup(markup);
1376 }
1377
1378 /**
1379 * Constructor. Not called directly. Use the factory.
1380 */
1381-FileImportFromOCALDialog::FileImportFromOCALDialog(Gtk::Window& parentWindow,
1382- const Glib::ustring &/*dir*/,
1383- FileDialogType fileTypes,
1384+ImportDialog::ImportDialog(Gtk::Window& parent_window, FileDialogType file_types,
1385 const Glib::ustring &title) :
1386- FileDialogOCALBase(title, parentWindow)
1387+ FileDialogBase(title, parent_window)
1388 {
1389+ printf("%s\n", "Hi!");
1390 // Initalize to Autodetect
1391 extension = NULL;
1392 // No filename to start out with
1393- Glib::ustring searchTag = "";
1394-
1395- dialogType = fileTypes;
1396- Gtk::VBox *vbox = get_vbox();
1397- Gtk::Label *tagLabel = new Gtk::Label(_("Search for:"));
1398- notFoundLabel = new Gtk::Label(_("No files matched your search"));
1399- descriptionLabel = new Gtk::Label();
1400- descriptionLabel->set_max_width_chars(260);
1401- descriptionLabel->set_size_request(500, -1);
1402- descriptionLabel->set_single_line_mode(false);
1403- descriptionLabel->set_line_wrap(true);
1404- messageBox.pack_start(*notFoundLabel);
1405- descriptionBox.pack_start(*descriptionLabel);
1406- searchTagEntry = new Gtk::Entry();
1407- searchTagEntry->set_text(searchTag);
1408- searchTagEntry->set_max_length(255);
1409- searchButton = new Gtk::Button(_("Search"));
1410- tagBox.pack_start(*tagLabel);
1411- tagBox.pack_start(*searchTagEntry, Gtk::PACK_EXPAND_WIDGET, 3);
1412- tagBox.pack_start(*searchButton);
1413- filesPreview = new SVGPreview();
1414- filesPreview->showNoPreview();
1415- // add the buttons in the bottom of the dialog
1416- add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1417- okButton = add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
1418- // sets the okbutton to default
1419- set_default(*okButton);
1420- filesList = new FileListViewText(5, *filesPreview, *descriptionLabel, *okButton);
1421- filesList->set_sensitive(false);
1422- // add the listview inside a ScrolledWindow
1423- listScrolledWindow.add(*filesList);
1424- // only show the scrollbars when they are necessary:
1425- listScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
1426- filesList->set_column_title(0, _("Files found"));
1427- listScrolledWindow.set_size_request(400, 180);
1428- filesList->get_column(1)->set_visible(false); // file url
1429- filesList->get_column(2)->set_visible(false); // tmp file path
1430- filesList->get_column(3)->set_visible(false); // author dir
1431- filesList->get_column(4)->set_visible(false); // file description
1432- filesBox.pack_start(listScrolledWindow);
1433- filesBox.pack_start(*filesPreview);
1434- vbox->pack_start(tagBox, false, false);
1435- vbox->pack_start(messageBox);
1436- vbox->pack_start(filesBox);
1437- vbox->pack_start(descriptionBox);
1438-
1439- //Let's do some customization
1440- searchTagEntry = NULL;
1441- Gtk::Container *cont = get_toplevel();
1442- std::vector<Gtk::Entry *> entries;
1443- findEntryWidgets(cont, entries);
1444- if (entries.size() >=1 )
1445- {
1446- //Catch when user hits [return] on the text field
1447- searchTagEntry = entries[0];
1448- searchTagEntry->signal_activate().connect(
1449- sigc::mem_fun(*this, &FileImportFromOCALDialog::searchTagEntryChangedCallback));
1450- }
1451-
1452- searchButton->signal_clicked().connect(
1453- sigc::mem_fun(*this, &FileImportFromOCALDialog::searchTagEntryChangedCallback));
1454+ Glib::ustring search_keywords = "";
1455+
1456+ dialogType = file_types;
1457+
1458+ // Creation
1459+ Gtk::VBox *vbox = new Gtk::VBox(false, 0);
1460+ Gtk::HButtonBox *hbuttonbox_bottom = new Gtk::HButtonBox();
1461+ Gtk::HBox *hbox_bottom = new Gtk::HBox(false, 12);
1462+ BaseBox *basebox_logo = new BaseBox();
1463+ BaseBox *basebox_no_search_results = new BaseBox();
1464+ label_not_found = new Gtk::Label();
1465+ label_description = new Gtk::Label();
1466+ entry_search = new SearchEntry();
1467+ button_search = new Gtk::Button(_("Search"));
1468+ Gtk::HButtonBox* hbuttonbox_search = new Gtk::HButtonBox();
1469+ Gtk::ScrolledWindow* scrolledwindow_preview = new Gtk::ScrolledWindow();
1470+ preview_files = new PreviewWidget();
1471+ /// Add the buttons in the bottom of the dialog
1472+ button_cancel = new Gtk::Button(Gtk::Stock::CANCEL);
1473+ button_close = new Gtk::Button(_("Close"));
1474+ button_import = new Gtk::Button(_("Import"));
1475+ list_results = new SearchResultList(RESULTS_COLUMN_LENGTH);
1476+ drawingarea_logo = new LogoArea();
1477+ notebook_content = new Gtk::Notebook();
1478+ widget_status = new StatusWidget();
1479+
1480+ downloading_thumbnail = false;
1481+ cancelled_thumbnail = false;
1482+ cancelled_image = false;
1483+
1484+ // Packing
1485+ add(*vbox);
1486+ vbox->pack_start(hbox_tags, false, false);
1487+ vbox->pack_start(hbox_files, true, true);
1488+ vbox->pack_start(*hbox_bottom, false, false);
1489+ basebox_logo->add(*drawingarea_logo);
1490+ basebox_no_search_results->add(*label_not_found);
1491+ hbox_bottom->pack_start(*widget_status, true, true);
1492+ hbox_bottom->pack_start(*hbuttonbox_bottom, true, true);
1493+ hbuttonbox_bottom->pack_start(*button_cancel, false, false);
1494+ hbuttonbox_bottom->pack_start(*button_close, false, false);
1495+ hbuttonbox_bottom->pack_start(*button_import, false, false);
1496+ hbuttonbox_search->pack_start(*button_search, false, false);
1497+ hbox_tags.pack_start(*entry_search, true, true);
1498+ hbox_tags.pack_start(*hbuttonbox_search, false, false);
1499+ hbox_files.pack_start(*notebook_content, true, true);
1500+ scrolledwindow_preview->add(*preview_files);
1501+ hbox_files.pack_start(*scrolledwindow_preview, true, true);
1502+
1503+ notebook_content->insert_page(*basebox_logo, NOTEBOOK_PAGE_LOGO);
1504+ notebook_content->insert_page(scrolledwindow_list, NOTEBOOK_PAGE_RESULTS);
1505+ notebook_content->insert_page(*basebox_no_search_results, NOTEBOOK_PAGE_NOT_FOUND);
1506+
1507+ // Properties
1508+ set_border_width(12);
1509+ set_default_size(480, 330);
1510+ vbox->set_spacing(12);
1511+ hbuttonbox_bottom->set_spacing(6);
1512+ hbuttonbox_bottom->set_layout(Gtk::BUTTONBOX_END);
1513+ button_import->set_sensitive(false);
1514+ entry_search->set_max_length(255);
1515+ hbox_tags.set_spacing(6);
1516+ preview_files->clear();
1517+ notebook_content->set_current_page(NOTEBOOK_PAGE_LOGO);
1518+ /// Add the listview inside a ScrolledWindow
1519+ scrolledwindow_list.add(*list_results);
1520+ scrolledwindow_list.set_shadow_type(Gtk::SHADOW_IN);
1521+ /// Only show the scrollbars when they are necessary
1522+ scrolledwindow_list.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
1523+ preview_files->set_size_request(120, -1);
1524+ hbox_files.set_spacing(12);
1525+ label_not_found->set_line_wrap(true);
1526+ label_not_found->set_line_wrap_mode(Pango::WRAP_WORD);
1527+ label_not_found->set_justify(Gtk::JUSTIFY_CENTER);
1528+ label_not_found->set_size_request(260, -1);
1529+ scrolledwindow_preview->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1530+ notebook_content->set_show_tabs(false);
1531+ notebook_content->set_show_border(false);
1532+ button_cancel->set_no_show_all(true);
1533+ button_close->set_no_show_all(true);
1534+ button_close->show();
1535+ button_cancel->hide();
1536+
1537+ // Signals
1538+ entry_search->signal_activate().connect(
1539+ sigc::mem_fun(*this, &ImportDialog::on_entry_search_activated));
1540+ button_import->signal_clicked().connect(
1541+ sigc::mem_fun(*this, &ImportDialog::on_button_import_clicked));
1542+ button_close->signal_clicked().connect(
1543+ sigc::mem_fun(*this, &ImportDialog::on_button_close_clicked));
1544+ button_cancel->signal_clicked().connect(
1545+ sigc::mem_fun(*this, &ImportDialog::on_button_cancel_clicked));
1546+ button_search->signal_clicked().connect(
1547+ sigc::mem_fun(*this, &ImportDialog::on_button_search_clicked));
1548+ list_results->signal_cursor_changed().connect(
1549+ sigc::mem_fun(*this, &ImportDialog::on_list_results_cursor_changed));
1550+ list_results->signal_row_activated().connect(
1551+ sigc::mem_fun(*this, &ImportDialog::on_list_results_row_activated));
1552+ list_results->get_selection()->signal_changed().connect(
1553+ sigc::mem_fun(*this, &ImportDialog::on_list_results_selection_changed));
1554
1555 show_all_children();
1556- notFoundLabel->hide();
1557+ entry_search->grab_focus();
1558+
1559+ // Create the temporary directories that will be needed later
1560+ create_temporary_dirs();
1561 }
1562
1563 /**
1564 * Destructor
1565 */
1566-FileImportFromOCALDialog::~FileImportFromOCALDialog()
1567-{
1568-
1569-}
1570-
1571-/**
1572- * Show this dialog modally. Return true if user hits [OK]
1573- */
1574-bool
1575-FileImportFromOCALDialog::show()
1576-{
1577- set_modal (TRUE); //Window
1578- sp_transientize((GtkWidget *)gobj()); //Make transient
1579- gint b = run(); //Dialog
1580- hide();
1581-
1582- if (b == Gtk::RESPONSE_OK)
1583- {
1584- return TRUE;
1585- }
1586- else
1587- {
1588- return FALSE;
1589- }
1590-}
1591-
1592+ImportDialog::~ImportDialog()
1593+{
1594+
1595+}
1596
1597 /**
1598 * Get the file extension type that was selected by the user. Valid after an [OK]
1599 */
1600 Inkscape::Extension::Extension *
1601-FileImportFromOCALDialog::getSelectionType()
1602+ImportDialog::get_selection_type()
1603 {
1604 return extension;
1605 }
1606
1607-
1608-/**
1609- * Get the file name chosen by the user. Valid after an [OK]
1610- */
1611-Glib::ustring
1612-FileImportFromOCALDialog::getFilename (void)
1613+ImportDialog::type_signal_response ImportDialog::signal_response()
1614 {
1615- return filesList->getFilename();
1616+ return m_signal_response;
1617 }
1618
1619
1620+} //namespace OCAL
1621 } //namespace Dialog
1622 } //namespace UI
1623 } //namespace Inkscape
1624
1625-
1626-
1627 /*
1628 Local Variables:
1629 mode:c++
1630
1631=== modified file 'src/ui/dialog/ocaldialogs.h'
1632--- src/ui/dialog/ocaldialogs.h 2011-06-03 10:44:52 +0000
1633+++ src/ui/dialog/ocaldialogs.h 2011-12-27 21:09:25 +0000
1634@@ -4,6 +4,7 @@
1635 /* Authors:
1636 * Bruno Dilly <bruno.dilly@gmail.com>
1637 * Inkscape Guys
1638+ * Andrew Higginson
1639 *
1640 * Copyright (C) 2007 Bruno Dilly <bruno.dilly@gmail.com>
1641 * Released under GNU GPL, read the file 'COPYING' for more information
1642@@ -52,6 +53,7 @@
1643 //For export dialog
1644 #include "ui/widget/scalar-unit.h"
1645
1646+#include <dialogs/dialog-events.h>
1647
1648 namespace Inkscape
1649 {
1650@@ -59,7 +61,8 @@
1651 {
1652 namespace Dialog
1653 {
1654-
1655+namespace OCAL
1656+{
1657 /*#########################################################################
1658 ### F I L E D I A L O G O C A L B A S E C L A S S
1659 #########################################################################*/
1660@@ -67,20 +70,32 @@
1661 /**
1662 * This class is the base implementation for export to OCAL.
1663 */
1664-class FileDialogOCALBase : public Gtk::Dialog
1665+class FileDialogBase : public Gtk::Window
1666 {
1667 public:
1668
1669 /**
1670 * Constructor
1671 */
1672- FileDialogOCALBase(const Glib::ustring &title, Gtk::Window& parent) : Gtk::Dialog(title, parent, true)
1673- {}
1674+ FileDialogBase(const Glib::ustring &title, Gtk::Window& parent) : Gtk::Window(Gtk::WINDOW_TOPLEVEL)
1675+ {
1676+ set_title(title);
1677+ sp_transientize((GtkWidget*) gobj());
1678+
1679+ // Allow shrinking of window so labels wrap correctly
1680+ set_resizable(true);
1681+
1682+ Gdk::Geometry geom;
1683+ geom.min_width = 480;
1684+ geom.min_height = 320;
1685+
1686+ set_geometry_hints(*this, geom, Gdk::HINT_MIN_SIZE);
1687+ }
1688
1689 /*
1690 * Destructor
1691 */
1692- virtual ~FileDialogOCALBase()
1693+ virtual ~FileDialogBase()
1694 {}
1695
1696 protected:
1697@@ -101,10 +116,10 @@
1698
1699
1700 /**
1701- * Our implementation of the FileExportToOCALDialog interface.
1702+ * Our implementation of the ExportDialog interface.
1703 */
1704 /*
1705-class FileExportToOCALDialog : public FileDialogOCALBase
1706+class ExportDialog : public FileDialogBase
1707 {
1708
1709 public:
1710@@ -116,7 +131,7 @@
1711 * @param key a list of file types from which the user can select
1712 */
1713 /*
1714- FileExportToOCALDialog(Gtk::Window& parentWindow,
1715+ ExportDialog(Gtk::Window& parentWindow,
1716 FileDialogType fileTypes,
1717 const Glib::ustring &title);
1718 */
1719@@ -125,7 +140,7 @@
1720 * Perform any necessary cleanups.
1721 */
1722 /*
1723- ~FileExportToOCALDialog();
1724+ ~ExportDialog();
1725 */
1726 /**
1727 * Show an SaveAs file selector.
1728@@ -181,7 +196,7 @@
1729 /*
1730 std::set<Glib::ustring> knownExtensions;
1731
1732-}; //FileExportToOCAL
1733+}; //ExportDialog
1734 */
1735
1736 //########################################################################
1737@@ -190,10 +205,10 @@
1738
1739
1740 /**
1741- * Our implementation of the FileExportToOCALPasswordDialog interface.
1742+ * Our implementation of the ExportPasswordDialog interface.
1743 */
1744 /*
1745-class FileExportToOCALPasswordDialog : public FileDialogOCALBase
1746+class ExportPasswordDialog : public FileDialogBase
1747 {
1748
1749 public:
1750@@ -203,7 +218,7 @@
1751 * @param title the title of the dialog
1752 */
1753 /*
1754- FileExportToOCALPasswordDialog(Gtk::Window& parentWindow,
1755+ ExportPasswordDialog(Gtk::Window& parentWindow,
1756 const Glib::ustring &title);
1757 */
1758 /**
1759@@ -211,7 +226,7 @@
1760 * Perform any necessary cleanups.
1761 */
1762 /*
1763- ~FileExportToOCALPasswordDialog();
1764+ ~ExportPasswordDialog();
1765 */
1766
1767 /**
1768@@ -246,7 +261,7 @@
1769 Gtk::HBox userBox;
1770 Gtk::HBox passBox;
1771
1772-}; //FileExportToOCALPassword
1773+}; //ExportPasswordDialog
1774 */
1775
1776
1777@@ -254,34 +269,147 @@
1778 //### F I L E I M P O R T F R O M O C A L
1779 //#########################################################################
1780
1781-/**
1782- * Our implementation class for filesListView
1783- */
1784-class FileListViewText : public Gtk::ListViewText
1785-{
1786-public:
1787- FileListViewText(guint columns_count, SVGPreview& filesPreview, Gtk::Label& description, Gtk::Button& okButton)
1788- :ListViewText(columns_count)
1789- {
1790- myPreview = &filesPreview;
1791- myLabel = &description;
1792- myButton = &okButton;
1793- }
1794- Glib::ustring getFilename();
1795-protected:
1796- void on_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column);
1797- void on_cursor_changed();
1798-private:
1799- Glib::ustring myFilename;
1800- SVGPreview *myPreview;
1801- Gtk::Label *myLabel;
1802- Gtk::Button *myButton;
1803-};
1804-
1805-/**
1806- * Our implementation class for the FileImportFromOCALDialog interface..
1807- */
1808-class FileImportFromOCALDialog : public FileDialogOCALBase
1809+class WrapLabel : public Gtk::Label
1810+{
1811+public:
1812+ WrapLabel();
1813+
1814+private:
1815+ void _on_size_allocate(Gtk::Allocation& allocation);
1816+};
1817+
1818+class LoadingBox : public Gtk::EventBox
1819+{
1820+public:
1821+ LoadingBox();
1822+
1823+ void start();
1824+ void stop();
1825+
1826+private:
1827+ unsigned int spinner_step;
1828+ sigc::connection timeout;
1829+ bool draw_spinner;
1830+ bool _on_expose_event(GdkEventExpose* event);
1831+ bool on_timeout();
1832+};
1833+
1834+class PreviewWidget : public Gtk::VBox
1835+{
1836+public:
1837+ PreviewWidget();
1838+
1839+ void set_metadata(Glib::ustring description, Glib::ustring creator, Glib::ustring time);
1840+ void show_box_loading();
1841+ void hide_box_loading();
1842+ void set_image(std::string path);
1843+ void clear();
1844+ bool _on_expose_event(GdkEventExpose* event);
1845+
1846+private:
1847+ LoadingBox* box_loading;
1848+ Gtk::Image* image;
1849+
1850+ WrapLabel* label_title;
1851+ WrapLabel* label_description;
1852+ WrapLabel* label_time;
1853+};
1854+
1855+/**
1856+ * A Widget that contains an status icon and a message
1857+ */
1858+class StatusWidget : public Gtk::HBox
1859+{
1860+public:
1861+ StatusWidget();
1862+
1863+ void clear();
1864+ void set_error(Glib::ustring text);
1865+ void set_info(Glib::ustring text);
1866+ void start_process(Glib::ustring text);
1867+ void end_process();
1868+
1869+ Gtk::Spinner* spinner;
1870+ Gtk::Image* image;
1871+ Gtk::Label* label;
1872+};
1873+
1874+/**
1875+ * A Gtk::Entry with search & clear icons
1876+ */
1877+class SearchEntry : public Gtk::Entry
1878+{
1879+public:
1880+ SearchEntry();
1881+
1882+private:
1883+ void _on_icon_pressed(Gtk::EntryIconPosition icon_position, const GdkEventButton* event);
1884+ void _on_changed();
1885+};
1886+
1887+/**
1888+ * A box which paints an overlay of the OCAL logo
1889+ */
1890+class LogoArea : public Gtk::EventBox
1891+{
1892+public:
1893+ LogoArea();
1894+private:
1895+ bool _on_expose_event(GdkEventExpose* event);
1896+ bool draw_logo;
1897+ Cairo::RefPtr<Cairo::ImageSurface> logo_mask;
1898+};
1899+
1900+/**
1901+ * A box filled with the Base colour from the user's GTK theme, and a border
1902+ */
1903+class BaseBox : public Gtk::EventBox
1904+{
1905+public:
1906+ BaseBox();
1907+private:
1908+ bool _on_expose_event(GdkEventExpose* event);
1909+};
1910+
1911+enum {
1912+ RESULTS_COLUMN_MARKUP,
1913+ RESULTS_COLUMN_TITLE,
1914+ RESULTS_COLUMN_DESCRIPTION,
1915+ RESULTS_COLUMN_CREATOR,
1916+ RESULTS_COLUMN_DATE,
1917+ RESULTS_COLUMN_FILENAME,
1918+ RESULTS_COLUMN_THUMBNAIL_FILENAME,
1919+ RESULTS_COLUMN_URL,
1920+ RESULTS_COLUMN_THUMBNAIL_URL,
1921+ RESULTS_COLUMN_GUID,
1922+ RESULTS_COLUMN_LENGTH,
1923+};
1924+
1925+enum {
1926+ NOTEBOOK_PAGE_LOGO,
1927+ NOTEBOOK_PAGE_RESULTS,
1928+ NOTEBOOK_PAGE_NOT_FOUND,
1929+};
1930+
1931+enum ResourceType {
1932+ TYPE_THUMBNAIL,
1933+ TYPE_IMAGE,
1934+};
1935+
1936+/**
1937+ * The TreeView which holds the search results
1938+ */
1939+class SearchResultList : public Gtk::ListViewText
1940+{
1941+public:
1942+ SearchResultList(guint columns_count);
1943+ void populate_from_xml(xmlNode* a_node);
1944+};
1945+
1946+/**
1947+ * The Import Dialog
1948+ */
1949+class ImportDialog : public FileDialogBase
1950 {
1951 public:
1952 /**
1953@@ -290,16 +418,14 @@
1954 * @param fileTypes one of FileDialogTypes
1955 * @param title the title of the dialog
1956 */
1957- FileImportFromOCALDialog(Gtk::Window& parentWindow,
1958- const Glib::ustring &dir,
1959- FileDialogType fileTypes,
1960- const Glib::ustring &title);
1961+ ImportDialog(Gtk::Window& parent_window, FileDialogType file_types,
1962+ const Glib::ustring &title);
1963
1964 /**
1965 * Destructor.
1966 * Perform any necessary cleanups.
1967 */
1968- ~FileImportFromOCALDialog();
1969+ ~ImportDialog();
1970
1971 /**
1972 * Show an OpenFile file selector.
1973@@ -312,51 +438,73 @@
1974 * @return a pointer to a string if successful (which must
1975 * be later freed with g_free(), else NULL.
1976 */
1977- Inkscape::Extension::Extension *getSelectionType();
1978-
1979- Glib::ustring getFilename();
1980+ Inkscape::Extension::Extension *get_selection_type();
1981+
1982+ typedef sigc::signal<void, Glib::ustring> type_signal_response;
1983+ type_signal_response signal_response();
1984+
1985+protected:
1986+ type_signal_response m_signal_response;
1987
1988 private:
1989-
1990- /**
1991- * Allow the user to type the tag to be searched
1992- */
1993- Gtk::Entry *searchTagEntry;
1994- FileListViewText *filesList;
1995- SVGPreview *filesPreview;
1996- Gtk::Label *notFoundLabel;
1997- Gtk::Label *descriptionLabel;
1998- Gtk::Button *searchButton;
1999- Gtk::Button *okButton;
2000+ Glib::ustring filename_image;
2001+ Glib::ustring filename_thumbnail;
2002+ SearchEntry *entry_search;
2003+ LogoArea *drawingarea_logo;
2004+ SearchResultList *list_results;
2005+ PreviewWidget *preview_files;
2006+ Gtk::Label *label_not_found;
2007+ Gtk::Label *label_description;
2008+ Gtk::Button *button_search;
2009+ Gtk::Button *button_import;
2010+ Gtk::Button *button_close;
2011+ Gtk::Button *button_cancel;
2012+ StatusWidget *widget_status;
2013
2014 // Child widgets
2015- Gtk::HBox tagBox;
2016- Gtk::HBox filesBox;
2017- Gtk::HBox messageBox;
2018- Gtk::HBox descriptionBox;
2019- Gtk::ScrolledWindow listScrolledWindow;
2020+ Gtk::Notebook *notebook_content;
2021+ Gtk::HBox hbox_tags;
2022+ Gtk::HBox hbox_files;
2023+ Gtk::ScrolledWindow scrolledwindow_list;
2024 Glib::RefPtr<Gtk::TreeSelection> selection;
2025
2026- /**
2027- * Callback for user input into searchTagEntry
2028- */
2029- void searchTagEntryChangedCallback();
2030-
2031-
2032- /**
2033- * Prints the names of the all the xml elements
2034- * that are siblings or children of a given xml node
2035- */
2036- void print_xml_element_names(xmlNode * a_node);
2037+ Glib::RefPtr<Gio::Cancellable> cancellable_image;
2038+ Glib::RefPtr<Gio::Cancellable> cancellable_thumbnail;
2039+ bool downloading_thumbnail;
2040+ bool cancelled_thumbnail;
2041+ bool cancelled_image;
2042+
2043+ void update_label_no_search_results();
2044+ void update_preview(int row);
2045+ void on_list_results_cursor_changed();
2046+
2047+ void download_resource(ResourceType type, int row);
2048+ void on_resource_downloaded(const Glib::RefPtr<Gio::AsyncResult>& result,
2049+ Glib::RefPtr<Gio::File> file_remote, Glib::ustring path, ResourceType resource);
2050+ void on_image_downloaded(Glib::ustring path, bool success);
2051+ void on_thumbnail_downloaded(Glib::ustring path, bool success);
2052+ void on_list_results_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column);
2053+ void on_button_import_clicked();
2054+ void on_button_close_clicked();
2055+ void on_button_cancel_clicked();
2056+ void on_button_search_clicked();
2057+ void on_entry_search_activated();
2058+ void on_list_results_selection_changed();
2059+ void on_xml_file_read(const Glib::RefPtr<Gio::AsyncResult>& result,
2060+ Glib::RefPtr<Gio::File> xml_file, Glib::ustring xml_uri);
2061+ void create_temporary_dirs();
2062+ std::string get_temporary_dir(ResourceType type);
2063+
2064
2065 /**
2066 * The extension to use to write this file
2067 */
2068 Inkscape::Extension::Extension *extension;
2069
2070-}; //FileImportFromOCALDialog
2071-
2072-
2073+}; //ImportDialog
2074+
2075+
2076+} //namespace OCAL
2077 } //namespace Dialog
2078 } //namespace UI
2079 } //namespace Inkscape
2080
2081=== modified file 'src/verbs.cpp'
2082--- src/verbs.cpp 2011-10-03 03:44:17 +0000
2083+++ src/verbs.cpp 2011-12-27 21:09:25 +0000
2084@@ -2192,8 +2192,9 @@
2085 N_("Import a bitmap or SVG image into this document"), INKSCAPE_ICON("document-import")),
2086 new FileVerb(SP_VERB_FILE_EXPORT, "FileExport", N_("_Export Bitmap..."),
2087 N_("Export this document or a selection as a bitmap image"), INKSCAPE_ICON("document-export")),
2088- new FileVerb(SP_VERB_FILE_IMPORT_FROM_OCAL, "FileImportFromOCAL", N_("Import From Open Clip Art Library"), N_("Import a document from Open Clip Art Library"), INKSCAPE_ICON("document-import-ocal")),
2089-// new FileVerb(SP_VERB_FILE_EXPORT_TO_OCAL, "FileExportToOCAL", N_("Export To Open Clip Art Library"), N_("Export this document to Open Clip Art Library"), INKSCAPE_ICON("document-export-ocal")),
2090+ new FileVerb(SP_VERB_FILE_IMPORT_FROM_OCAL, "FileImportFromOCAL", N_("Import Clip Art..."),
2091+ N_("Import clipart from Open Clip Art Library"), INKSCAPE_ICON("document-import-ocal")),
2092+// new FileVerb(SP_VERB_FILE_EXPORT_TO_OCAL, "FileExportToOCAL", N_("Export To Open Clip Art Library"), N_("Export this document to Open Clip Art Library"), INKSCAPE_ICON_DOCUMENT_EXPORT_OCAL),
2093 new FileVerb(SP_VERB_FILE_NEXT_DESKTOP, "NextWindow", N_("N_ext Window"),
2094 N_("Switch to the next document window"), INKSCAPE_ICON("window-next")),
2095 new FileVerb(SP_VERB_FILE_PREV_DESKTOP, "PrevWindow", N_("P_revious Window"),