Code

merging gsoc 2009 color management work by Felipe Sanches (a.k.a. JucaBlues)
[inkscape.git] / src / ui / dialog / swatches.cpp
index 1bc3e68821b6927bb94cf80741e6aa8277eae80b..1f708e3de3534c1c84f8387ecc391d7da91c0f2b 100644 (file)
 #include "sp-gradient.h"
 #include "sp-gradient-vector.h"
 #include "swatches.h"
+#include "style.h"
 #include "widgets/gradient-vector.h"
 #include "widgets/eek-preview.h"
+#include "display/nr-plain-stuff.h"
+#include "sp-gradient-reference.h"
+
+//#define USE_DOCUMENT_PALETTE 1
 
 namespace Inkscape {
 namespace UI {
 namespace Dialogs {
 
+#define VBLOCK 16
+
+void _loadPaletteFile( gchar const *filename );
+
+/**
+ * The color swatch you see on screen as a clickable box.
+ */
+class ColorItem : public Inkscape::UI::Previewable
+{
+    friend void _loadPaletteFile( gchar const *filename );
+public:
+    ColorItem( ege::PaintDef::ColorType type );
+    ColorItem( unsigned int r, unsigned int g, unsigned int b,
+               Glib::ustring& name );
+    virtual ~ColorItem();
+    ColorItem(ColorItem const &other);
+    virtual ColorItem &operator=(ColorItem const &other);
+    virtual Gtk::Widget* getPreview(PreviewStyle style,
+                                    ViewType view,
+                                    ::PreviewSize size,
+                                    guint ratio);
+    void buttonClicked(bool secondary = false);
+
+    void setState( bool fill, bool stroke );
+    bool isFill() { return _isFill; }
+    bool isStroke() { return _isStroke; }
+
+    ege::PaintDef def;
+    void* ptr;
+
+private:
+    static void _dropDataIn( GtkWidget *widget,
+                             GdkDragContext *drag_context,
+                             gint x, gint y,
+                             GtkSelectionData *data,
+                             guint info,
+                             guint event_time,
+                             gpointer user_data);
+
+    static void _dragGetColorData( GtkWidget *widget,
+                                   GdkDragContext *drag_context,
+                                   GtkSelectionData *data,
+                                   guint info,
+                                   guint time,
+                                   gpointer user_data);
+
+    static void _wireMagicColors( void* p );
+    static void _colorDefChanged(void* data);
+
+    void _linkTint( ColorItem& other, int percent );
+    void _linkTone( ColorItem& other, int percent, int grayLevel );
+
+    Gtk::Tooltips tips;
+    std::vector<Gtk::Widget*> _previews;
+
+    bool _isFill;
+    bool _isStroke;
+    bool _isLive;
+    bool _linkIsTone;
+    int _linkPercent;
+    int _linkGray;
+    ColorItem* _linkSrc;
+    std::vector<ColorItem*> _listeners;
+};
+
+
 
 ColorItem::ColorItem(ege::PaintDef::ColorType type) :
     def(type),
     ptr(0),
+    _isFill(false),
+    _isStroke(false),
     _isLive(false),
     _linkIsTone(false),
     _linkPercent(0),
@@ -63,6 +136,8 @@ ColorItem::ColorItem(ege::PaintDef::ColorType type) :
 ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) :
     def( r, g, b, name ),
     ptr(0),
+    _isFill(false),
+    _isStroke(false),
     _isLive(false),
     _linkIsTone(false),
     _linkPercent(0),
@@ -95,6 +170,31 @@ ColorItem &ColorItem::operator=(ColorItem const &other)
     return *this;
 }
 
+void ColorItem::setState( bool fill, bool stroke )
+{
+    if ( (_isFill != fill) || (_isStroke != stroke) ) {
+        _isFill = fill;
+        _isStroke = stroke;
+
+        for ( std::vector<Gtk::Widget*>::iterator it = _previews.begin(); it != _previews.end(); ++it ) {
+            Gtk::Widget* widget = *it;
+            if ( IS_EEK_PREVIEW(widget->gobj()) ) {
+                EekPreview * preview = EEK_PREVIEW(widget->gobj());
+
+                int val = eek_preview_get_linked( preview );
+                val &= ~(PREVIEW_FILL | PREVIEW_STROKE);
+                if ( _isFill ) {
+                    val |= PREVIEW_FILL;
+                }
+                if ( _isStroke ) {
+                    val |= PREVIEW_STROKE;
+                }
+                eek_preview_set_linked( preview, static_cast<LinkType>(val) );
+            }
+        }
+    }
+}
+
 
 class JustForNow
 {
@@ -112,6 +212,9 @@ static std::vector<JustForNow*> possible;
 static std::vector<std::string> mimeStrings;
 static std::map<std::string, guint> mimeToInt;
 
+static std::map<ColorItem*, guchar*> previewMap;
+static std::map<ColorItem*, SPGradient*> gradMap; // very temporary workaround.
+
 void ColorItem::_dragGetColorData( GtkWidget */*widget*/,
                                    GdkDragContext */*drag_context*/,
                                    GtkSelectionData *data,
@@ -148,6 +251,8 @@ static void dragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data
         using Inkscape::IO::Resource::get_path;
         using Inkscape::IO::Resource::ICONS;
         using Inkscape::IO::Resource::SYSTEM;
+        int width = 32;
+        int height = 24;
 
         if (item->def.getType() != ege::PaintDef::RGB){
             GError *error = NULL;
@@ -158,18 +263,35 @@ static void dragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data
                                                  &bytesRead,
                                                  &bytesWritten,
                                                  &error);
-            GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(localFilename, 32, 24, FALSE, &error);
+            GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(localFilename, width, height, FALSE, &error);
             g_free(localFilename);
             gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 );
-            return;
-        }
+        } else {
+            GdkPixbuf* pixbuf = 0;
+            if ( gradMap.find(item) == gradMap.end() ){
+                Glib::RefPtr<Gdk::Pixbuf> thumb = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, width, height );
+                guint32 fillWith = (0xff000000 & (item->def.getR() << 24))
+                    | (0x00ff0000 & (item->def.getG() << 16))
+                    | (0x0000ff00 & (item->def.getB() <<  8));
+                thumb->fill( fillWith );
+                pixbuf = thumb->gobj();
+            } else {
+                SPGradient* grad = gradMap[item];
+
+                guchar* px = g_new( guchar, 3 * height * width );
+                nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
 
-        Glib::RefPtr<Gdk::Pixbuf> thumb = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, 32, 24 );
-        guint32 fillWith = (0xff000000 & (item->def.getR() << 24))
-                         | (0x00ff0000 & (item->def.getG() << 16))
-                         | (0x0000ff00 & (item->def.getB() <<  8));
-        thumb->fill( fillWith );
-        gtk_drag_set_icon_pixbuf( dc, thumb->gobj(), 0, 0 );
+                sp_gradient_render_vector_block_rgb( grad,
+                                                     px, width, height, 3 * width,
+                                                     0, width, TRUE );
+
+                pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
+                                                   width, height, width * 3,
+                                                   0, // add delete function
+                                                   0 );
+            }
+            gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 );
+        }
     }
 
 }
@@ -244,6 +366,7 @@ static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
     }
 }
 
+#if USE_DOCUMENT_PALETTE
 static void editGradientImpl( SPGradient* gr )
 {
     if ( gr ) {
@@ -304,6 +427,7 @@ static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
         }
     }
 }
+#endif // USE_DOCUMENT_PALETTE
 
 static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gpointer user_data)
 {
@@ -331,6 +455,7 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event,
                               user_data);
             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
+#if USE_DOCUMENT_PALETTE
             child = gtk_separator_menu_item_new();
             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
             popupExtras.push_back(child);
@@ -349,7 +474,6 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event,
             gtk_widget_set_sensitive( child, FALSE );
 
             child = gtk_menu_item_new_with_label(_("Edit..."));
-            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
             g_signal_connect( G_OBJECT(child),
                               "activate",
                               G_CALLBACK(editGradient),
@@ -365,6 +489,7 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event,
             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
             //popupExtras.push_back(child);
             gtk_widget_set_sensitive( child, FALSE );
+#endif // USE_DOCUMENT_PALETTE
 
             gtk_widget_show_all(popupMenu);
         }
@@ -550,7 +675,20 @@ Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, ::PreviewS
         EekPreview * preview = EEK_PREVIEW(eekWidget);
         Gtk::Widget* newBlot = Glib::wrap(eekWidget);
 
-        eek_preview_set_color( preview, (def.getR() << 8) | def.getR(), (def.getG() << 8) | def.getG(), (def.getB() << 8) | def.getB());
+        if ( previewMap.find(this) == previewMap.end() ){
+            eek_preview_set_color( preview, (def.getR() << 8) | def.getR(),
+                                   (def.getG() << 8) | def.getG(),
+                                   (def.getB() << 8) | def.getB());
+        } else {
+            guchar* px = previewMap[this];
+            int width = 128;
+            int height = 16;
+            GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
+                                                          width, height, width * 3,
+                                                          0, // add delete function
+                                                          0 );
+            eek_preview_set_pixbuf( preview, pixbuf );
+        }
         if ( def.getType() != ege::PaintDef::RGB ) {
             using Inkscape::IO::Resource::get_path;
             using Inkscape::IO::Resource::ICONS;
@@ -720,11 +858,19 @@ void ColorItem::buttonClicked(bool secondary)
                 break;
             }
             case ege::PaintDef::RGB: {
-                gchar c[64];
-                guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff;
-                sp_svg_write_color(c, sizeof(c), rgba);
-
-                sp_repr_css_set_property( css, attrName, c );
+                Glib::ustring colorspec;
+                if ( gradMap.find(this) == gradMap.end() ){
+                    gchar c[64];
+                    guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff;
+                    sp_svg_write_color(c, sizeof(c), rgba);
+                    colorspec = c;
+                } else {
+                    SPGradient* grad = gradMap[this];
+                    colorspec = "url(#";
+                    colorspec += grad->id;
+                    colorspec += ")";
+                }
+                sp_repr_css_set_property( css, attrName, colorspec.c_str() );
                 descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch");
                 break;
             }
@@ -1108,6 +1254,7 @@ SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
     _clear->ptr = this;
     _remove = new ColorItem( ege::PaintDef::NONE );
     _remove->ptr = this;
+#if USE_DOCUMENT_PALETTE
     {
         JustForNow *docPalette = new JustForNow();
 
@@ -1116,6 +1263,7 @@ SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
 
         _ptr = docPalette;
     }
+#endif // USE_DOCUMENT_PALETTE
     loadEmUp();
     if ( !possible.empty() ) {
         JustForNow* first = 0;
@@ -1174,6 +1322,9 @@ SwatchesPanel::~SwatchesPanel()
 {
     _documentConnection.disconnect();
     _resourceConnection.disconnect();
+    _selChanged.disconnect();
+    _setModified.disconnect();
+    _subselChanged.disconnect();
 
     if ( _clear ) {
         delete _clear;
@@ -1202,15 +1353,25 @@ void SwatchesPanel::setDesktop( SPDesktop* desktop )
     if ( desktop != _currentDesktop ) {
         if ( _currentDesktop ) {
             _documentConnection.disconnect();
+            _selChanged.disconnect();
+            _setModified.disconnect();
+            _subselChanged.disconnect();
         }
 
         _currentDesktop = desktop;
 
         if ( desktop ) {
-            sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
+            _currentDesktop->selection->connectChanged(
+                sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
 
-            sigc::slot<void, SPDocument*> base2 = first;
+            _currentDesktop->selection->connectModified(
+                sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
+
+            _currentDesktop->connectToolSubselectionChanged(
+                sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
 
+            sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
+            sigc::slot<void, SPDocument*> base2 = first;
             sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
             _documentConnection = desktop->connectDocumentReplaced( slot2 );
 
@@ -1250,6 +1411,7 @@ void SwatchesPanel::handleGradientsChange()
         }
     }
 
+#if USE_DOCUMENT_PALETTE
     if ( _ptr ) {
         JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr);
         // TODO delete pointed to objects
@@ -1262,15 +1424,45 @@ void SwatchesPanel::handleGradientsChange()
                     sp_gradient_ensure_vector( grad );
                     SPGradientStop first = grad->vector.stops[0];
                     SPColor color = first.color;
-                    guint32 together = color.toRGBA32(0);
-
-                    Glib::ustring name( grad->id );
-                    unsigned int r = SP_RGBA32_R_U(together);
-                    unsigned int g = SP_RGBA32_G_U(together);
-                    unsigned int b = SP_RGBA32_B_U(together);
-                    ColorItem* item = new ColorItem( r, g, b, name );
-                    item->ptr = this;
-                    docPalette->_colors.push_back(item);
+                    guint32 together = color.toRGBA32(first.opacity);
+
+                    // At the moment we can't trust the count of 1 vs 2 stops.
+                    SPGradientStop second = (*it)->vector.stops[1];
+                    SPColor color2 = second.color;
+                    guint32 together2 = color2.toRGBA32(second.opacity);
+
+                    if ( (grad->vector.stops.size() <= 2) && (together == together2) ) {
+                        // Treat as solid-color
+                        Glib::ustring name( grad->id );
+                        unsigned int r = SP_RGBA32_R_U(together);
+                        unsigned int g = SP_RGBA32_G_U(together);
+                        unsigned int b = SP_RGBA32_B_U(together);
+                        ColorItem* item = new ColorItem( r, g, b, name );
+                        item->ptr = this;
+                        docPalette->_colors.push_back(item);
+                        gradMap[item] = grad;
+                    } else {
+                        // Treat as gradient
+                        Glib::ustring name( grad->id );
+                        unsigned int r = SP_RGBA32_R_U(together);
+                        unsigned int g = SP_RGBA32_G_U(together);
+                        unsigned int b = SP_RGBA32_B_U(together);
+                        ColorItem* item = new ColorItem( r, g, b, name );
+                        item->ptr = this;
+                        docPalette->_colors.push_back(item);
+
+                        gint width = 128;
+                        gint height = VBLOCK;
+                        guchar* px = g_new( guchar, 3 * height * width );
+                        nr_render_checkerboard_rgb( px, width, VBLOCK, 3 * width, 0, 0 );
+
+                        sp_gradient_render_vector_block_rgb( grad,
+                                                             px, width, height, 3 * width,
+                                                             0, width, TRUE );
+
+                        previewMap[item] = px;
+                        gradMap[item] = grad;
+                    }
                 }
             }
         }
@@ -1279,6 +1471,90 @@ void SwatchesPanel::handleGradientsChange()
             _rebuild();
         }
     }
+#endif // USE_DOCUMENT_PALETTE
+}
+
+void SwatchesPanel::_updateFromSelection()
+{
+#if USE_DOCUMENT_PALETTE
+    if ( _ptr ) {
+        JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr);
+
+        Glib::ustring fillId;
+        Glib::ustring strokeId;
+
+        SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
+        int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
+        switch (result) {
+            case QUERY_STYLE_SINGLE:
+            case QUERY_STYLE_MULTIPLE_AVERAGED:
+            case QUERY_STYLE_MULTIPLE_SAME:
+            {
+                if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
+                    SPPaintServer* server = tmpStyle->getFillPaintServer();
+                    if ( SP_IS_GRADIENT(server) ) {
+                        SPGradient* target = 0;
+                        SPGradient* grad = SP_GRADIENT(server);
+                        if (grad->repr->attribute("osb:paint")) {
+                            target = grad;
+                        } else if ( grad->ref ) {
+                            SPGradient *tmp = grad->ref->getObject();
+                            if ( tmp && tmp->repr->attribute("osb:paint") ) {
+                                target = tmp;
+                            }
+                        }
+                        if ( target ) {
+                            gchar const* id = target->repr->attribute("id");
+                            if ( id ) {
+                                fillId = id;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+
+        result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
+        switch (result) {
+            case QUERY_STYLE_SINGLE:
+            case QUERY_STYLE_MULTIPLE_AVERAGED:
+            case QUERY_STYLE_MULTIPLE_SAME:
+            {
+                if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
+                    SPPaintServer* server = tmpStyle->getStrokePaintServer();
+                    if ( SP_IS_GRADIENT(server) ) {
+                        SPGradient* target = 0;
+                        SPGradient* grad = SP_GRADIENT(server);
+                        if (grad->repr->attribute("osb:paint")) {
+                            target = grad;
+                        } else if ( grad->ref ) {
+                            SPGradient *tmp = grad->ref->getObject();
+                            if ( tmp && tmp->repr->attribute("osb:paint") ) {
+                                target = tmp;
+                            }
+                        }
+                        if ( target ) {
+                            gchar const* id = target->repr->attribute("id");
+                            if ( id ) {
+                                strokeId = id;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+        sp_style_unref(tmpStyle);
+
+        for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
+            ColorItem* item = *it;
+            bool isFill = (fillId == item->def.descr);
+            bool isStroke = (strokeId == item->def.descr);
+            item->setState( isFill, isStroke );
+        }
+    }
+#endif // USE_DOCUMENT_PALETTE
 }
 
 void SwatchesPanel::_handleAction( int setId, int itemId )