Code

Pot and Dutch translation update
[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"
53 #include "dialog-manager.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( SPDesktop* desktop, SPGradient* gr )
118     if ( gr ) {
119         bool shown = false;
120         if ( desktop && desktop->doc() ) {
121             Inkscape::Selection *selection = sp_desktop_selection( desktop );
122             GSList const *items = selection->itemList();
123             if (items) {
124                 SPStyle *query = sp_style_new( desktop->doc() );
125                 int result = objects_query_fillstroke(const_cast<GSList *>(items), query, true);
126                 if ( (result == QUERY_STYLE_MULTIPLE_SAME) || (result == QUERY_STYLE_SINGLE) ) {
127                     // could be pertinent
128                     if (query->fill.isPaintserver()) {
129                         SPPaintServer* server = query->getFillPaintServer();
130                         if ( SP_IS_GRADIENT(server) ) {
131                             SPGradient* grad = SP_GRADIENT(server);
132                             if ( grad->isSwatch() && grad->getId() == gr->getId()) {
133                                 desktop->_dlg_mgr->showDialog("FillAndStroke");
134                                 shown = true;
135                             }
136                         }
137                     }
138                 }
139                 sp_style_unref(query);
140             }
141         }
143         if (!shown) {
144             GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
145             gtk_widget_show( dialog );
146         }
147     }
150 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
152     if ( bounceTarget ) {
153         SwatchesPanel* swp = bouncePanel;
154         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
155         SPDocument *doc = desktop ? desktop->doc() : 0;
156         if (doc) {
157             std::string targetName(bounceTarget->def.descr);
158             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
159             for (const GSList *item = gradients; item; item = item->next) {
160                 SPGradient* grad = SP_GRADIENT(item->data);
161                 if ( targetName == grad->getId() ) {
162                     editGradientImpl( desktop, grad );
163                     break;
164                 }
165             }
166         }
167     }
170 void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData )
172     if ( bounceTarget ) {
173         SwatchesPanel* swp = bouncePanel;
174         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
175         SPDocument *doc = desktop ? desktop->doc() : 0;
176         gint index = GPOINTER_TO_INT(userData);
177         if ( doc && (index >= 0) && (static_cast<guint>(index) < popupItems.size()) ) {
178             Glib::ustring targetName = popupItems[index];
180             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
181             for (const GSList *item = gradients; item; item = item->next) {
182                 SPGradient* grad = SP_GRADIENT(item->data);
183                 if ( targetName == grad->getId() ) {
184                     grad->setSwatch();
185                     sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT,
186                                      _("Add gradient stop"));
187                     break;
188                 }
189             }
190         }
191     }
194 void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ )
196     if ( bounceTarget ) {
197         SwatchesPanel* swp = bouncePanel;
198         SPDesktop* desktop = swp ? swp->getDesktop() : 0;
199         SPDocument *doc = desktop ? desktop->doc() : 0;
200         if (doc) {
201             std::string targetName(bounceTarget->def.descr);
202             const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
203             for (const GSList *item = gradients; item; item = item->next) {
204                 SPGradient* grad = SP_GRADIENT(item->data);
205                 if ( targetName == grad->getId() ) {
206                     grad->setSwatch(false);
207                     sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT,
208                                      _("Delete"));
209                     break;
210                 }
211             }
212         }
213     }
216 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
218     SwatchesPanel *swp = 0;
220     std::map<GtkWidget*, SwatchesPanel*> rawObjects;
221     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
222         rawObjects[GTK_WIDGET(it->first->gobj())] = it->first;
223     }
225     for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) {
226         if (rawObjects.find(curr) != rawObjects.end()) {
227             swp = rawObjects[curr];
228         }
229     }
231     return swp;
234 static void removeit( GtkWidget *widget, gpointer data )
236     gtk_container_remove( GTK_CONTAINER(data), widget );
239 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
241     gboolean handled = FALSE;
243     if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
244         SwatchesPanel* swp = findContainingPanel( widget );
246         if ( !popupMenu ) {
247             popupMenu = gtk_menu_new();
248             GtkWidget* child = 0;
250             //TRANSLATORS: An item in context menu on a colour in the swatches
251             child = gtk_menu_item_new_with_label(_("Set fill"));
252             g_signal_connect( G_OBJECT(child),
253                               "activate",
254                               G_CALLBACK(redirClick),
255                               user_data);
256             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
258             //TRANSLATORS: An item in context menu on a colour in the swatches
259             child = gtk_menu_item_new_with_label(_("Set stroke"));
261             g_signal_connect( G_OBJECT(child),
262                               "activate",
263                               G_CALLBACK(redirSecondaryClick),
264                               user_data);
265             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
267             child = gtk_separator_menu_item_new();
268             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
269             popupExtras.push_back(child);
271             child = gtk_menu_item_new_with_label(_("Delete"));
272             g_signal_connect( G_OBJECT(child),
273                               "activate",
274                               G_CALLBACK(SwatchesPanelHook::deleteGradient),
275                               user_data );
276             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
277             popupExtras.push_back(child);
278             gtk_widget_set_sensitive( child, FALSE );
280             child = gtk_menu_item_new_with_label(_("Edit..."));
281             g_signal_connect( G_OBJECT(child),
282                               "activate",
283                               G_CALLBACK(editGradient),
284                               user_data );
285             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
286             popupExtras.push_back(child);
288             child = gtk_separator_menu_item_new();
289             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
290             popupExtras.push_back(child);
292             child = gtk_menu_item_new_with_label(_("Convert"));
293             gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
294             //popupExtras.push_back(child);
295             //gtk_widget_set_sensitive( child, FALSE );
296             {
297                 popupSubHolder = child;
298                 popupSub = gtk_menu_new();
299                 gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub );
300             }
302             gtk_widget_show_all(popupMenu);
303         }
305         ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
306         if ( item ) {
307             bool show = swp && (swp->getSelectedIndex() == 0);
308             for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
309                 gtk_widget_set_sensitive(*it, show);
310             }
312             bounceTarget = item;
313             bouncePanel = swp;
314             popupItems.clear();
315             if ( popupMenu ) {
316                 gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub);
317                 bool processed = false;
318                 GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET);
319                 if ( wdgt ) {
320                     SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt);
321                     if ( dtw && dtw->desktop ) {
322                         // Pick up all gradients with vectors
323                         const GSList *gradients = sp_document_get_resource_list(dtw->desktop->doc(), "gradient");
324                         gint index = 0;
325                         for (const GSList *curr = gradients; curr; curr = curr->next) {
326                             SPGradient* grad = SP_GRADIENT(curr->data);
327                             if ( grad->hasStops() && !grad->isSwatch() ) {
328                                 //gl = g_slist_prepend(gl, curr->data);
329                                 processed = true;
330                                 GtkWidget *child = gtk_menu_item_new_with_label(grad->getId());
331                                 gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child);
333                                 popupItems.push_back(grad->getId());
334                                 g_signal_connect( G_OBJECT(child),
335                                                   "activate",
336                                                   G_CALLBACK(SwatchesPanelHook::convertGradient),
337                                                   GINT_TO_POINTER(index) );
338                                 index++;
339                             }
340                         }
342                         gtk_widget_show_all(popupSub);
343                     }
344                 }
345                 gtk_widget_set_sensitive( popupSubHolder, processed );
347                 gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
348                 handled = TRUE;
349             }
350         }
351     }
353     return handled;
357 static char* trim( char* str ) {
358     char* ret = str;
359     while ( *str && (*str == ' ' || *str == '\t') ) {
360         str++;
361     }
362     ret = str;
363     while ( *str ) {
364         str++;
365     }
366     str--;
367     while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
368         *str-- = 0;
369     }
370     return ret;
373 void skipWhitespace( char*& str ) {
374     while ( *str == ' ' || *str == '\t' ) {
375         str++;
376     }
379 bool parseNum( char*& str, int& val ) {
380     val = 0;
381     while ( '0' <= *str && *str <= '9' ) {
382         val = val * 10 + (*str - '0');
383         str++;
384     }
385     bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
386     return retval;
390 void _loadPaletteFile( gchar const *filename )
392     char block[1024];
393     FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
394     if ( f ) {
395         char* result = fgets( block, sizeof(block), f );
396         if ( result ) {
397             if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
398                 bool inHeader = true;
399                 bool hasErr = false;
401                 SwatchPage *onceMore = new SwatchPage();
403                 do {
404                     result = fgets( block, sizeof(block), f );
405                     block[sizeof(block) - 1] = 0;
406                     if ( result ) {
407                         if ( block[0] == '#' ) {
408                             // ignore comment
409                         } else {
410                             char *ptr = block;
411                             // very simple check for header versus entry
412                             while ( *ptr == ' ' || *ptr == '\t' ) {
413                                 ptr++;
414                             }
415                             if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
416                                 // blank line. skip it.
417                             } else if ( '0' <= *ptr && *ptr <= '9' ) {
418                                 // should be an entry link
419                                 inHeader = false;
420                                 ptr = block;
421                                 Glib::ustring name("");
422                                 int r = 0;
423                                 int g = 0;
424                                 int b = 0;
425                                 skipWhitespace(ptr);
426                                 if ( *ptr ) {
427                                     hasErr = parseNum(ptr, r);
428                                     if ( !hasErr ) {
429                                         skipWhitespace(ptr);
430                                         hasErr = parseNum(ptr, g);
431                                     }
432                                     if ( !hasErr ) {
433                                         skipWhitespace(ptr);
434                                         hasErr = parseNum(ptr, b);
435                                     }
436                                     if ( !hasErr && *ptr ) {
437                                         char* n = trim(ptr);
438                                         if (n != NULL) {
439                                             name = g_dpgettext2(NULL, "Palette", n);
440                                         }
441                                     }
442                                     if ( !hasErr ) {
443                                         // Add the entry now
444                                         Glib::ustring nameStr(name);
445                                         ColorItem* item = new ColorItem( r, g, b, nameStr );
446                                         onceMore->_colors.push_back(item);
447                                     }
448                                 } else {
449                                     hasErr = true;
450                                 }
451                             } else {
452                                 if ( !inHeader ) {
453                                     // Hmmm... probably bad. Not quite the format we want?
454                                     hasErr = true;
455                                 } else {
456                                     char* sep = strchr(result, ':');
457                                     if ( sep ) {
458                                         *sep = 0;
459                                         char* val = trim(sep + 1);
460                                         char* name = trim(result);
461                                         if ( *name ) {
462                                             if ( strcmp( "Name", name ) == 0 )
463                                             {
464                                                 onceMore->_name = val;
465                                             }
466                                             else if ( strcmp( "Columns", name ) == 0 )
467                                             {
468                                                 gchar* endPtr = 0;
469                                                 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
470                                                 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
471                                                     // overflow
472                                                 } else if ( (numVal == 0) && (endPtr == val) ) {
473                                                     // failed conversion
474                                                 } else {
475                                                     onceMore->_prefWidth = numVal;
476                                                 }
477                                             }
478                                         } else {
479                                             // error
480                                             hasErr = true;
481                                         }
482                                     } else {
483                                         // error
484                                         hasErr = true;
485                                     }
486                                 }
487                             }
488                         }
489                     }
490                 } while ( result && !hasErr );
491                 if ( !hasErr ) {
492                     possible.push_back(onceMore);
493 #if ENABLE_MAGIC_COLORS
494                     ColorItem::_wireMagicColors( onceMore );
495 #endif // ENABLE_MAGIC_COLORS
496                 } else {
497                     delete onceMore;
498                 }
499             }
500         }
502         fclose(f);
503     }
506 static void loadEmUp()
508     static bool beenHere = false;
509     if ( !beenHere ) {
510         beenHere = true;
512         std::list<gchar *> sources;
513         sources.push_back( profile_path("palettes") );
514         sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
515         sources.push_back( g_strdup(CREATE_PALETTESDIR) );
517         // Use this loop to iterate through a list of possible document locations.
518         while (!sources.empty()) {
519             gchar *dirname = sources.front();
521             if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
522                 && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
523                 GError *err = 0;
524                 GDir *directory = g_dir_open(dirname, 0, &err);
525                 if (!directory) {
526                     gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
527                     g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
528                     g_free(safeDir);
529                 } else {
530                     gchar *filename = 0;
531                     while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
532                         gchar* lower = g_ascii_strdown( filename, -1 );
533 //                        if ( g_str_has_suffix(lower, ".gpl") ) {
534                             gchar* full = g_build_filename(dirname, filename, NULL);
535                             if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
536                                 _loadPaletteFile(full);
537                             }
538                             g_free(full);
539 //                      }
540                         g_free(lower);
541                     }
542                     g_dir_close(directory);
543                 }
544             }
546             // toss the dirname
547             g_free(dirname);
548             sources.pop_front();
549         }
550     }
561 SwatchesPanel& SwatchesPanel::getInstance()
563     return *new SwatchesPanel();
567 /**
568  * Constructor
569  */
570 SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
571     Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
572     _holder(0),
573     _clear(0),
574     _remove(0),
575     _currentIndex(0),
576     _currentDesktop(0),
577     _currentDocument(0)
579     Gtk::RadioMenuItem* hotItem = 0;
580     _holder = new PreviewHolder();
581     _clear = new ColorItem( ege::PaintDef::CLEAR );
582     _remove = new ColorItem( ege::PaintDef::NONE );
583     if (docPalettes.empty()) {
584         SwatchPage *docPalette = new SwatchPage();
586         docPalette->_name = "Auto";
587         docPalettes[0] = docPalette;
588     }
590     loadEmUp();
591     if ( !possible.empty() ) {
592         SwatchPage* first = 0;
593         int index = 0;
594         Glib::ustring targetName;
595         if ( !_prefs_path.empty() ) {
596             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
597             targetName = prefs->getString(_prefs_path + "/palette");
598             if (!targetName.empty()) {
599                 if (targetName == "Auto") {
600                     first = docPalettes[0];
601                 } else {
602                     index++;
603                     for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
604                         if ( (*iter)->_name == targetName ) {
605                             first = *iter;
606                             break;
607                         }
608                         index++;
609                     }
610                 }
611             }
612         }
614         if ( !first ) {
615             first = docPalettes[0];
616             _currentIndex = 0;
617         } else {
618             _currentIndex = index;
619         }
621         _rebuild();
623         Gtk::RadioMenuItem::Group groupOne;
625         int i = 0;
626         std::vector<SwatchPage*> swatchSets = _getSwatchSets();
627         for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) {
628             SwatchPage* curr = *it;
629             Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
630             if ( curr == first ) {
631                 hotItem = single;
632             }
633             _regItem( single, 3, i );
634             i++;
635         }
636     }
639     _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
640     _setTargetFillable(_holder);
642     show_all_children();
644     restorePanelPrefs();
645     if ( hotItem ) {
646         hotItem->set_active();
647     }
650 SwatchesPanel::~SwatchesPanel()
652     _trackDocument( this, 0 );
654     _documentConnection.disconnect();
655     _selChanged.disconnect();
657     if ( _clear ) {
658         delete _clear;
659     }
660     if ( _remove ) {
661         delete _remove;
662     }
663     if ( _holder ) {
664         delete _holder;
665     }
668 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
670     // Must call the parent class or bad things might happen
671     Inkscape::UI::Widget::Panel::setOrientation( how );
673     if ( _holder )
674     {
675         _holder->setOrientation( Gtk::ANCHOR_SOUTH );
676     }
679 void SwatchesPanel::setDesktop( SPDesktop* desktop )
681     if ( desktop != _currentDesktop ) {
682         if ( _currentDesktop ) {
683             _documentConnection.disconnect();
684             _selChanged.disconnect();
685         }
687         _currentDesktop = desktop;
689         if ( desktop ) {
690             _currentDesktop->selection->connectChanged(
691                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
693             _currentDesktop->selection->connectModified(
694                 sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
696             _currentDesktop->connectToolSubselectionChanged(
697                 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
699             sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
700             sigc::slot<void, SPDocument*> base2 = first;
701             sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
702             _documentConnection = desktop->connectDocumentReplaced( slot2 );
704             _setDocument( desktop->doc() );
705         } else {
706             _setDocument(0);
707         }
708     }
712 class DocTrack
714 public:
715     DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) :
716         doc(doc),
717         gradientRsrcChanged(gradientRsrcChanged),
718         defsChanged(defsChanged),
719         defsModified(defsModified)
720     {
721     }
723     ~DocTrack()
724     {
725         if (doc) {
726             gradientRsrcChanged.disconnect();
727             defsChanged.disconnect();
728             defsModified.disconnect();
729         }
730     }
732     SPDocument *doc;
733     sigc::connection gradientRsrcChanged;
734     sigc::connection defsChanged;
735     sigc::connection defsModified;
737 private:
738     DocTrack(DocTrack const &); // no copy
739     DocTrack &operator=(DocTrack const &); // no assign
740 };
742 void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document )
744     SPDocument *oldDoc = 0;
745     if (docPerPanel.find(panel) != docPerPanel.end()) {
746         oldDoc = docPerPanel[panel];
747         if (!oldDoc) {
748             docPerPanel.erase(panel); // Should not be needed, but clean up just in case.
749         }
750     }
751     if (oldDoc != document) {
752         if (oldDoc) {
753             docPerPanel[panel] = 0;
754             bool found = false;
755             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
756                 found = (it->second == document);
757             }
758             if (!found) {
759                 for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){
760                     if ((*it)->doc == oldDoc) {
761                         delete *it;
762                         docTrackings.erase(it);
763                         break;
764                     }
765                 }
766             }
767         }
769         if (document) {
770             bool found = false;
771             for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
772                 found = (it->second == document);
773             }
774             docPerPanel[panel] = document;
775             if (!found) {
776                 sigc::connection conn1 = sp_document_resources_changed_connect( document, "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) );
777                 sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) );
778                 sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) );
780                 DocTrack *dt = new DocTrack(document, conn1, conn2, conn3);
781                 docTrackings.push_back(dt);
783                 if (docPalettes.find(document) == docPalettes.end()) {
784                     SwatchPage *docPalette = new SwatchPage();
785                     docPalette->_name = "Auto";
786                     docPalettes[document] = docPalette;
787                 }
788             }
789         }
790     }
792     std::set<SPDocument*> docs;
793     for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
794         docs.insert(it->second);
795     }
798 void SwatchesPanel::_setDocument( SPDocument *document )
800     if ( document != _currentDocument ) {
801         _trackDocument(this, document);
802         _currentDocument = document;
803         handleGradientsChange( document );
804     }
807 static void recalcSwatchContents(SPDocument* doc,
808                 std::vector<ColorItem*> &tmpColors,
809                 std::map<ColorItem*, guchar*> &previewMappings,
810                 std::map<ColorItem*, SPGradient*> &gradMappings)
812     std::vector<SPGradient*> newList;
814     const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
815     for (const GSList *item = gradients; item; item = item->next) {
816         SPGradient* grad = SP_GRADIENT(item->data);
817         if ( grad->isSwatch() ) {
818             newList.push_back(SP_GRADIENT(item->data));
819         }
820     }
822     if ( !newList.empty() ) {
823         std::reverse(newList.begin(), newList.end());
824         for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
825         {
826             SPGradient* grad = *it;
827             grad->ensureVector();
828             SPGradientStop first = grad->vector.stops[0];
829             SPColor color = first.color;
830             guint32 together = color.toRGBA32(first.opacity);
832             SPGradientStop second = (*it)->vector.stops[1];
833             SPColor color2 = second.color;
835             Glib::ustring name( grad->getId() );
836             unsigned int r = SP_RGBA32_R_U(together);
837             unsigned int g = SP_RGBA32_G_U(together);
838             unsigned int b = SP_RGBA32_B_U(together);
839             ColorItem* item = new ColorItem( r, g, b, name );
841             gint width = PREVIEW_PIXBUF_WIDTH;
842             gint height = VBLOCK;
843             guchar* px = g_new( guchar, 3 * height * width );
844             nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
846             sp_gradient_render_vector_block_rgb( grad,
847                                                  px, width, height, 3 * width,
848                                                  0, width, TRUE );
850             previewMappings[item] = px;
852             tmpColors.push_back(item);
853             gradMappings[item] = grad;
854         }
855     }
858 void SwatchesPanel::handleGradientsChange(SPDocument *document)
860     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
861     if (docPalette) {
862         std::vector<ColorItem*> tmpColors;
863         std::map<ColorItem*, guchar*> tmpPrevs;
864         std::map<ColorItem*, SPGradient*> tmpGrads;
865         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
867         for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) {
868             it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK);
869         }
871         for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) {
872             it->first->setGradient(it->second);
873         }
875         docPalette->_colors.swap(tmpColors);
876         for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) {
877             delete *it;
878         }
881         // Figure out which SwatchesPanel instances are affected and update them.
883         for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
884             if (it->second == document) {
885                 SwatchesPanel* swp = it->first;
886                 std::vector<SwatchPage*> pages = swp->_getSwatchSets();
887                 SwatchPage* curr = pages[swp->_currentIndex];
888                 if (curr == docPalette) {
889                     swp->_rebuild();
890                 }
891             }
892         }
893     }
896 void SwatchesPanel::handleDefsModified(SPDocument *document)
898     SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
899     if (docPalette) {
900         std::vector<ColorItem*> tmpColors;
901         std::map<ColorItem*, guchar*> tmpPrevs;
902         std::map<ColorItem*, SPGradient*> tmpGrads;
903         recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
905         if ( tmpColors.size() != docPalette->_colors.size() ) {
906             handleGradientsChange(document);
907         } else {
908             int cap = std::min(docPalette->_colors.size(), tmpColors.size());
909             for (int i = 0; i < cap; i++) {
910                 ColorItem* newColor = tmpColors[i];
911                 ColorItem* oldColor = docPalette->_colors[i];
912                 if ( (newColor->def.getType() != oldColor->def.getType()) ||
913                      (newColor->def.getR() != oldColor->def.getR()) ||
914                      (newColor->def.getG() != oldColor->def.getG()) ||
915                      (newColor->def.getB() != oldColor->def.getB()) ) {
916                     oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB());
917                 }
918                 if (tmpGrads.find(newColor) != tmpGrads.end()) {
919                     oldColor->setGradient(tmpGrads[newColor]);
920                 }
921                 if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) {
922                     oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK);
923                 }
924             }
925         }
926     }
929 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
931     std::vector<SwatchPage*> tmp;
932     if (docPalettes.find(_currentDocument) != docPalettes.end()) {
933         tmp.push_back(docPalettes[_currentDocument]);
934     }
936     tmp.insert(tmp.end(), possible.begin(), possible.end());
938     return tmp;
941 void SwatchesPanel::_updateFromSelection()
943     SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0;
944     if ( docPalette ) {
945         Glib::ustring fillId;
946         Glib::ustring strokeId;
948         SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
949         int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
950         switch (result) {
951             case QUERY_STYLE_SINGLE:
952             case QUERY_STYLE_MULTIPLE_AVERAGED:
953             case QUERY_STYLE_MULTIPLE_SAME:
954             {
955                 if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
956                     SPPaintServer* server = tmpStyle->getFillPaintServer();
957                     if ( SP_IS_GRADIENT(server) ) {
958                         SPGradient* target = 0;
959                         SPGradient* grad = SP_GRADIENT(server);
961                         if ( grad->isSwatch() ) {
962                             target = grad;
963                         } else if ( grad->ref ) {
964                             SPGradient *tmp = grad->ref->getObject();
965                             if ( tmp && tmp->isSwatch() ) {
966                                 target = tmp;
967                             }
968                         }
969                         if ( target ) {
970                             gchar const* id = target->repr->attribute("id");
971                             if ( id ) {
972                                 fillId = id;
973                             }
974                         }
975                     }
976                 }
977                 break;
978             }
979         }
981         result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
982         switch (result) {
983             case QUERY_STYLE_SINGLE:
984             case QUERY_STYLE_MULTIPLE_AVERAGED:
985             case QUERY_STYLE_MULTIPLE_SAME:
986             {
987                 if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
988                     SPPaintServer* server = tmpStyle->getStrokePaintServer();
989                     if ( SP_IS_GRADIENT(server) ) {
990                         SPGradient* target = 0;
991                         SPGradient* grad = SP_GRADIENT(server);
992                         if ( grad->isSwatch() ) {
993                             target = grad;
994                         } else if ( grad->ref ) {
995                             SPGradient *tmp = grad->ref->getObject();
996                             if ( tmp && tmp->isSwatch() ) {
997                                 target = tmp;
998                             }
999                         }
1000                         if ( target ) {
1001                             gchar const* id = target->repr->attribute("id");
1002                             if ( id ) {
1003                                 strokeId = id;
1004                             }
1005                         }
1006                     }
1007                 }
1008                 break;
1009             }
1010         }
1011         sp_style_unref(tmpStyle);
1013         for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
1014             ColorItem* item = *it;
1015             bool isFill = (fillId == item->def.descr);
1016             bool isStroke = (strokeId == item->def.descr);
1017             item->setState( isFill, isStroke );
1018         }
1019     }
1022 void SwatchesPanel::_handleAction( int setId, int itemId )
1024     switch( setId ) {
1025         case 3:
1026         {
1027             std::vector<SwatchPage*> pages = _getSwatchSets();
1028             if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) {
1029                 _currentIndex = itemId;
1031                 if ( !_prefs_path.empty() ) {
1032                     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1033                     prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name);
1034                 }
1036                 _rebuild();
1037             }
1038         }
1039         break;
1040     }
1043 void SwatchesPanel::_rebuild()
1045     std::vector<SwatchPage*> pages = _getSwatchSets();
1046     SwatchPage* curr = pages[_currentIndex];
1047     _holder->clear();
1049     if ( curr->_prefWidth > 0 ) {
1050         _holder->setColumnPref( curr->_prefWidth );
1051     }
1052     _holder->freezeUpdates();
1053     // TODO restore once 'clear' works _holder->addPreview(_clear);
1054     _holder->addPreview(_remove);
1055     for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
1056         _holder->addPreview(*it);
1057     }
1058     _holder->thawUpdates();
1064 } //namespace Dialogs
1065 } //namespace UI
1066 } //namespace Inkscape
1069 /*
1070   Local Variables:
1071   mode:c++
1072   c-file-style:"stroustrup"
1073   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1074   indent-tabs-mode:nil
1075   fill-column:99
1076   End:
1077 */
1078 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :