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