Code

e70cdaccf3963137624044858c81d29fc5ee805f
[inkscape.git] / src / ui / dialog / swatches.cpp
2 /** @file
3  * @brief Color swatches dialog
4  */
5 /* Authors:
6  *   Jon A. Cruz
7  *   John Bintz
8  *
9  * Copyright (C) 2005 Jon A. Cruz
10  * Copyright (C) 2008 John Bintz
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include <errno.h>
16 #include <map>
17 #include <algorithm>
19 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
20 #include <gtk/gtkdnd.h>
21 #include <gtk/gtkmenu.h>
22 #include <gtk/gtkmenuitem.h>
23 #include <gtk/gtkseparatormenuitem.h>
24 #include <glibmm/i18n.h>
25 #include <gdkmm/pixbuf.h>
27 #include "color-item.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "desktop-style.h"
31 #include "document.h"
32 #include "document-private.h"
33 #include "extension/db.h"
34 #include "inkscape.h"
35 #include "inkscape.h"
36 #include "io/sys.h"
37 #include "io/resource.h"
38 #include "message-context.h"
39 #include "path-prefix.h"
40 #include "preferences.h"
41 #include "sp-item.h"
42 #include "sp-gradient-fns.h"
43 #include "sp-gradient.h"
44 #include "sp-gradient-vector.h"
45 #include "swatches.h"
46 #include "style.h"
47 #include "ui/previewholder.h"
48 #include "widgets/desktop-widget.h"
49 #include "widgets/gradient-vector.h"
50 #include "widgets/eek-preview.h"
51 #include "display/nr-plain-stuff.h"
52 #include "sp-gradient-reference.h"
55 namespace Inkscape {
56 namespace UI {
57 namespace Dialogs {
59 #define VBLOCK 16
60 #define PREVIEW_PIXBUF_WIDTH 128
62 void _loadPaletteFile( gchar const *filename );
65 class DocTrack;
67 std::vector<SwatchPage*> possible;
68 static std::map<SPDocument*, SwatchPage*> docPalettes;
69 static std::vector<DocTrack*> docTrackings;
70 static std::map<SwatchesPanel*, SPDocument*> docPerPanel;
73 class SwatchesPanelHook : public SwatchesPanel
74 {
75 public:
76     static void convertGradient( GtkMenuItem *menuitem, gpointer userData );
77     static void deleteGradient( GtkMenuItem *menuitem, gpointer userData );
78 };
80 static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
81     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
82     if ( item ) {
83         item->buttonClicked(false);
84     }
85 }
87 static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
88     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
89     if ( item ) {
90         item->buttonClicked(true);
91     }
92 }
94 static GtkWidget* popupMenu = 0;
95 static GtkWidget *popupSubHolder = 0;
96 static GtkWidget *popupSub = 0;
97 static std::vector<Glib::ustring> popupItems;
98 static std::vector<GtkWidget*> popupExtras;
99 static ColorItem* bounceTarget = 0;
100 static SwatchesPanel* bouncePanel = 0;
102 static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
104     if ( bounceTarget ) {
105         handleClick( GTK_WIDGET(menuitem), bounceTarget );
106     }
109 static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
111     if ( bounceTarget ) {
112         handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
113     }
116 static void editGradientImpl( SPGradient* gr )
118     if ( gr ) {
119         GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
120         gtk_widget_show( dialog );
121     }
124 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
126     if ( bounceTarget ) {
127         SwatchesPanel* swp = bouncePanel;
128         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
129         SPDocument *doc = desktop ? desktop->doc() : 0;
130         if (doc) {
131             std::string targetName(bounceTarget->def.descr);
132             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
133             for (const GSList *item = gradients; item; item = item->next) {
134                 SPGradient* grad = SP_GRADIENT(item->data);
135                 if ( targetName == grad->getId() ) {
136                     editGradientImpl( grad );
137                     break;
138                 }
139             }
140         }
141     }
144 void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData )
146     if ( bounceTarget ) {
147         SwatchesPanel* swp = bouncePanel;
148         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
149         SPDocument *doc = desktop ? desktop->doc() : 0;
150         gint index = GPOINTER_TO_INT(userData);
151         if ( doc && (index >= 0) && (static_cast<guint>(index) < popupItems.size()) ) {
152             Glib::ustring targetName = popupItems[index];
154             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
155             for (const GSList *item = gradients; item; item = item->next) {
156                 SPGradient* grad = SP_GRADIENT(item->data);
157                 if ( targetName == grad->getId() ) {
158                     grad->setSwatch();
159                     sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT,
160                                      _("Add gradient stop"));
161                     break;
162                 }
163             }
164         }
165     }
168 void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ )
170     if ( bounceTarget ) {
171         SwatchesPanel* swp = bouncePanel;
172         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
173         SPDocument *doc = desktop ? desktop->doc() : 0;
174         if (doc) {
175             std::string targetName(bounceTarget->def.descr);
176             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
177             for (const GSList *item = gradients; item; item = item->next) {
178                 SPGradient* grad = SP_GRADIENT(item->data);
179                 if ( targetName == grad->getId() ) {
180                     //editGradientImpl( grad );
181                     grad->setSwatch(false);
182                     sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT,
183                                      _("Delete"));
184                     break;
185                 }
186             }
187         }
188     }
191 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
193     SwatchesPanel *swp = 0;
195     std::map<GtkWidget*, SwatchesPanel*> rawObjects;
196     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
197         rawObjects[GTK_WIDGET(it->first->gobj())] = it->first;
198     }
200     for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) {
201         if (rawObjects.find(curr) != rawObjects.end()) {
202             swp = rawObjects[curr];
203         }
204     }
206     return swp;
209 static void removeit( GtkWidget *widget, gpointer data )
211     gtk_container_remove( GTK_CONTAINER(data), widget );
214 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
216     gboolean handled = FALSE;
218     if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
219         SwatchesPanel* swp = findContainingPanel( widget );
221         if ( !popupMenu ) {
222             popupMenu = gtk_menu_new();
223             GtkWidget* child = 0;
225             //TRANSLATORS: An item in context menu on a colour in the swatches
226             child = gtk_menu_item_new_with_label(_("Set fill"));
227             g_signal_connect( G_OBJECT(child),
228                               "activate",
229                               G_CALLBACK(redirClick),
230                               user_data);
231             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
233             //TRANSLATORS: An item in context menu on a colour in the swatches
234             child = gtk_menu_item_new_with_label(_("Set stroke"));
236             g_signal_connect( G_OBJECT(child),
237                               "activate",
238                               G_CALLBACK(redirSecondaryClick),
239                               user_data);
240             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
242             child = gtk_separator_menu_item_new();
243             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
244             popupExtras.push_back(child);
246             child = gtk_menu_item_new_with_label(_("Delete"));
247             g_signal_connect( G_OBJECT(child),
248                               "activate",
249                               G_CALLBACK(SwatchesPanelHook::deleteGradient),
250                               user_data );
251             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
252             popupExtras.push_back(child);
253             gtk_widget_set_sensitive( child, FALSE );
255             child = gtk_menu_item_new_with_label(_("Edit..."));
256             g_signal_connect( G_OBJECT(child),
257                               "activate",
258                               G_CALLBACK(editGradient),
259                               user_data );
260             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
261             popupExtras.push_back(child);
263             child = gtk_separator_menu_item_new();
264             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
265             popupExtras.push_back(child);
267             child = gtk_menu_item_new_with_label(_("Convert"));
268             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
269             //popupExtras.push_back(child);
270             //gtk_widget_set_sensitive( child, FALSE );
271             {
272                 popupSubHolder = child;
273                 popupSub = gtk_menu_new();
274                 gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub );
275             }
277             gtk_widget_show_all(popupMenu);
278         }
280         ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
281         if ( item ) {
282             bool show = swp && (swp->getSelectedIndex() == 0);
283             for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
284                 gtk_widget_set_sensitive(*it, show);
285             }
287             bounceTarget = item;
288             bouncePanel = swp;
289             popupItems.clear();
290             if ( popupMenu ) {
291                 gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub);
292                 bool processed = false;
293                 GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET);
294                 if ( wdgt ) {
295                     SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt);
296                     if ( dtw && dtw->desktop ) {
297                         // Pick up all gradients with vectors
298                         const GSList *gradients = sp_document_get_resource_list(dtw->desktop->doc(), "gradient");
299                         gint index = 0;
300                         for (const GSList *curr = gradients; curr; curr = curr->next) {
301                             SPGradient* grad = SP_GRADIENT(curr->data);
302                             if ( grad->hasStops() && !grad->isSwatch() ) {
303                                 //gl = g_slist_prepend(gl, curr->data);
304                                 processed = true;
305                                 GtkWidget *child = gtk_menu_item_new_with_label(grad->getId());
306                                 gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child);
308                                 popupItems.push_back(grad->getId());
309                                 g_signal_connect( G_OBJECT(child),
310                                                   "activate",
311                                                   G_CALLBACK(SwatchesPanelHook::convertGradient),
312                                                   GINT_TO_POINTER(index) );
313                                 index++;
314                             }
315                         }
317                         gtk_widget_show_all(popupSub);
318                     }
319                 }
320                 gtk_widget_set_sensitive( popupSubHolder, processed );
322                 gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
323                 handled = TRUE;
324             }
325         }
326     }
328     return handled;
332 static char* trim( char* str ) {
333     char* ret = str;
334     while ( *str && (*str == ' ' || *str == '\t') ) {
335         str++;
336     }
337     ret = str;
338     while ( *str ) {
339         str++;
340     }
341     str--;
342     while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
343         *str-- = 0;
344     }
345     return ret;
348 void skipWhitespace( char*& str ) {
349     while ( *str == ' ' || *str == '\t' ) {
350         str++;
351     }
354 bool parseNum( char*& str, int& val ) {
355     val = 0;
356     while ( '0' <= *str && *str <= '9' ) {
357         val = val * 10 + (*str - '0');
358         str++;
359     }
360     bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
361     return retval;
365 void _loadPaletteFile( gchar const *filename )
367     char block[1024];
368     FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
369     if ( f ) {
370         char* result = fgets( block, sizeof(block), f );
371         if ( result ) {
372             if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
373                 bool inHeader = true;
374                 bool hasErr = false;
376                 SwatchPage *onceMore = new SwatchPage();
378                 do {
379                     result = fgets( block, sizeof(block), f );
380                     block[sizeof(block) - 1] = 0;
381                     if ( result ) {
382                         if ( block[0] == '#' ) {
383                             // ignore comment
384                         } else {
385                             char *ptr = block;
386                             // very simple check for header versus entry
387                             while ( *ptr == ' ' || *ptr == '\t' ) {
388                                 ptr++;
389                             }
390                             if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
391                                 // blank line. skip it.
392                             } else if ( '0' <= *ptr && *ptr <= '9' ) {
393                                 // should be an entry link
394                                 inHeader = false;
395                                 ptr = block;
396                                 Glib::ustring name("");
397                                 int r = 0;
398                                 int g = 0;
399                                 int b = 0;
400                                 skipWhitespace(ptr);
401                                 if ( *ptr ) {
402                                     hasErr = parseNum(ptr, r);
403                                     if ( !hasErr ) {
404                                         skipWhitespace(ptr);
405                                         hasErr = parseNum(ptr, g);
406                                     }
407                                     if ( !hasErr ) {
408                                         skipWhitespace(ptr);
409                                         hasErr = parseNum(ptr, b);
410                                     }
411                                     if ( !hasErr && *ptr ) {
412                                         char* n = trim(ptr);
413                                         if (n != NULL) {
414                                             name = n;
415                                         }
416                                     }
417                                     if ( !hasErr ) {
418                                         // Add the entry now
419                                         Glib::ustring nameStr(name);
420                                         ColorItem* item = new ColorItem( r, g, b, nameStr );
421                                         onceMore->_colors.push_back(item);
422                                     }
423                                 } else {
424                                     hasErr = true;
425                                 }
426                             } else {
427                                 if ( !inHeader ) {
428                                     // Hmmm... probably bad. Not quite the format we want?
429                                     hasErr = true;
430                                 } else {
431                                     char* sep = strchr(result, ':');
432                                     if ( sep ) {
433                                         *sep = 0;
434                                         char* val = trim(sep + 1);
435                                         char* name = trim(result);
436                                         if ( *name ) {
437                                             if ( strcmp( "Name", name ) == 0 )
438                                             {
439                                                 onceMore->_name = val;
440                                             }
441                                             else if ( strcmp( "Columns", name ) == 0 )
442                                             {
443                                                 gchar* endPtr = 0;
444                                                 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
445                                                 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
446                                                     // overflow
447                                                 } else if ( (numVal == 0) && (endPtr == val) ) {
448                                                     // failed conversion
449                                                 } else {
450                                                     onceMore->_prefWidth = numVal;
451                                                 }
452                                             }
453                                         } else {
454                                             // error
455                                             hasErr = true;
456                                         }
457                                     } else {
458                                         // error
459                                         hasErr = true;
460                                     }
461                                 }
462                             }
463                         }
464                     }
465                 } while ( result && !hasErr );
466                 if ( !hasErr ) {
467                     possible.push_back(onceMore);
468 #if ENABLE_MAGIC_COLORS
469                     ColorItem::_wireMagicColors( onceMore );
470 #endif // ENABLE_MAGIC_COLORS
471                 } else {
472                     delete onceMore;
473                 }
474             }
475         }
477         fclose(f);
478     }
481 static void loadEmUp()
483     static bool beenHere = false;
484     if ( !beenHere ) {
485         beenHere = true;
487         std::list<gchar *> sources;
488         sources.push_back( profile_path("palettes") );
489         sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
490         sources.push_back( g_strdup(CREATE_PALETTESDIR) );
492         // Use this loop to iterate through a list of possible document locations.
493         while (!sources.empty()) {
494             gchar *dirname = sources.front();
496             if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
497                 && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
498                 GError *err = 0;
499                 GDir *directory = g_dir_open(dirname, 0, &err);
500                 if (!directory) {
501                     gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
502                     g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
503                     g_free(safeDir);
504                 } else {
505                     gchar *filename = 0;
506                     while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
507                         gchar* lower = g_ascii_strdown( filename, -1 );
508 //                        if ( g_str_has_suffix(lower, ".gpl") ) {
509                             gchar* full = g_build_filename(dirname, filename, NULL);
510                             if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
511                                 _loadPaletteFile(full);
512                             }
513                             g_free(full);
514 //                      }
515                         g_free(lower);
516                     }
517                     g_dir_close(directory);
518                 }
519             }
521             // toss the dirname
522             g_free(dirname);
523             sources.pop_front();
524         }
525     }
536 SwatchesPanel& SwatchesPanel::getInstance()
538     return *new SwatchesPanel();
542 /**
543  * Constructor
544  */
545 SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
546     Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
547     _holder(0),
548     _clear(0),
549     _remove(0),
550     _currentIndex(0),
551     _currentDesktop(0),
552     _currentDocument(0)
554     Gtk::RadioMenuItem* hotItem = 0;
555     _holder = new PreviewHolder();
556     _clear = new ColorItem( ege::PaintDef::CLEAR );
557     _remove = new ColorItem( ege::PaintDef::NONE );
558     if (docPalettes.empty()) {
559         SwatchPage *docPalette = new SwatchPage();
561         docPalette->_name = "Auto";
562         docPalettes[0] = docPalette;
563     }
565     loadEmUp();
566     if ( !possible.empty() ) {
567         SwatchPage* first = 0;
568         int index = 0;
569         Glib::ustring targetName;
570         if ( !_prefs_path.empty() ) {
571             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
572             targetName = prefs->getString(_prefs_path + "/palette");
573             if (!targetName.empty()) {
574                 if (targetName == "Auto") {
575                     first = docPalettes[0];
576                 } else {
577                     index++;
578                     for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
579                         if ( (*iter)->_name == targetName ) {
580                             first = *iter;
581                             break;
582                         }
583                         index++;
584                     }
585                 }
586             }
587         }
589         if ( !first ) {
590             first = docPalettes[0];
591             _currentIndex = 0;
592         } else {
593             _currentIndex = index;
594         }
596         _rebuild();
598         Gtk::RadioMenuItem::Group groupOne;
600         int i = 0;
601         std::vector<SwatchPage*> swatchSets = _getSwatchSets();
602         for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) {
603             SwatchPage* curr = *it;
604             Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
605             if ( curr == first ) {
606                 hotItem = single;
607             }
608             _regItem( single, 3, i );
609             i++;
610         }
611     }
614     _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
615     _setTargetFillable(_holder);
617     show_all_children();
619     restorePanelPrefs();
620     if ( hotItem ) {
621         hotItem->set_active();
622     }
625 SwatchesPanel::~SwatchesPanel()
627     _trackDocument( this, 0 );
629     _documentConnection.disconnect();
630     _selChanged.disconnect();
632     if ( _clear ) {
633         delete _clear;
634     }
635     if ( _remove ) {
636         delete _remove;
637     }
638     if ( _holder ) {
639         delete _holder;
640     }
643 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
645     // Must call the parent class or bad things might happen
646     Inkscape::UI::Widget::Panel::setOrientation( how );
648     if ( _holder )
649     {
650         _holder->setOrientation( Gtk::ANCHOR_SOUTH );
651     }
654 void SwatchesPanel::setDesktop( SPDesktop* desktop )
656     if ( desktop != _currentDesktop ) {
657         if ( _currentDesktop ) {
658             _documentConnection.disconnect();
659             _selChanged.disconnect();
660         }
662         _currentDesktop = desktop;
664         if ( desktop ) {
665             _currentDesktop->selection->connectChanged(
666                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
668             _currentDesktop->selection->connectModified(
669                 sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
671             _currentDesktop->connectToolSubselectionChanged(
672                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
674             sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
675             sigc::slot<void, SPDocument*> base2 = first;
676             sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
677             _documentConnection = desktop->connectDocumentReplaced( slot2 );
679             _setDocument( desktop->doc() );
680         } else {
681             _setDocument(0);
682         }
683     }
687 class DocTrack
689 public:
690     DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) :
691         doc(doc),
692         gradientRsrcChanged(gradientRsrcChanged),
693         defsChanged(defsChanged),
694         defsModified(defsModified)
695     {
696     }
698     ~DocTrack()
699     {
700         if (doc) {
701             gradientRsrcChanged.disconnect();
702             defsChanged.disconnect();
703             defsModified.disconnect();
704         }
705     }
707     SPDocument *doc;
708     sigc::connection gradientRsrcChanged;
709     sigc::connection defsChanged;
710     sigc::connection defsModified;
712 private:
713     DocTrack(DocTrack const &); // no copy
714     DocTrack &operator=(DocTrack const &); // no assign
715 };
717 void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document )
719     SPDocument *oldDoc = 0;
720     if (docPerPanel.find(panel) != docPerPanel.end()) {
721         oldDoc = docPerPanel[panel];
722         if (!oldDoc) {
723             docPerPanel.erase(panel); // Should not be needed, but clean up just in case.
724         }
725     }
726     if (oldDoc != document) {
727         if (oldDoc) {
728             docPerPanel[panel] = 0;
729             bool found = false;
730             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
731                 found = (it->second == document);
732             }
733             if (!found) {
734                 for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){
735                     if ((*it)->doc == oldDoc) {
736                         delete *it;
737                         docTrackings.erase(it);
738                         break;
739                     }
740                 }
741             }
742         }
744         if (document) {
745             bool found = false;
746             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
747                 found = (it->second == document);
748             }
749             docPerPanel[panel] = document;
750             if (!found) {
751                 sigc::connection conn1 = sp_document_resources_changed_connect( document, "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) );
752                 sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) );
753                 sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) );
755                 DocTrack *dt = new DocTrack(document, conn1, conn2, conn3);
756                 docTrackings.push_back(dt);
758                 if (docPalettes.find(document) == docPalettes.end()) {
759                     SwatchPage *docPalette = new SwatchPage();
760                     docPalette->_name = "Auto";
761                     docPalettes[document] = docPalette;
762                 }
763             }
764         }
765     }
767     std::set<SPDocument*> docs;
768     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
769         docs.insert(it->second);
770     }
773 void SwatchesPanel::_setDocument( SPDocument *document )
775     if ( document != _currentDocument ) {
776         _trackDocument(this, document);
777         _currentDocument = document;
778         handleGradientsChange( document );
779     }
782 static void recalcSwatchContents(SPDocument* doc,
783                 std::vector<ColorItem*> &tmpColors,
784                 std::map<ColorItem*, guchar*> &previewMappings,
785                 std::map<ColorItem*, SPGradient*> &gradMappings)
787     std::vector<SPGradient*> newList;
789     const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
790     for (const GSList *item = gradients; item; item = item->next) {
791         SPGradient* grad = SP_GRADIENT(item->data);
792         if ( grad->isSwatch() ) {
793             newList.push_back(SP_GRADIENT(item->data));
794         }
795     }
797     if ( !newList.empty() ) {
798         std::reverse(newList.begin(), newList.end());
799         for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
800         {
801             SPGradient* grad = *it;
802             grad->ensureVector();
803             SPGradientStop first = grad->vector.stops[0];
804             SPColor color = first.color;
805             guint32 together = color.toRGBA32(first.opacity);
807             SPGradientStop second = (*it)->vector.stops[1];
808             SPColor color2 = second.color;
810             Glib::ustring name( grad->getId() );
811             unsigned int r = SP_RGBA32_R_U(together);
812             unsigned int g = SP_RGBA32_G_U(together);
813             unsigned int b = SP_RGBA32_B_U(together);
814             ColorItem* item = new ColorItem( r, g, b, name );
816             gint width = PREVIEW_PIXBUF_WIDTH;
817             gint height = VBLOCK;
818             guchar* px = g_new( guchar, 3 * height * width );
819             nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
821             sp_gradient_render_vector_block_rgb( grad,
822                                                  px, width, height, 3 * width,
823                                                  0, width, TRUE );
825             previewMappings[item] = px;
827             tmpColors.push_back(item);
828             gradMappings[item] = grad;
829         }
830     }
833 void SwatchesPanel::handleGradientsChange(SPDocument *document)
835     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
836     if (docPalette) {
837         std::vector<ColorItem*> tmpColors;
838         std::map<ColorItem*, guchar*> tmpPrevs;
839         std::map<ColorItem*, SPGradient*> tmpGrads;
840         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
842         for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) {
843             it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK);
844         }
846         for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) {
847             it->first->setGradient(it->second);
848         }
850         docPalette->_colors.swap(tmpColors);
851         for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) {
852             delete *it;
853         }
856         // Figure out which SwatchesPanel instances are affected and update them.
858         for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
859             if (it->second == document) {
860                 SwatchesPanel* swp = it->first;
861                 std::vector<SwatchPage*> pages = swp->_getSwatchSets();
862                 SwatchPage* curr = pages[swp->_currentIndex];
863                 if (curr == docPalette) {
864                     swp->_rebuild();
865                 }
866             }
867         }
868     }
871 void SwatchesPanel::handleDefsModified(SPDocument *document)
873     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
874     if (docPalette) {
875         std::vector<ColorItem*> tmpColors;
876         std::map<ColorItem*, guchar*> tmpPrevs;
877         std::map<ColorItem*, SPGradient*> tmpGrads;
878         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
880         if ( tmpColors.size() != docPalette->_colors.size() ) {
881             handleGradientsChange(document);
882         } else {
883             int cap = std::min(docPalette->_colors.size(), tmpColors.size());
884             for (int i = 0; i < cap; i++) {
885                 ColorItem* newColor = tmpColors[i];
886                 ColorItem* oldColor = docPalette->_colors[i];
887                 if ( (newColor->def.getType() != oldColor->def.getType()) ||
888                      (newColor->def.getR() != oldColor->def.getR()) ||
889                      (newColor->def.getG() != oldColor->def.getG()) ||
890                      (newColor->def.getB() != oldColor->def.getB()) ) {
891                     oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB());
892                 }
893                 if (tmpGrads.find(newColor) != tmpGrads.end()) {
894                     oldColor->setGradient(tmpGrads[newColor]);
895                 }
896                 if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) {
897                     oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK);
898                 }
899             }
900         }
901     }
904 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
906     std::vector<SwatchPage*> tmp;
907     if (docPalettes.find(_currentDocument) != docPalettes.end()) {
908         tmp.push_back(docPalettes[_currentDocument]);
909     }
911     tmp.insert(tmp.end(), possible.begin(), possible.end());
913     return tmp;
916 void SwatchesPanel::_updateFromSelection()
918     SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0;
919     if ( docPalette ) {
920         Glib::ustring fillId;
921         Glib::ustring strokeId;
923         SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
924         int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
925         switch (result) {
926             case QUERY_STYLE_SINGLE:
927             case QUERY_STYLE_MULTIPLE_AVERAGED:
928             case QUERY_STYLE_MULTIPLE_SAME:
929             {
930                 if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
931                     SPPaintServer* server = tmpStyle->getFillPaintServer();
932                     if ( SP_IS_GRADIENT(server) ) {
933                         SPGradient* target = 0;
934                         SPGradient* grad = SP_GRADIENT(server);
936                         if ( grad->isSwatch() ) {
937                             target = grad;
938                         } else if ( grad->ref ) {
939                             SPGradient *tmp = grad->ref->getObject();
940                             if ( tmp && tmp->isSwatch() ) {
941                                 target = tmp;
942                             }
943                         }
944                         if ( target ) {
945                             gchar const* id = target->repr->attribute("id");
946                             if ( id ) {
947                                 fillId = id;
948                             }
949                         }
950                     }
951                 }
952                 break;
953             }
954         }
956         result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
957         switch (result) {
958             case QUERY_STYLE_SINGLE:
959             case QUERY_STYLE_MULTIPLE_AVERAGED:
960             case QUERY_STYLE_MULTIPLE_SAME:
961             {
962                 if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
963                     SPPaintServer* server = tmpStyle->getStrokePaintServer();
964                     if ( SP_IS_GRADIENT(server) ) {
965                         SPGradient* target = 0;
966                         SPGradient* grad = SP_GRADIENT(server);
967                         if ( grad->isSwatch() ) {
968                             target = grad;
969                         } else if ( grad->ref ) {
970                             SPGradient *tmp = grad->ref->getObject();
971                             if ( tmp && tmp->isSwatch() ) {
972                                 target = tmp;
973                             }
974                         }
975                         if ( target ) {
976                             gchar const* id = target->repr->attribute("id");
977                             if ( id ) {
978                                 strokeId = id;
979                             }
980                         }
981                     }
982                 }
983                 break;
984             }
985         }
986         sp_style_unref(tmpStyle);
988         for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
989             ColorItem* item = *it;
990             bool isFill = (fillId == item->def.descr);
991             bool isStroke = (strokeId == item->def.descr);
992             item->setState( isFill, isStroke );
993         }
994     }
997 void SwatchesPanel::_handleAction( int setId, int itemId )
999     switch( setId ) {
1000         case 3:
1001         {
1002             std::vector<SwatchPage*> pages = _getSwatchSets();
1003             if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) {
1004                 _currentIndex = itemId;
1006                 if ( !_prefs_path.empty() ) {
1007                     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1008                     prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name);
1009                 }
1011                 _rebuild();
1012             }
1013         }
1014         break;
1015     }
1018 void SwatchesPanel::_rebuild()
1020     std::vector<SwatchPage*> pages = _getSwatchSets();
1021     SwatchPage* curr = pages[_currentIndex];
1022     _holder->clear();
1024     if ( curr->_prefWidth > 0 ) {
1025         _holder->setColumnPref( curr->_prefWidth );
1026     }
1027     _holder->freezeUpdates();
1028     // TODO restore once 'clear' works _holder->addPreview(_clear);
1029     _holder->addPreview(_remove);
1030     for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
1031         _holder->addPreview(*it);
1032     }
1033     _holder->thawUpdates();
1039 } //namespace Dialogs
1040 } //namespace UI
1041 } //namespace Inkscape
1044 /*
1045   Local Variables:
1046   mode:c++
1047   c-file-style:"stroustrup"
1048   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1049   indent-tabs-mode:nil
1050   fill-column:99
1051   End:
1052 */
1053 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :