Code

A simple layout document as to what, why and how is cppification.
[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>
18 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
19 #include <gtk/gtkdnd.h>
20 #include <gtk/gtkmenu.h>
21 #include <gtk/gtkmenuitem.h>
22 #include <gtk/gtkseparatormenuitem.h>
23 #include <glibmm/i18n.h>
24 #include <gdkmm/pixbuf.h>
26 #include "color-item.h"
27 #include "desktop.h"
28 #include "desktop-handles.h"
29 #include "desktop-style.h"
30 #include "document.h"
31 #include "document-private.h"
32 #include "extension/db.h"
33 #include "inkscape.h"
34 #include "inkscape.h"
35 #include "io/sys.h"
36 #include "io/resource.h"
37 #include "message-context.h"
38 #include "path-prefix.h"
39 #include "preferences.h"
40 #include "sp-item.h"
41 #include "sp-gradient-fns.h"
42 #include "sp-gradient.h"
43 #include "sp-gradient-vector.h"
44 #include "swatches.h"
45 #include "style.h"
46 #include "ui/previewholder.h"
47 #include "widgets/desktop-widget.h"
48 #include "widgets/gradient-vector.h"
49 #include "widgets/eek-preview.h"
50 #include "display/nr-plain-stuff.h"
51 #include "sp-gradient-reference.h"
54 namespace Inkscape {
55 namespace UI {
56 namespace Dialogs {
58 #define VBLOCK 16
59 #define PREVIEW_PIXBUF_WIDTH 128
61 void _loadPaletteFile( gchar const *filename );
64 class DocTrack;
66 std::vector<SwatchPage*> possible;
67 static std::map<SPDocument*, SwatchPage*> docPalettes;
68 static std::vector<DocTrack*> docTrackings;
69 static std::map<SwatchesPanel*, SPDocument*> docPerPanel;
72 class SwatchesPanelHook : public SwatchesPanel
73 {
74 public:
75     static void convertGradient( GtkMenuItem * menuitem, gpointer userData );
76 };
78 static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
79     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
80     if ( item ) {
81         item->buttonClicked(false);
82     }
83 }
85 static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
86     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
87     if ( item ) {
88         item->buttonClicked(true);
89     }
90 }
92 static GtkWidget* popupMenu = 0;
93 static GtkWidget *popupSubHolder = 0;
94 static GtkWidget *popupSub = 0;
95 static std::vector<Glib::ustring> popupItems;
96 static std::vector<GtkWidget*> popupExtras;
97 static ColorItem* bounceTarget = 0;
98 static SwatchesPanel* bouncePanel = 0;
100 static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
102     if ( bounceTarget ) {
103         handleClick( GTK_WIDGET(menuitem), bounceTarget );
104     }
107 static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
109     if ( bounceTarget ) {
110         handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
111     }
114 static void editGradientImpl( SPGradient* gr )
116     if ( gr ) {
117         GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
118         gtk_widget_show( dialog );
119     }
122 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
124     if ( bounceTarget ) {
125         SwatchesPanel* swp = bouncePanel;
126         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
127         SPDocument *doc = desktop ? desktop->doc() : 0;
128         if (doc) {
129             std::string targetName(bounceTarget->def.descr);
130             const GSList *gradients = doc->get_resource_list("gradient");
131             for (const GSList *item = gradients; item; item = item->next) {
132                 SPGradient* grad = SP_GRADIENT(item->data);
133                 if ( targetName == grad->getId() ) {
134                     editGradientImpl( grad );
135                     break;
136                 }
137             }
138         }
139     }
142 static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
144     if ( bounceTarget ) {
145         SwatchesPanel* swp = bouncePanel;
146         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
147         SPDocument *doc = desktop ? desktop->doc() : 0;
148         if (doc) {
149             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
151             Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient");
152             repr->setAttribute("osb:paint", "solid");
153             Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
154             stop->setAttribute("offset", "0");
155             stop->setAttribute("style", "stop-color:#000;stop-opacity:1;");
156             repr->appendChild(stop);
157             Inkscape::GC::release(stop);
159             SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL);
161             SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr));
163             Inkscape::GC::release(repr);
166             editGradientImpl( gr );
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->get_resource_list("gradient");
182             for (const GSList *item = gradients; item; item = item->next) {
183                 SPGradient* grad = SP_GRADIENT(item->data);
184                 if ( targetName == grad->getId() ) {
185                                         //XML Tree being used directly here while it shouldn't be
186                     grad->getRepr()->setAttribute("osb:paint", "solid"); // TODO make conditional
188                     SPDocumentUndo::done(doc, SP_VERB_CONTEXT_GRADIENT,
189                                      _("Add gradient stop"));
191                     handleGradientsChange(doc); // work-around for signal not being emitted
192                     break;
193                 }
194             }
195         }
196     }
199 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
201     SwatchesPanel *swp = 0;
203     std::map<GtkWidget*, SwatchesPanel*> rawObjects;
204     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
205         rawObjects[GTK_WIDGET(it->first->gobj())] = it->first;
206     }
208     for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) {
209         if (rawObjects.find(curr) != rawObjects.end()) {
210             swp = rawObjects[curr];
211         }
212     }
214     return swp;
217 static void removeit( GtkWidget *widget, gpointer data )
219     gtk_container_remove( GTK_CONTAINER(data), widget );
222 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
224     gboolean handled = FALSE;
226     if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
227         SwatchesPanel* swp = findContainingPanel( widget );
229         if ( !popupMenu ) {
230             popupMenu = gtk_menu_new();
231             GtkWidget* child = 0;
233             //TRANSLATORS: An item in context menu on a colour in the swatches
234             child = gtk_menu_item_new_with_label(_("Set fill"));
235             g_signal_connect( G_OBJECT(child),
236                               "activate",
237                               G_CALLBACK(redirClick),
238                               user_data);
239             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
241             //TRANSLATORS: An item in context menu on a colour in the swatches
242             child = gtk_menu_item_new_with_label(_("Set stroke"));
244             g_signal_connect( G_OBJECT(child),
245                               "activate",
246                               G_CALLBACK(redirSecondaryClick),
247                               user_data);
248             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
250             child = gtk_separator_menu_item_new();
251             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
252             popupExtras.push_back(child);
254             child = gtk_menu_item_new_with_label(_("Add"));
255             g_signal_connect( G_OBJECT(child),
256                               "activate",
257                               G_CALLBACK(addNewGradient),
258                               user_data );
259             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
260             popupExtras.push_back(child);
262             child = gtk_menu_item_new_with_label(_("Delete"));
263             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
264             //popupExtras.push_back(child);
265             gtk_widget_set_sensitive( child, FALSE );
267             child = gtk_menu_item_new_with_label(_("Edit..."));
268             g_signal_connect( G_OBJECT(child),
269                               "activate",
270                               G_CALLBACK(editGradient),
271                               user_data );
272             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
273             popupExtras.push_back(child);
275             child = gtk_separator_menu_item_new();
276             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
277             popupExtras.push_back(child);
279             child = gtk_menu_item_new_with_label(_("Convert"));
280             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
281             //popupExtras.push_back(child);
282             //gtk_widget_set_sensitive( child, FALSE );
283             {
284                 popupSubHolder = child;
285                 popupSub = gtk_menu_new();
286                 gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub );
287             }
289             gtk_widget_show_all(popupMenu);
290         }
292         ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
293         if ( item ) {
294             bool show = swp && (swp->getSelectedIndex() == 0);
295             for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
296                 gtk_widget_set_sensitive(*it, show);
297             }
299             bounceTarget = item;
300             bouncePanel = swp;
301             popupItems.clear();
302             if ( popupMenu ) {
303                 gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub);
304                 bool processed = false;
305                 GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET);
306                 if ( wdgt ) {
307                     SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt);
308                     if ( dtw && dtw->desktop ) {
309                         // Pick up all gradients with vectors
310                         const GSList *gradients = (dtw->desktop->doc())->get_resource_list("gradient");
311                         gint index = 0;
312                         for (const GSList *curr = gradients; curr; curr = curr->next) {
313                             SPGradient* grad = SP_GRADIENT(curr->data);
314                             if ( grad->hasStops() && !grad->isSwatch() ) {
315                                 //gl = g_slist_prepend(gl, curr->data);
316                                 processed = true;
317                                 GtkWidget *child = gtk_menu_item_new_with_label(grad->getId());
318                                 gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child);
320                                 popupItems.push_back(grad->getId());
321                                 g_signal_connect( G_OBJECT(child),
322                                                   "activate",
323                                                   G_CALLBACK(SwatchesPanelHook::convertGradient),
324                                                   GINT_TO_POINTER(index) );
325                                 index++;
326                             }
327                         }
329                         gtk_widget_show_all(popupSub);
330                     }
331                 }
332                 gtk_widget_set_sensitive( popupSubHolder, processed );
334                 gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
335                 handled = TRUE;
336             }
337         }
338     }
340     return handled;
344 static char* trim( char* str ) {
345     char* ret = str;
346     while ( *str && (*str == ' ' || *str == '\t') ) {
347         str++;
348     }
349     ret = str;
350     while ( *str ) {
351         str++;
352     }
353     str--;
354     while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
355         *str-- = 0;
356     }
357     return ret;
360 void skipWhitespace( char*& str ) {
361     while ( *str == ' ' || *str == '\t' ) {
362         str++;
363     }
366 bool parseNum( char*& str, int& val ) {
367     val = 0;
368     while ( '0' <= *str && *str <= '9' ) {
369         val = val * 10 + (*str - '0');
370         str++;
371     }
372     bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
373     return retval;
377 void _loadPaletteFile( gchar const *filename )
379     char block[1024];
380     FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
381     if ( f ) {
382         char* result = fgets( block, sizeof(block), f );
383         if ( result ) {
384             if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
385                 bool inHeader = true;
386                 bool hasErr = false;
388                 SwatchPage *onceMore = new SwatchPage();
390                 do {
391                     result = fgets( block, sizeof(block), f );
392                     block[sizeof(block) - 1] = 0;
393                     if ( result ) {
394                         if ( block[0] == '#' ) {
395                             // ignore comment
396                         } else {
397                             char *ptr = block;
398                             // very simple check for header versus entry
399                             while ( *ptr == ' ' || *ptr == '\t' ) {
400                                 ptr++;
401                             }
402                             if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
403                                 // blank line. skip it.
404                             } else if ( '0' <= *ptr && *ptr <= '9' ) {
405                                 // should be an entry link
406                                 inHeader = false;
407                                 ptr = block;
408                                 Glib::ustring name("");
409                                 int r = 0;
410                                 int g = 0;
411                                 int b = 0;
412                                 skipWhitespace(ptr);
413                                 if ( *ptr ) {
414                                     hasErr = parseNum(ptr, r);
415                                     if ( !hasErr ) {
416                                         skipWhitespace(ptr);
417                                         hasErr = parseNum(ptr, g);
418                                     }
419                                     if ( !hasErr ) {
420                                         skipWhitespace(ptr);
421                                         hasErr = parseNum(ptr, b);
422                                     }
423                                     if ( !hasErr && *ptr ) {
424                                         char* n = trim(ptr);
425                                         if (n != NULL) {
426                                             name = n;
427                                         }
428                                     }
429                                     if ( !hasErr ) {
430                                         // Add the entry now
431                                         Glib::ustring nameStr(name);
432                                         ColorItem* item = new ColorItem( r, g, b, nameStr );
433                                         onceMore->_colors.push_back(item);
434                                     }
435                                 } else {
436                                     hasErr = true;
437                                 }
438                             } else {
439                                 if ( !inHeader ) {
440                                     // Hmmm... probably bad. Not quite the format we want?
441                                     hasErr = true;
442                                 } else {
443                                     char* sep = strchr(result, ':');
444                                     if ( sep ) {
445                                         *sep = 0;
446                                         char* val = trim(sep + 1);
447                                         char* name = trim(result);
448                                         if ( *name ) {
449                                             if ( strcmp( "Name", name ) == 0 )
450                                             {
451                                                 onceMore->_name = val;
452                                             }
453                                             else if ( strcmp( "Columns", name ) == 0 )
454                                             {
455                                                 gchar* endPtr = 0;
456                                                 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
457                                                 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
458                                                     // overflow
459                                                 } else if ( (numVal == 0) && (endPtr == val) ) {
460                                                     // failed conversion
461                                                 } else {
462                                                     onceMore->_prefWidth = numVal;
463                                                 }
464                                             }
465                                         } else {
466                                             // error
467                                             hasErr = true;
468                                         }
469                                     } else {
470                                         // error
471                                         hasErr = true;
472                                     }
473                                 }
474                             }
475                         }
476                     }
477                 } while ( result && !hasErr );
478                 if ( !hasErr ) {
479                     possible.push_back(onceMore);
480 #if ENABLE_MAGIC_COLORS
481                     ColorItem::_wireMagicColors( onceMore );
482 #endif // ENABLE_MAGIC_COLORS
483                 } else {
484                     delete onceMore;
485                 }
486             }
487         }
489         fclose(f);
490     }
493 static void loadEmUp()
495     static bool beenHere = false;
496     if ( !beenHere ) {
497         beenHere = true;
499         std::list<gchar *> sources;
500         sources.push_back( profile_path("palettes") );
501         sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
502         sources.push_back( g_strdup(CREATE_PALETTESDIR) );
504         // Use this loop to iterate through a list of possible document locations.
505         while (!sources.empty()) {
506             gchar *dirname = sources.front();
508             if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
509                 && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
510                 GError *err = 0;
511                 GDir *directory = g_dir_open(dirname, 0, &err);
512                 if (!directory) {
513                     gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
514                     g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
515                     g_free(safeDir);
516                 } else {
517                     gchar *filename = 0;
518                     while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
519                         gchar* lower = g_ascii_strdown( filename, -1 );
520 //                        if ( g_str_has_suffix(lower, ".gpl") ) {
521                             gchar* full = g_build_filename(dirname, filename, NULL);
522                             if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
523                                 _loadPaletteFile(full);
524                             }
525                             g_free(full);
526 //                      }
527                         g_free(lower);
528                     }
529                     g_dir_close(directory);
530                 }
531             }
533             // toss the dirname
534             g_free(dirname);
535             sources.pop_front();
536         }
537     }
548 SwatchesPanel& SwatchesPanel::getInstance()
550     return *new SwatchesPanel();
554 /**
555  * Constructor
556  */
557 SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
558     Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
559     _holder(0),
560     _clear(0),
561     _remove(0),
562     _currentIndex(0),
563     _currentDesktop(0),
564     _currentDocument(0)
566     Gtk::RadioMenuItem* hotItem = 0;
567     _holder = new PreviewHolder();
568     _clear = new ColorItem( ege::PaintDef::CLEAR );
569     _remove = new ColorItem( ege::PaintDef::NONE );
570     if (docPalettes.empty()) {
571         SwatchPage *docPalette = new SwatchPage();
573         docPalette->_name = "Auto";
574         docPalettes[0] = docPalette;
575     }
577     loadEmUp();
578     if ( !possible.empty() ) {
579         SwatchPage* first = 0;
580         int index = 0;
581         Glib::ustring targetName;
582         if ( !_prefs_path.empty() ) {
583             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
584             targetName = prefs->getString(_prefs_path + "/palette");
585             if (!targetName.empty()) {
586                 if (targetName == "Auto") {
587                     first = docPalettes[0];
588                 } else {
589                     index++;
590                     for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
591                         if ( (*iter)->_name == targetName ) {
592                             first = *iter;
593                             break;
594                         }
595                         index++;
596                     }
597                 }
598             }
599         }
601         if ( !first ) {
602             first = docPalettes[0];
603             _currentIndex = 0;
604         } else {
605             _currentIndex = index;
606         }
608         _rebuild();
610         Gtk::RadioMenuItem::Group groupOne;
612         int i = 0;
613         std::vector<SwatchPage*> swatchSets = _getSwatchSets();
614         for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) {
615             SwatchPage* curr = *it;
616             Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
617             if ( curr == first ) {
618                 hotItem = single;
619             }
620             _regItem( single, 3, i );
621             i++;
622         }
623     }
626     _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
627     _setTargetFillable(_holder);
629     show_all_children();
631     restorePanelPrefs();
632     if ( hotItem ) {
633         hotItem->set_active();
634     }
637 SwatchesPanel::~SwatchesPanel()
639     _trackDocument( this, 0 );
641     _documentConnection.disconnect();
642     _selChanged.disconnect();
644     if ( _clear ) {
645         delete _clear;
646     }
647     if ( _remove ) {
648         delete _remove;
649     }
650     if ( _holder ) {
651         delete _holder;
652     }
655 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
657     // Must call the parent class or bad things might happen
658     Inkscape::UI::Widget::Panel::setOrientation( how );
660     if ( _holder )
661     {
662         _holder->setOrientation( Gtk::ANCHOR_SOUTH );
663     }
666 void SwatchesPanel::setDesktop( SPDesktop* desktop )
668     if ( desktop != _currentDesktop ) {
669         if ( _currentDesktop ) {
670             _documentConnection.disconnect();
671             _selChanged.disconnect();
672         }
674         _currentDesktop = desktop;
676         if ( desktop ) {
677             _currentDesktop->selection->connectChanged(
678                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
680             _currentDesktop->selection->connectModified(
681                 sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
683             _currentDesktop->connectToolSubselectionChanged(
684                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
686             sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
687             sigc::slot<void, SPDocument*> base2 = first;
688             sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
689             _documentConnection = desktop->connectDocumentReplaced( slot2 );
691             _setDocument( desktop->doc() );
692         } else {
693             _setDocument(0);
694         }
695     }
699 class DocTrack
701 public:
702     DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) :
703         doc(doc),
704         gradientRsrcChanged(gradientRsrcChanged),
705         defsChanged(defsChanged),
706         defsModified(defsModified)
707     {
708     }
710     ~DocTrack()
711     {
712         if (doc) {
713             gradientRsrcChanged.disconnect();
714             defsChanged.disconnect();
715             defsModified.disconnect();
716         }
717     }
719     SPDocument *doc;
720     sigc::connection gradientRsrcChanged;
721     sigc::connection defsChanged;
722     sigc::connection defsModified;
724 private:
725     DocTrack(DocTrack const &); // no copy
726     DocTrack &operator=(DocTrack const &); // no assign
727 };
729 void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document )
731     SPDocument *oldDoc = 0;
732     if (docPerPanel.find(panel) != docPerPanel.end()) {
733         oldDoc = docPerPanel[panel];
734         if (!oldDoc) {
735             docPerPanel.erase(panel); // Should not be needed, but clean up just in case.
736         }
737     }
738     if (oldDoc != document) {
739         if (oldDoc) {
740             docPerPanel[panel] = 0;
741             bool found = false;
742             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
743                 found = (it->second == document);
744             }
745             if (!found) {
746                 for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){
747                     if ((*it)->doc == oldDoc) {
748                         delete *it;
749                         docTrackings.erase(it);
750                         break;
751                     }
752                 }
753             }
754         }
756         if (document) {
757             bool found = false;
758             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
759                 found = (it->second == document);
760             }
761             docPerPanel[panel] = document;
762             if (!found) {
763                 sigc::connection conn1 = document->resources_changed_connect( "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) );
764                 sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) );
765                 sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) );
767                 DocTrack *dt = new DocTrack(document, conn1, conn2, conn3);
768                 docTrackings.push_back(dt);
770                 if (docPalettes.find(document) == docPalettes.end()) {
771                     SwatchPage *docPalette = new SwatchPage();
772                     docPalette->_name = "Auto";
773                     docPalettes[document] = docPalette;
774                 }
775             }
776         }
777     }
779     std::set<SPDocument*> docs;
780     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
781         docs.insert(it->second);
782     }
785 void SwatchesPanel::_setDocument( SPDocument *document )
787     if ( document != _currentDocument ) {
788         _trackDocument(this, document);
789         _currentDocument = document;
790         handleGradientsChange( document );
791     }
794 static void recalcSwatchContents(SPDocument* doc,
795                 std::vector<ColorItem*> &tmpColors,
796                 std::map<ColorItem*, guchar*> &previewMappings,
797                 std::map<ColorItem*, SPGradient*> &gradMappings)
799     std::vector<SPGradient*> newList;
801     const GSList *gradients = doc->get_resource_list("gradient");
802     for (const GSList *item = gradients; item; item = item->next) {
803         SPGradient* grad = SP_GRADIENT(item->data);
804         if ( grad->isSwatch() ) {
805             newList.push_back(SP_GRADIENT(item->data));
806         }
807     }
809     if ( !newList.empty() ) {
810         for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
811         {
812             SPGradient* grad = *it;
813             grad->ensureVector();
814             SPGradientStop first = grad->vector.stops[0];
815             SPColor color = first.color;
816             guint32 together = color.toRGBA32(first.opacity);
818             SPGradientStop second = (*it)->vector.stops[1];
819             SPColor color2 = second.color;
821             Glib::ustring name( grad->getId() );
822             unsigned int r = SP_RGBA32_R_U(together);
823             unsigned int g = SP_RGBA32_G_U(together);
824             unsigned int b = SP_RGBA32_B_U(together);
825             ColorItem* item = new ColorItem( r, g, b, name );
827             gint width = PREVIEW_PIXBUF_WIDTH;
828             gint height = VBLOCK;
829             guchar* px = g_new( guchar, 3 * height * width );
830             nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
832             sp_gradient_render_vector_block_rgb( grad,
833                                                  px, width, height, 3 * width,
834                                                  0, width, TRUE );
836             previewMappings[item] = px;
838             tmpColors.push_back(item);
839             gradMappings[item] = grad;
840         }
841     }
844 void SwatchesPanel::handleGradientsChange(SPDocument *document)
846     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
847     if (docPalette) {
848         std::vector<ColorItem*> tmpColors;
849         std::map<ColorItem*, guchar*> tmpPrevs;
850         std::map<ColorItem*, SPGradient*> tmpGrads;
851         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
853         for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) {
854             it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK);
855         }
857         for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) {
858             it->first->setGradient(it->second);
859         }
861         docPalette->_colors.swap(tmpColors);
862         for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) {
863             delete *it;
864         }
867         // Figure out which SwatchesPanel instances are affected and update them.
869         for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
870             if (it->second == document) {
871                 SwatchesPanel* swp = it->first;
872                 std::vector<SwatchPage*> pages = swp->_getSwatchSets();
873                 SwatchPage* curr = pages[swp->_currentIndex];
874                 if (curr == docPalette) {
875                     swp->_rebuild();
876                 }
877             }
878         }
879     }
882 void SwatchesPanel::handleDefsModified(SPDocument *document)
884     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
885     if (docPalette) {
886         std::vector<ColorItem*> tmpColors;
887         std::map<ColorItem*, guchar*> tmpPrevs;
888         std::map<ColorItem*, SPGradient*> tmpGrads;
889         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
891         int cap = std::min(docPalette->_colors.size(), tmpColors.size());
892         for (int i = 0; i < cap; i++) {
893             ColorItem* newColor = tmpColors[i];
894             ColorItem* oldColor = docPalette->_colors[i];
895             if ( (newColor->def.getType() != oldColor->def.getType()) ||
896                  (newColor->def.getR() != oldColor->def.getR()) ||
897                  (newColor->def.getG() != oldColor->def.getG()) ||
898                  (newColor->def.getB() != oldColor->def.getB()) ) {
899                 oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB());
900             }
901             if (tmpGrads.find(newColor) != tmpGrads.end()) {
902                 oldColor->setGradient(tmpGrads[newColor]);
903             }
904             if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) {
905                 oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK);
906             }
907         }
908     }
911 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
913     std::vector<SwatchPage*> tmp;
914     if (docPalettes.find(_currentDocument) != docPalettes.end()) {
915         tmp.push_back(docPalettes[_currentDocument]);
916     }
918     tmp.insert(tmp.end(), possible.begin(), possible.end());
920     return tmp;
923 void SwatchesPanel::_updateFromSelection()
925     SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0;
926     if ( docPalette ) {
927         Glib::ustring fillId;
928         Glib::ustring strokeId;
930         SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
931         int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
932         switch (result) {
933             case QUERY_STYLE_SINGLE:
934             case QUERY_STYLE_MULTIPLE_AVERAGED:
935             case QUERY_STYLE_MULTIPLE_SAME:
936             {
937                 if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
938                     SPPaintServer* server = tmpStyle->getFillPaintServer();
939                     if ( SP_IS_GRADIENT(server) ) {
940                         SPGradient* target = 0;
941                         SPGradient* grad = SP_GRADIENT(server);
943                         if ( grad->isSwatch() ) {
944                             target = grad;
945                         } else if ( grad->ref ) {
946                             SPGradient *tmp = grad->ref->getObject();
947                             if ( tmp && tmp->isSwatch() ) {
948                                 target = tmp;
949                             }
950                         }
951                         if ( target ) {
952                                                         //XML Tree being used directly here while it shouldn't be
953                             gchar const* id = target->getRepr()->attribute("id");
954                             if ( id ) {
955                                 fillId = id;
956                             }
957                         }
958                     }
959                 }
960                 break;
961             }
962         }
964         result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
965         switch (result) {
966             case QUERY_STYLE_SINGLE:
967             case QUERY_STYLE_MULTIPLE_AVERAGED:
968             case QUERY_STYLE_MULTIPLE_SAME:
969             {
970                 if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
971                     SPPaintServer* server = tmpStyle->getStrokePaintServer();
972                     if ( SP_IS_GRADIENT(server) ) {
973                         SPGradient* target = 0;
974                         SPGradient* grad = SP_GRADIENT(server);
975                         if ( grad->isSwatch() ) {
976                             target = grad;
977                         } else if ( grad->ref ) {
978                             SPGradient *tmp = grad->ref->getObject();
979                             if ( tmp && tmp->isSwatch() ) {
980                                 target = tmp;
981                             }
982                         }
983                         if ( target ) {
985                                                         //XML Tree being used directly here while it shouldn't be
986                             gchar const* id = target->getRepr()->attribute("id");
987                             if ( id ) {
988                                 strokeId = id;
989                             }
990                         }
991                     }
992                 }
993                 break;
994             }
995         }
996         sp_style_unref(tmpStyle);
998         for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
999             ColorItem* item = *it;
1000             bool isFill = (fillId == item->def.descr);
1001             bool isStroke = (strokeId == item->def.descr);
1002             item->setState( isFill, isStroke );
1003         }
1004     }
1007 void SwatchesPanel::_handleAction( int setId, int itemId )
1009     switch( setId ) {
1010         case 3:
1011         {
1012             std::vector<SwatchPage*> pages = _getSwatchSets();
1013             if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) {
1014                 _currentIndex = itemId;
1016                 if ( !_prefs_path.empty() ) {
1017                     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1018                     prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name);
1019                 }
1021                 _rebuild();
1022             }
1023         }
1024         break;
1025     }
1028 void SwatchesPanel::_rebuild()
1030     std::vector<SwatchPage*> pages = _getSwatchSets();
1031     SwatchPage* curr = pages[_currentIndex];
1032     _holder->clear();
1034     if ( curr->_prefWidth > 0 ) {
1035         _holder->setColumnPref( curr->_prefWidth );
1036     }
1037     _holder->freezeUpdates();
1038     // TODO restore once 'clear' works _holder->addPreview(_clear);
1039     _holder->addPreview(_remove);
1040     for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
1041         _holder->addPreview(*it);
1042     }
1043     _holder->thawUpdates();
1049 } //namespace Dialogs
1050 } //namespace UI
1051 } //namespace Inkscape
1054 /*
1055   Local Variables:
1056   mode:c++
1057   c-file-style:"stroustrup"
1058   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1059   indent-tabs-mode:nil
1060   fill-column:99
1061   End:
1062 */
1063 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :