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*/ )
101 {
102 if ( bounceTarget ) {
103 handleClick( GTK_WIDGET(menuitem), bounceTarget );
104 }
105 }
107 static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
108 {
109 if ( bounceTarget ) {
110 handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
111 }
112 }
114 static void editGradientImpl( SPGradient* gr )
115 {
116 if ( gr ) {
117 GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
118 gtk_widget_show( dialog );
119 }
120 }
122 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
123 {
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 }
140 }
142 static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
143 {
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 }
169 }
171 void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData )
172 {
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 }
197 }
199 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
200 {
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;
215 }
217 static void removeit( GtkWidget *widget, gpointer data )
218 {
219 gtk_container_remove( GTK_CONTAINER(data), widget );
220 }
222 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
223 {
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;
341 }
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;
358 }
360 void skipWhitespace( char*& str ) {
361 while ( *str == ' ' || *str == '\t' ) {
362 str++;
363 }
364 }
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;
374 }
377 void _loadPaletteFile( gchar const *filename )
378 {
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 }
491 }
493 static void loadEmUp()
494 {
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 }
538 }
548 SwatchesPanel& SwatchesPanel::getInstance()
549 {
550 return *new SwatchesPanel();
551 }
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)
565 {
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 }
635 }
637 SwatchesPanel::~SwatchesPanel()
638 {
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 }
653 }
655 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
656 {
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 }
664 }
666 void SwatchesPanel::setDesktop( SPDesktop* desktop )
667 {
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 }
696 }
699 class DocTrack
700 {
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 )
730 {
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 }
783 }
785 void SwatchesPanel::_setDocument( SPDocument *document )
786 {
787 if ( document != _currentDocument ) {
788 _trackDocument(this, document);
789 _currentDocument = document;
790 handleGradientsChange( document );
791 }
792 }
794 static void recalcSwatchContents(SPDocument* doc,
795 std::vector<ColorItem*> &tmpColors,
796 std::map<ColorItem*, guchar*> &previewMappings,
797 std::map<ColorItem*, SPGradient*> &gradMappings)
798 {
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 }
842 }
844 void SwatchesPanel::handleGradientsChange(SPDocument *document)
845 {
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 }
880 }
882 void SwatchesPanel::handleDefsModified(SPDocument *document)
883 {
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 }
909 }
911 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
912 {
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;
921 }
923 void SwatchesPanel::_updateFromSelection()
924 {
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 }
1005 }
1007 void SwatchesPanel::_handleAction( int setId, int itemId )
1008 {
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 }
1026 }
1028 void SwatchesPanel::_rebuild()
1029 {
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();
1044 }
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 :