Code

Merge and cleanup of GSoC C++-ification project.
[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  *   Abhishek Sharma
9  *
10  * Copyright (C) 2005 Jon A. Cruz
11  * Copyright (C) 2008 John Bintz
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include <errno.h>
17 #include <map>
18 #include <algorithm>
20 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
21 #include <gtk/gtkdnd.h>
22 #include <gtk/gtkmenu.h>
23 #include <gtk/gtkmenuitem.h>
24 #include <gtk/gtkseparatormenuitem.h>
25 #include <glibmm/i18n.h>
26 #include <gdkmm/pixbuf.h>
28 #include "color-item.h"
29 #include "desktop.h"
30 #include "desktop-handles.h"
31 #include "desktop-style.h"
32 #include "document.h"
33 #include "document-private.h"
34 #include "extension/db.h"
35 #include "inkscape.h"
36 #include "inkscape.h"
37 #include "io/sys.h"
38 #include "io/resource.h"
39 #include "message-context.h"
40 #include "path-prefix.h"
41 #include "preferences.h"
42 #include "sp-item.h"
43 #include "sp-gradient-fns.h"
44 #include "sp-gradient.h"
45 #include "sp-gradient-vector.h"
46 #include "swatches.h"
47 #include "style.h"
48 #include "ui/previewholder.h"
49 #include "widgets/desktop-widget.h"
50 #include "widgets/gradient-vector.h"
51 #include "widgets/eek-preview.h"
52 #include "display/nr-plain-stuff.h"
53 #include "sp-gradient-reference.h"
54 #include "dialog-manager.h"
56 namespace Inkscape {
57 namespace UI {
58 namespace Dialogs {
60 #define VBLOCK 16
61 #define PREVIEW_PIXBUF_WIDTH 128
63 void _loadPaletteFile( gchar const *filename );
66 class DocTrack;
68 std::vector<SwatchPage*> possible;
69 static std::map<SPDocument*, SwatchPage*> docPalettes;
70 static std::vector<DocTrack*> docTrackings;
71 static std::map<SwatchesPanel*, SPDocument*> docPerPanel;
74 class SwatchesPanelHook : public SwatchesPanel
75 {
76 public:
77     static void convertGradient( GtkMenuItem *menuitem, gpointer userData );
78     static void deleteGradient( GtkMenuItem *menuitem, gpointer userData );
79 };
81 static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
82     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
83     if ( item ) {
84         item->buttonClicked(false);
85     }
86 }
88 static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
89     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
90     if ( item ) {
91         item->buttonClicked(true);
92     }
93 }
95 static GtkWidget* popupMenu = 0;
96 static GtkWidget *popupSubHolder = 0;
97 static GtkWidget *popupSub = 0;
98 static std::vector<Glib::ustring> popupItems;
99 static std::vector<GtkWidget*> popupExtras;
100 static ColorItem* bounceTarget = 0;
101 static SwatchesPanel* bouncePanel = 0;
103 static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
105     if ( bounceTarget ) {
106         handleClick( GTK_WIDGET(menuitem), bounceTarget );
107     }
110 static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
112     if ( bounceTarget ) {
113         handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
114     }
117 static void editGradientImpl( SPDesktop* desktop, SPGradient* gr )
119     if ( gr ) {
120         bool shown = false;
121         if ( desktop && desktop->doc() ) {
122             Inkscape::Selection *selection = sp_desktop_selection( desktop );
123             GSList const *items = selection->itemList();
124             if (items) {
125                 SPStyle *query = sp_style_new( desktop->doc() );
126                 int result = objects_query_fillstroke(const_cast<GSList *>(items), query, true);
127                 if ( (result == QUERY_STYLE_MULTIPLE_SAME) || (result == QUERY_STYLE_SINGLE) ) {
128                     // could be pertinent
129                     if (query->fill.isPaintserver()) {
130                         SPPaintServer* server = query->getFillPaintServer();
131                         if ( SP_IS_GRADIENT(server) ) {
132                             SPGradient* grad = SP_GRADIENT(server);
133                             if ( grad->isSwatch() && grad->getId() == gr->getId()) {
134                                 desktop->_dlg_mgr->showDialog("FillAndStroke");
135                                 shown = true;
136                             }
137                         }
138                     }
139                 }
140                 sp_style_unref(query);
141             }
142         }
144         if (!shown) {
145             GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
146             gtk_widget_show( dialog );
147         }
148     }
151 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
153     if ( bounceTarget ) {
154         SwatchesPanel* swp = bouncePanel;
155         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
156         SPDocument *doc = desktop ? desktop->doc() : 0;
157         if (doc) {
158             std::string targetName(bounceTarget->def.descr);
159             const GSList *gradients = doc->getResourceList("gradient");
160             for (const GSList *item = gradients; item; item = item->next) {
161                 SPGradient* grad = SP_GRADIENT(item->data);
162                 if ( targetName == grad->getId() ) {
163                     editGradientImpl( desktop, grad );
164                     break;
165                 }
166             }
167         }
168     }
171 void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData )
173     if ( bounceTarget ) {
174         SwatchesPanel* swp = bouncePanel;
175         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
176         SPDocument *doc = desktop ? desktop->doc() : 0;
177         gint index = GPOINTER_TO_INT(userData);
178         if ( doc && (index >= 0) && (static_cast<guint>(index) < popupItems.size()) ) {
179             Glib::ustring targetName = popupItems[index];
181             const GSList *gradients = doc->getResourceList("gradient");
182             for (const GSList *item = gradients; item; item = item->next) {
183                 SPGradient* grad = SP_GRADIENT(item->data);
184                 if ( targetName == grad->getId() ) {
185                     grad->setSwatch();
186                     DocumentUndo::done(doc, SP_VERB_CONTEXT_GRADIENT,
187                                        _("Add gradient stop"));
188                     break;
189                 }
190             }
191         }
192     }
195 void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ )
197     if ( bounceTarget ) {
198         SwatchesPanel* swp = bouncePanel;
199         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
200         SPDocument *doc = desktop ? desktop->doc() : 0;
201         if (doc) {
202             std::string targetName(bounceTarget->def.descr);
203             const GSList *gradients = doc->getResourceList("gradient");
204             for (const GSList *item = gradients; item; item = item->next) {
205                 SPGradient* grad = SP_GRADIENT(item->data);
206                 if ( targetName == grad->getId() ) {
207                     grad->setSwatch(false);
208                     DocumentUndo::done(doc, SP_VERB_CONTEXT_GRADIENT,
209                                        _("Delete"));
210                     break;
211                 }
212             }
213         }
214     }
217 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
219     SwatchesPanel *swp = 0;
221     std::map<GtkWidget*, SwatchesPanel*> rawObjects;
222     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
223         rawObjects[GTK_WIDGET(it->first->gobj())] = it->first;
224     }
226     for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) {
227         if (rawObjects.find(curr) != rawObjects.end()) {
228             swp = rawObjects[curr];
229         }
230     }
232     return swp;
235 static void removeit( GtkWidget *widget, gpointer data )
237     gtk_container_remove( GTK_CONTAINER(data), widget );
240 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
242     gboolean handled = FALSE;
244     if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
245         SwatchesPanel* swp = findContainingPanel( widget );
247         if ( !popupMenu ) {
248             popupMenu = gtk_menu_new();
249             GtkWidget* child = 0;
251             //TRANSLATORS: An item in context menu on a colour in the swatches
252             child = gtk_menu_item_new_with_label(_("Set fill"));
253             g_signal_connect( G_OBJECT(child),
254                               "activate",
255                               G_CALLBACK(redirClick),
256                               user_data);
257             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
259             //TRANSLATORS: An item in context menu on a colour in the swatches
260             child = gtk_menu_item_new_with_label(_("Set stroke"));
262             g_signal_connect( G_OBJECT(child),
263                               "activate",
264                               G_CALLBACK(redirSecondaryClick),
265                               user_data);
266             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
268             child = gtk_separator_menu_item_new();
269             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
270             popupExtras.push_back(child);
272             child = gtk_menu_item_new_with_label(_("Delete"));
273             g_signal_connect( G_OBJECT(child),
274                               "activate",
275                               G_CALLBACK(SwatchesPanelHook::deleteGradient),
276                               user_data );
277             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
278             popupExtras.push_back(child);
279             gtk_widget_set_sensitive( child, FALSE );
281             child = gtk_menu_item_new_with_label(_("Edit..."));
282             g_signal_connect( G_OBJECT(child),
283                               "activate",
284                               G_CALLBACK(editGradient),
285                               user_data );
286             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
287             popupExtras.push_back(child);
289             child = gtk_separator_menu_item_new();
290             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
291             popupExtras.push_back(child);
293             child = gtk_menu_item_new_with_label(_("Convert"));
294             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
295             //popupExtras.push_back(child);
296             //gtk_widget_set_sensitive( child, FALSE );
297             {
298                 popupSubHolder = child;
299                 popupSub = gtk_menu_new();
300                 gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub );
301             }
303             gtk_widget_show_all(popupMenu);
304         }
306         ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
307         if ( item ) {
308             bool show = swp && (swp->getSelectedIndex() == 0);
309             for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
310                 gtk_widget_set_sensitive(*it, show);
311             }
313             bounceTarget = item;
314             bouncePanel = swp;
315             popupItems.clear();
316             if ( popupMenu ) {
317                 gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub);
318                 bool processed = false;
319                 GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET);
320                 if ( wdgt ) {
321                     SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt);
322                     if ( dtw && dtw->desktop ) {
323                         // Pick up all gradients with vectors
324                         const GSList *gradients = (dtw->desktop->doc())->getResourceList("gradient");
325                         gint index = 0;
326                         for (const GSList *curr = gradients; curr; curr = curr->next) {
327                             SPGradient* grad = SP_GRADIENT(curr->data);
328                             if ( grad->hasStops() && !grad->isSwatch() ) {
329                                 //gl = g_slist_prepend(gl, curr->data);
330                                 processed = true;
331                                 GtkWidget *child = gtk_menu_item_new_with_label(grad->getId());
332                                 gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child);
334                                 popupItems.push_back(grad->getId());
335                                 g_signal_connect( G_OBJECT(child),
336                                                   "activate",
337                                                   G_CALLBACK(SwatchesPanelHook::convertGradient),
338                                                   GINT_TO_POINTER(index) );
339                                 index++;
340                             }
341                         }
343                         gtk_widget_show_all(popupSub);
344                     }
345                 }
346                 gtk_widget_set_sensitive( popupSubHolder, processed );
348                 gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
349                 handled = TRUE;
350             }
351         }
352     }
354     return handled;
358 static char* trim( char* str ) {
359     char* ret = str;
360     while ( *str && (*str == ' ' || *str == '\t') ) {
361         str++;
362     }
363     ret = str;
364     while ( *str ) {
365         str++;
366     }
367     str--;
368     while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
369         *str-- = 0;
370     }
371     return ret;
374 void skipWhitespace( char*& str ) {
375     while ( *str == ' ' || *str == '\t' ) {
376         str++;
377     }
380 bool parseNum( char*& str, int& val ) {
381     val = 0;
382     while ( '0' <= *str && *str <= '9' ) {
383         val = val * 10 + (*str - '0');
384         str++;
385     }
386     bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
387     return retval;
391 void _loadPaletteFile( gchar const *filename )
393     char block[1024];
394     FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
395     if ( f ) {
396         char* result = fgets( block, sizeof(block), f );
397         if ( result ) {
398             if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
399                 bool inHeader = true;
400                 bool hasErr = false;
402                 SwatchPage *onceMore = new SwatchPage();
404                 do {
405                     result = fgets( block, sizeof(block), f );
406                     block[sizeof(block) - 1] = 0;
407                     if ( result ) {
408                         if ( block[0] == '#' ) {
409                             // ignore comment
410                         } else {
411                             char *ptr = block;
412                             // very simple check for header versus entry
413                             while ( *ptr == ' ' || *ptr == '\t' ) {
414                                 ptr++;
415                             }
416                             if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
417                                 // blank line. skip it.
418                             } else if ( '0' <= *ptr && *ptr <= '9' ) {
419                                 // should be an entry link
420                                 inHeader = false;
421                                 ptr = block;
422                                 Glib::ustring name("");
423                                 int r = 0;
424                                 int g = 0;
425                                 int b = 0;
426                                 skipWhitespace(ptr);
427                                 if ( *ptr ) {
428                                     hasErr = parseNum(ptr, r);
429                                     if ( !hasErr ) {
430                                         skipWhitespace(ptr);
431                                         hasErr = parseNum(ptr, g);
432                                     }
433                                     if ( !hasErr ) {
434                                         skipWhitespace(ptr);
435                                         hasErr = parseNum(ptr, b);
436                                     }
437                                     if ( !hasErr && *ptr ) {
438                                         char* n = trim(ptr);
439                                         if (n != NULL) {
440                                             name = g_dpgettext2(NULL, "Palette", n);
441                                         }
442                                     }
443                                     if ( !hasErr ) {
444                                         // Add the entry now
445                                         Glib::ustring nameStr(name);
446                                         ColorItem* item = new ColorItem( r, g, b, nameStr );
447                                         onceMore->_colors.push_back(item);
448                                     }
449                                 } else {
450                                     hasErr = true;
451                                 }
452                             } else {
453                                 if ( !inHeader ) {
454                                     // Hmmm... probably bad. Not quite the format we want?
455                                     hasErr = true;
456                                 } else {
457                                     char* sep = strchr(result, ':');
458                                     if ( sep ) {
459                                         *sep = 0;
460                                         char* val = trim(sep + 1);
461                                         char* name = trim(result);
462                                         if ( *name ) {
463                                             if ( strcmp( "Name", name ) == 0 )
464                                             {
465                                                 onceMore->_name = val;
466                                             }
467                                             else if ( strcmp( "Columns", name ) == 0 )
468                                             {
469                                                 gchar* endPtr = 0;
470                                                 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
471                                                 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
472                                                     // overflow
473                                                 } else if ( (numVal == 0) && (endPtr == val) ) {
474                                                     // failed conversion
475                                                 } else {
476                                                     onceMore->_prefWidth = numVal;
477                                                 }
478                                             }
479                                         } else {
480                                             // error
481                                             hasErr = true;
482                                         }
483                                     } else {
484                                         // error
485                                         hasErr = true;
486                                     }
487                                 }
488                             }
489                         }
490                     }
491                 } while ( result && !hasErr );
492                 if ( !hasErr ) {
493                     possible.push_back(onceMore);
494 #if ENABLE_MAGIC_COLORS
495                     ColorItem::_wireMagicColors( onceMore );
496 #endif // ENABLE_MAGIC_COLORS
497                 } else {
498                     delete onceMore;
499                 }
500             }
501         }
503         fclose(f);
504     }
507 static void loadEmUp()
509     static bool beenHere = false;
510     if ( !beenHere ) {
511         beenHere = true;
513         std::list<gchar *> sources;
514         sources.push_back( profile_path("palettes") );
515         sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
516         sources.push_back( g_strdup(CREATE_PALETTESDIR) );
518         // Use this loop to iterate through a list of possible document locations.
519         while (!sources.empty()) {
520             gchar *dirname = sources.front();
522             if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
523                 && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
524                 GError *err = 0;
525                 GDir *directory = g_dir_open(dirname, 0, &err);
526                 if (!directory) {
527                     gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
528                     g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
529                     g_free(safeDir);
530                 } else {
531                     gchar *filename = 0;
532                     while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
533                         gchar* lower = g_ascii_strdown( filename, -1 );
534 //                        if ( g_str_has_suffix(lower, ".gpl") ) {
535                             gchar* full = g_build_filename(dirname, filename, NULL);
536                             if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
537                                 _loadPaletteFile(full);
538                             }
539                             g_free(full);
540 //                      }
541                         g_free(lower);
542                     }
543                     g_dir_close(directory);
544                 }
545             }
547             // toss the dirname
548             g_free(dirname);
549             sources.pop_front();
550         }
551     }
562 SwatchesPanel& SwatchesPanel::getInstance()
564     return *new SwatchesPanel();
568 /**
569  * Constructor
570  */
571 SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
572     Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
573     _holder(0),
574     _clear(0),
575     _remove(0),
576     _currentIndex(0),
577     _currentDesktop(0),
578     _currentDocument(0)
580     Gtk::RadioMenuItem* hotItem = 0;
581     _holder = new PreviewHolder();
582     _clear = new ColorItem( ege::PaintDef::CLEAR );
583     _remove = new ColorItem( ege::PaintDef::NONE );
584     if (docPalettes.empty()) {
585         SwatchPage *docPalette = new SwatchPage();
587         docPalette->_name = "Auto";
588         docPalettes[0] = docPalette;
589     }
591     loadEmUp();
592     if ( !possible.empty() ) {
593         SwatchPage* first = 0;
594         int index = 0;
595         Glib::ustring targetName;
596         if ( !_prefs_path.empty() ) {
597             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
598             targetName = prefs->getString(_prefs_path + "/palette");
599             if (!targetName.empty()) {
600                 if (targetName == "Auto") {
601                     first = docPalettes[0];
602                 } else {
603                     index++;
604                     for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
605                         if ( (*iter)->_name == targetName ) {
606                             first = *iter;
607                             break;
608                         }
609                         index++;
610                     }
611                 }
612             }
613         }
615         if ( !first ) {
616             first = docPalettes[0];
617             _currentIndex = 0;
618         } else {
619             _currentIndex = index;
620         }
622         _rebuild();
624         Gtk::RadioMenuItem::Group groupOne;
626         int i = 0;
627         std::vector<SwatchPage*> swatchSets = _getSwatchSets();
628         for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) {
629             SwatchPage* curr = *it;
630             Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
631             if ( curr == first ) {
632                 hotItem = single;
633             }
634             _regItem( single, 3, i );
635             i++;
636         }
637     }
640     _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
641     _setTargetFillable(_holder);
643     show_all_children();
645     restorePanelPrefs();
646     if ( hotItem ) {
647         hotItem->set_active();
648     }
651 SwatchesPanel::~SwatchesPanel()
653     _trackDocument( this, 0 );
655     _documentConnection.disconnect();
656     _selChanged.disconnect();
658     if ( _clear ) {
659         delete _clear;
660     }
661     if ( _remove ) {
662         delete _remove;
663     }
664     if ( _holder ) {
665         delete _holder;
666     }
669 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
671     // Must call the parent class or bad things might happen
672     Inkscape::UI::Widget::Panel::setOrientation( how );
674     if ( _holder )
675     {
676         _holder->setOrientation( Gtk::ANCHOR_SOUTH );
677     }
680 void SwatchesPanel::setDesktop( SPDesktop* desktop )
682     if ( desktop != _currentDesktop ) {
683         if ( _currentDesktop ) {
684             _documentConnection.disconnect();
685             _selChanged.disconnect();
686         }
688         _currentDesktop = desktop;
690         if ( desktop ) {
691             _currentDesktop->selection->connectChanged(
692                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
694             _currentDesktop->selection->connectModified(
695                 sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
697             _currentDesktop->connectToolSubselectionChanged(
698                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
700             sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
701             sigc::slot<void, SPDocument*> base2 = first;
702             sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
703             _documentConnection = desktop->connectDocumentReplaced( slot2 );
705             _setDocument( desktop->doc() );
706         } else {
707             _setDocument(0);
708         }
709     }
713 class DocTrack
715 public:
716     DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) :
717         doc(doc),
718         gradientRsrcChanged(gradientRsrcChanged),
719         defsChanged(defsChanged),
720         defsModified(defsModified)
721     {
722     }
724     ~DocTrack()
725     {
726         if (doc) {
727             gradientRsrcChanged.disconnect();
728             defsChanged.disconnect();
729             defsModified.disconnect();
730         }
731     }
733     SPDocument *doc;
734     sigc::connection gradientRsrcChanged;
735     sigc::connection defsChanged;
736     sigc::connection defsModified;
738 private:
739     DocTrack(DocTrack const &); // no copy
740     DocTrack &operator=(DocTrack const &); // no assign
741 };
743 void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document )
745     SPDocument *oldDoc = 0;
746     if (docPerPanel.find(panel) != docPerPanel.end()) {
747         oldDoc = docPerPanel[panel];
748         if (!oldDoc) {
749             docPerPanel.erase(panel); // Should not be needed, but clean up just in case.
750         }
751     }
752     if (oldDoc != document) {
753         if (oldDoc) {
754             docPerPanel[panel] = 0;
755             bool found = false;
756             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
757                 found = (it->second == document);
758             }
759             if (!found) {
760                 for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){
761                     if ((*it)->doc == oldDoc) {
762                         delete *it;
763                         docTrackings.erase(it);
764                         break;
765                     }
766                 }
767             }
768         }
770         if (document) {
771             bool found = false;
772             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
773                 found = (it->second == document);
774             }
775             docPerPanel[panel] = document;
776             if (!found) {
777                 sigc::connection conn1 = document->connectResourcesChanged( "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) );
778                 sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) );
779                 sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) );
781                 DocTrack *dt = new DocTrack(document, conn1, conn2, conn3);
782                 docTrackings.push_back(dt);
784                 if (docPalettes.find(document) == docPalettes.end()) {
785                     SwatchPage *docPalette = new SwatchPage();
786                     docPalette->_name = "Auto";
787                     docPalettes[document] = docPalette;
788                 }
789             }
790         }
791     }
793     std::set<SPDocument*> docs;
794     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
795         docs.insert(it->second);
796     }
799 void SwatchesPanel::_setDocument( SPDocument *document )
801     if ( document != _currentDocument ) {
802         _trackDocument(this, document);
803         _currentDocument = document;
804         handleGradientsChange( document );
805     }
808 static void recalcSwatchContents(SPDocument* doc,
809                 std::vector<ColorItem*> &tmpColors,
810                 std::map<ColorItem*, guchar*> &previewMappings,
811                 std::map<ColorItem*, SPGradient*> &gradMappings)
813     std::vector<SPGradient*> newList;
815     const GSList *gradients = doc->getResourceList("gradient");
816     for (const GSList *item = gradients; item; item = item->next) {
817         SPGradient* grad = SP_GRADIENT(item->data);
818         if ( grad->isSwatch() ) {
819             newList.push_back(SP_GRADIENT(item->data));
820         }
821     }
823     if ( !newList.empty() ) {
824         std::reverse(newList.begin(), newList.end());
825         for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
826         {
827             SPGradient* grad = *it;
828             grad->ensureVector();
829             SPGradientStop first = grad->vector.stops[0];
830             SPColor color = first.color;
831             guint32 together = color.toRGBA32(first.opacity);
833             SPGradientStop second = (*it)->vector.stops[1];
834             SPColor color2 = second.color;
836             Glib::ustring name( grad->getId() );
837             unsigned int r = SP_RGBA32_R_U(together);
838             unsigned int g = SP_RGBA32_G_U(together);
839             unsigned int b = SP_RGBA32_B_U(together);
840             ColorItem* item = new ColorItem( r, g, b, name );
842             gint width = PREVIEW_PIXBUF_WIDTH;
843             gint height = VBLOCK;
844             guchar* px = g_new( guchar, 3 * height * width );
845             nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
847             sp_gradient_render_vector_block_rgb( grad,
848                                                  px, width, height, 3 * width,
849                                                  0, width, TRUE );
851             previewMappings[item] = px;
853             tmpColors.push_back(item);
854             gradMappings[item] = grad;
855         }
856     }
859 void SwatchesPanel::handleGradientsChange(SPDocument *document)
861     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
862     if (docPalette) {
863         std::vector<ColorItem*> tmpColors;
864         std::map<ColorItem*, guchar*> tmpPrevs;
865         std::map<ColorItem*, SPGradient*> tmpGrads;
866         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
868         for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) {
869             it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK);
870         }
872         for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) {
873             it->first->setGradient(it->second);
874         }
876         docPalette->_colors.swap(tmpColors);
877         for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) {
878             delete *it;
879         }
882         // Figure out which SwatchesPanel instances are affected and update them.
884         for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
885             if (it->second == document) {
886                 SwatchesPanel* swp = it->first;
887                 std::vector<SwatchPage*> pages = swp->_getSwatchSets();
888                 SwatchPage* curr = pages[swp->_currentIndex];
889                 if (curr == docPalette) {
890                     swp->_rebuild();
891                 }
892             }
893         }
894     }
897 void SwatchesPanel::handleDefsModified(SPDocument *document)
899     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
900     if (docPalette) {
901         std::vector<ColorItem*> tmpColors;
902         std::map<ColorItem*, guchar*> tmpPrevs;
903         std::map<ColorItem*, SPGradient*> tmpGrads;
904         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
906         if ( tmpColors.size() != docPalette->_colors.size() ) {
907             handleGradientsChange(document);
908         } else {
909             int cap = std::min(docPalette->_colors.size(), tmpColors.size());
910             for (int i = 0; i < cap; i++) {
911                 ColorItem* newColor = tmpColors[i];
912                 ColorItem* oldColor = docPalette->_colors[i];
913                 if ( (newColor->def.getType() != oldColor->def.getType()) ||
914                      (newColor->def.getR() != oldColor->def.getR()) ||
915                      (newColor->def.getG() != oldColor->def.getG()) ||
916                      (newColor->def.getB() != oldColor->def.getB()) ) {
917                     oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB());
918                 }
919                 if (tmpGrads.find(newColor) != tmpGrads.end()) {
920                     oldColor->setGradient(tmpGrads[newColor]);
921                 }
922                 if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) {
923                     oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK);
924                 }
925             }
926         }
927     }
930 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
932     std::vector<SwatchPage*> tmp;
933     if (docPalettes.find(_currentDocument) != docPalettes.end()) {
934         tmp.push_back(docPalettes[_currentDocument]);
935     }
937     tmp.insert(tmp.end(), possible.begin(), possible.end());
939     return tmp;
942 void SwatchesPanel::_updateFromSelection()
944     SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0;
945     if ( docPalette ) {
946         Glib::ustring fillId;
947         Glib::ustring strokeId;
949         SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
950         int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
951         switch (result) {
952             case QUERY_STYLE_SINGLE:
953             case QUERY_STYLE_MULTIPLE_AVERAGED:
954             case QUERY_STYLE_MULTIPLE_SAME:
955             {
956                 if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
957                     SPPaintServer* server = tmpStyle->getFillPaintServer();
958                     if ( SP_IS_GRADIENT(server) ) {
959                         SPGradient* target = 0;
960                         SPGradient* grad = SP_GRADIENT(server);
962                         if ( grad->isSwatch() ) {
963                             target = grad;
964                         } else if ( grad->ref ) {
965                             SPGradient *tmp = grad->ref->getObject();
966                             if ( tmp && tmp->isSwatch() ) {
967                                 target = tmp;
968                             }
969                         }
970                         if ( target ) {
971                             //XML Tree being used directly here while it shouldn't be
972                             gchar const* id = target->getRepr()->attribute("id");
973                             if ( id ) {
974                                 fillId = id;
975                             }
976                         }
977                     }
978                 }
979                 break;
980             }
981         }
983         result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
984         switch (result) {
985             case QUERY_STYLE_SINGLE:
986             case QUERY_STYLE_MULTIPLE_AVERAGED:
987             case QUERY_STYLE_MULTIPLE_SAME:
988             {
989                 if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
990                     SPPaintServer* server = tmpStyle->getStrokePaintServer();
991                     if ( SP_IS_GRADIENT(server) ) {
992                         SPGradient* target = 0;
993                         SPGradient* grad = SP_GRADIENT(server);
994                         if ( grad->isSwatch() ) {
995                             target = grad;
996                         } else if ( grad->ref ) {
997                             SPGradient *tmp = grad->ref->getObject();
998                             if ( tmp && tmp->isSwatch() ) {
999                                 target = tmp;
1000                             }
1001                         }
1002                         if ( target ) {
1003                             //XML Tree being used directly here while it shouldn't be
1004                             gchar const* id = target->getRepr()->attribute("id");
1005                             if ( id ) {
1006                                 strokeId = id;
1007                             }
1008                         }
1009                     }
1010                 }
1011                 break;
1012             }
1013         }
1014         sp_style_unref(tmpStyle);
1016         for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
1017             ColorItem* item = *it;
1018             bool isFill = (fillId == item->def.descr);
1019             bool isStroke = (strokeId == item->def.descr);
1020             item->setState( isFill, isStroke );
1021         }
1022     }
1025 void SwatchesPanel::_handleAction( int setId, int itemId )
1027     switch( setId ) {
1028         case 3:
1029         {
1030             std::vector<SwatchPage*> pages = _getSwatchSets();
1031             if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) {
1032                 _currentIndex = itemId;
1034                 if ( !_prefs_path.empty() ) {
1035                     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1036                     prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name);
1037                 }
1039                 _rebuild();
1040             }
1041         }
1042         break;
1043     }
1046 void SwatchesPanel::_rebuild()
1048     std::vector<SwatchPage*> pages = _getSwatchSets();
1049     SwatchPage* curr = pages[_currentIndex];
1050     _holder->clear();
1052     if ( curr->_prefWidth > 0 ) {
1053         _holder->setColumnPref( curr->_prefWidth );
1054     }
1055     _holder->freezeUpdates();
1056     // TODO restore once 'clear' works _holder->addPreview(_clear);
1057     _holder->addPreview(_remove);
1058     for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
1059         _holder->addPreview(*it);
1060     }
1061     _holder->thawUpdates();
1067 } //namespace Dialogs
1068 } //namespace UI
1069 } //namespace Inkscape
1072 /*
1073   Local Variables:
1074   mode:c++
1075   c-file-style:"stroustrup"
1076   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1077   indent-tabs-mode:nil
1078   fill-column:99
1079   End:
1080 */
1081 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :