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/gradient-vector.h"
48 #include "widgets/eek-preview.h"
49 #include "display/nr-plain-stuff.h"
50 #include "sp-gradient-reference.h"
53 namespace Inkscape {
54 namespace UI {
55 namespace Dialogs {
57 #define VBLOCK 16
58 #define PREVIEW_PIXBUF_WIDTH 128
60 void _loadPaletteFile( gchar const *filename );
63 class DocTrack;
65 std::vector<SwatchPage*> possible;
66 static std::map<SPDocument*, SwatchPage*> docPalettes;
67 static std::vector<DocTrack*> docTrackings;
68 static std::map<SwatchesPanel*, SPDocument*> docPerPanel;
72 static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
73 ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
74 if ( item ) {
75 item->buttonClicked(false);
76 }
77 }
79 static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
80 ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
81 if ( item ) {
82 item->buttonClicked(true);
83 }
84 }
86 static GtkWidget* popupMenu = 0;
87 static std::vector<GtkWidget*> popupExtras;
88 static ColorItem* bounceTarget = 0;
89 static SwatchesPanel* bouncePanel = 0;
91 static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
92 {
93 if ( bounceTarget ) {
94 handleClick( GTK_WIDGET(menuitem), bounceTarget );
95 }
96 }
98 static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
99 {
100 if ( bounceTarget ) {
101 handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
102 }
103 }
105 static void editGradientImpl( SPGradient* gr )
106 {
107 if ( gr ) {
108 GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
109 gtk_widget_show( dialog );
110 }
111 }
113 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
114 {
115 if ( bounceTarget ) {
116 SwatchesPanel* swp = bouncePanel;
117 SPDesktop* desktop = swp ? swp->getDesktop() : 0;
118 SPDocument *doc = desktop ? desktop->doc() : 0;
119 if (doc) {
120 std::string targetName(bounceTarget->def.descr);
121 const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
122 for (const GSList *item = gradients; item; item = item->next) {
123 SPGradient* grad = SP_GRADIENT(item->data);
124 if ( targetName == grad->getId() ) {
125 editGradientImpl( grad );
126 break;
127 }
128 }
129 }
130 }
131 }
133 static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
134 {
135 if ( bounceTarget ) {
136 SwatchesPanel* swp = bouncePanel;
137 SPDesktop* desktop = swp ? swp->getDesktop() : 0;
138 SPDocument *doc = desktop ? desktop->doc() : 0;
139 if (doc) {
140 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
142 Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient");
143 repr->setAttribute("osb:paint", "solid");
144 Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
145 stop->setAttribute("offset", "0");
146 stop->setAttribute("style", "stop-color:#000;stop-opacity:1;");
147 repr->appendChild(stop);
148 Inkscape::GC::release(stop);
150 SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL);
152 SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr));
154 Inkscape::GC::release(repr);
157 editGradientImpl( gr );
158 }
159 }
160 }
162 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
163 {
164 SwatchesPanel *swp = 0;
166 std::map<GtkWidget*, SwatchesPanel*> rawObjects;
167 for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
168 rawObjects[GTK_WIDGET(it->first->gobj())] = it->first;
169 }
171 for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) {
172 if (rawObjects.find(curr) != rawObjects.end()) {
173 swp = rawObjects[curr];
174 }
175 }
177 return swp;
178 }
180 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data)
181 {
182 gboolean handled = FALSE;
184 if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
185 SwatchesPanel* swp = findContainingPanel( widget );
187 if ( !popupMenu ) {
188 popupMenu = gtk_menu_new();
189 GtkWidget* child = 0;
191 //TRANSLATORS: An item in context menu on a colour in the swatches
192 child = gtk_menu_item_new_with_label(_("Set fill"));
193 g_signal_connect( G_OBJECT(child),
194 "activate",
195 G_CALLBACK(redirClick),
196 user_data);
197 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
199 //TRANSLATORS: An item in context menu on a colour in the swatches
200 child = gtk_menu_item_new_with_label(_("Set stroke"));
202 g_signal_connect( G_OBJECT(child),
203 "activate",
204 G_CALLBACK(redirSecondaryClick),
205 user_data);
206 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
208 child = gtk_separator_menu_item_new();
209 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
210 popupExtras.push_back(child);
212 child = gtk_menu_item_new_with_label(_("Add"));
213 g_signal_connect( G_OBJECT(child),
214 "activate",
215 G_CALLBACK(addNewGradient),
216 user_data );
217 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
218 popupExtras.push_back(child);
220 child = gtk_menu_item_new_with_label(_("Delete"));
221 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
222 //popupExtras.push_back(child);
223 gtk_widget_set_sensitive( child, FALSE );
225 child = gtk_menu_item_new_with_label(_("Edit..."));
226 g_signal_connect( G_OBJECT(child),
227 "activate",
228 G_CALLBACK(editGradient),
229 user_data );
230 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
231 popupExtras.push_back(child);
233 child = gtk_separator_menu_item_new();
234 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
235 popupExtras.push_back(child);
237 child = gtk_menu_item_new_with_label(_("Convert"));
238 gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
239 //popupExtras.push_back(child);
240 gtk_widget_set_sensitive( child, FALSE );
242 gtk_widget_show_all(popupMenu);
243 }
245 ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
246 if ( item ) {
247 bool show = swp && (swp->getSelectedIndex() == 0);
248 for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
249 gtk_widget_set_sensitive(*it, show);
250 }
252 bounceTarget = item;
253 bouncePanel = swp;
254 if ( popupMenu ) {
255 gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
256 handled = TRUE;
257 }
258 }
259 }
261 return handled;
262 }
265 static char* trim( char* str ) {
266 char* ret = str;
267 while ( *str && (*str == ' ' || *str == '\t') ) {
268 str++;
269 }
270 ret = str;
271 while ( *str ) {
272 str++;
273 }
274 str--;
275 while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
276 *str-- = 0;
277 }
278 return ret;
279 }
281 void skipWhitespace( char*& str ) {
282 while ( *str == ' ' || *str == '\t' ) {
283 str++;
284 }
285 }
287 bool parseNum( char*& str, int& val ) {
288 val = 0;
289 while ( '0' <= *str && *str <= '9' ) {
290 val = val * 10 + (*str - '0');
291 str++;
292 }
293 bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
294 return retval;
295 }
298 void _loadPaletteFile( gchar const *filename )
299 {
300 char block[1024];
301 FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
302 if ( f ) {
303 char* result = fgets( block, sizeof(block), f );
304 if ( result ) {
305 if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
306 bool inHeader = true;
307 bool hasErr = false;
309 SwatchPage *onceMore = new SwatchPage();
311 do {
312 result = fgets( block, sizeof(block), f );
313 block[sizeof(block) - 1] = 0;
314 if ( result ) {
315 if ( block[0] == '#' ) {
316 // ignore comment
317 } else {
318 char *ptr = block;
319 // very simple check for header versus entry
320 while ( *ptr == ' ' || *ptr == '\t' ) {
321 ptr++;
322 }
323 if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
324 // blank line. skip it.
325 } else if ( '0' <= *ptr && *ptr <= '9' ) {
326 // should be an entry link
327 inHeader = false;
328 ptr = block;
329 Glib::ustring name("");
330 int r = 0;
331 int g = 0;
332 int b = 0;
333 skipWhitespace(ptr);
334 if ( *ptr ) {
335 hasErr = parseNum(ptr, r);
336 if ( !hasErr ) {
337 skipWhitespace(ptr);
338 hasErr = parseNum(ptr, g);
339 }
340 if ( !hasErr ) {
341 skipWhitespace(ptr);
342 hasErr = parseNum(ptr, b);
343 }
344 if ( !hasErr && *ptr ) {
345 char* n = trim(ptr);
346 if (n != NULL) {
347 name = n;
348 }
349 }
350 if ( !hasErr ) {
351 // Add the entry now
352 Glib::ustring nameStr(name);
353 ColorItem* item = new ColorItem( r, g, b, nameStr );
354 onceMore->_colors.push_back(item);
355 }
356 } else {
357 hasErr = true;
358 }
359 } else {
360 if ( !inHeader ) {
361 // Hmmm... probably bad. Not quite the format we want?
362 hasErr = true;
363 } else {
364 char* sep = strchr(result, ':');
365 if ( sep ) {
366 *sep = 0;
367 char* val = trim(sep + 1);
368 char* name = trim(result);
369 if ( *name ) {
370 if ( strcmp( "Name", name ) == 0 )
371 {
372 onceMore->_name = val;
373 }
374 else if ( strcmp( "Columns", name ) == 0 )
375 {
376 gchar* endPtr = 0;
377 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
378 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
379 // overflow
380 } else if ( (numVal == 0) && (endPtr == val) ) {
381 // failed conversion
382 } else {
383 onceMore->_prefWidth = numVal;
384 }
385 }
386 } else {
387 // error
388 hasErr = true;
389 }
390 } else {
391 // error
392 hasErr = true;
393 }
394 }
395 }
396 }
397 }
398 } while ( result && !hasErr );
399 if ( !hasErr ) {
400 possible.push_back(onceMore);
401 #if ENABLE_MAGIC_COLORS
402 ColorItem::_wireMagicColors( onceMore );
403 #endif // ENABLE_MAGIC_COLORS
404 } else {
405 delete onceMore;
406 }
407 }
408 }
410 fclose(f);
411 }
412 }
414 static void loadEmUp()
415 {
416 static bool beenHere = false;
417 if ( !beenHere ) {
418 beenHere = true;
420 std::list<gchar *> sources;
421 sources.push_back( profile_path("palettes") );
422 sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
423 sources.push_back( g_strdup(CREATE_PALETTESDIR) );
425 // Use this loop to iterate through a list of possible document locations.
426 while (!sources.empty()) {
427 gchar *dirname = sources.front();
429 if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
430 && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
431 GError *err = 0;
432 GDir *directory = g_dir_open(dirname, 0, &err);
433 if (!directory) {
434 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
435 g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
436 g_free(safeDir);
437 } else {
438 gchar *filename = 0;
439 while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
440 gchar* lower = g_ascii_strdown( filename, -1 );
441 // if ( g_str_has_suffix(lower, ".gpl") ) {
442 gchar* full = g_build_filename(dirname, filename, NULL);
443 if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
444 _loadPaletteFile(full);
445 }
446 g_free(full);
447 // }
448 g_free(lower);
449 }
450 g_dir_close(directory);
451 }
452 }
454 // toss the dirname
455 g_free(dirname);
456 sources.pop_front();
457 }
458 }
459 }
469 SwatchesPanel& SwatchesPanel::getInstance()
470 {
471 return *new SwatchesPanel();
472 }
475 /**
476 * Constructor
477 */
478 SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
479 Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
480 _holder(0),
481 _clear(0),
482 _remove(0),
483 _currentIndex(0),
484 _currentDesktop(0),
485 _currentDocument(0)
486 {
487 Gtk::RadioMenuItem* hotItem = 0;
488 _holder = new PreviewHolder();
489 _clear = new ColorItem( ege::PaintDef::CLEAR );
490 _remove = new ColorItem( ege::PaintDef::NONE );
491 if (docPalettes.empty()) {
492 SwatchPage *docPalette = new SwatchPage();
494 docPalette->_name = "Auto";
495 docPalettes[0] = docPalette;
496 }
498 loadEmUp();
499 if ( !possible.empty() ) {
500 SwatchPage* first = 0;
501 int index = 0;
502 Glib::ustring targetName;
503 if ( !_prefs_path.empty() ) {
504 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
505 targetName = prefs->getString(_prefs_path + "/palette");
506 if (!targetName.empty()) {
507 if (targetName == "Auto") {
508 first = docPalettes[0];
509 } else {
510 index++;
511 for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
512 if ( (*iter)->_name == targetName ) {
513 first = *iter;
514 break;
515 }
516 index++;
517 }
518 }
519 }
520 }
522 if ( !first ) {
523 first = docPalettes[0];
524 _currentIndex = 0;
525 } else {
526 _currentIndex = index;
527 }
529 _rebuild();
531 Gtk::RadioMenuItem::Group groupOne;
533 int i = 0;
534 std::vector<SwatchPage*> swatchSets = _getSwatchSets();
535 for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) {
536 SwatchPage* curr = *it;
537 Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
538 if ( curr == first ) {
539 hotItem = single;
540 }
541 _regItem( single, 3, i );
542 i++;
543 }
544 }
547 _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
548 _setTargetFillable(_holder);
550 show_all_children();
552 restorePanelPrefs();
553 if ( hotItem ) {
554 hotItem->set_active();
555 }
556 }
558 SwatchesPanel::~SwatchesPanel()
559 {
560 _trackDocument( this, 0 );
562 _documentConnection.disconnect();
563 _selChanged.disconnect();
565 if ( _clear ) {
566 delete _clear;
567 }
568 if ( _remove ) {
569 delete _remove;
570 }
571 if ( _holder ) {
572 delete _holder;
573 }
574 }
576 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
577 {
578 // Must call the parent class or bad things might happen
579 Inkscape::UI::Widget::Panel::setOrientation( how );
581 if ( _holder )
582 {
583 _holder->setOrientation( Gtk::ANCHOR_SOUTH );
584 }
585 }
587 void SwatchesPanel::setDesktop( SPDesktop* desktop )
588 {
589 if ( desktop != _currentDesktop ) {
590 if ( _currentDesktop ) {
591 _documentConnection.disconnect();
592 _selChanged.disconnect();
593 }
595 _currentDesktop = desktop;
597 if ( desktop ) {
598 _currentDesktop->selection->connectChanged(
599 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
601 _currentDesktop->selection->connectModified(
602 sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
604 _currentDesktop->connectToolSubselectionChanged(
605 sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
607 sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
608 sigc::slot<void, SPDocument*> base2 = first;
609 sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
610 _documentConnection = desktop->connectDocumentReplaced( slot2 );
612 _setDocument( desktop->doc() );
613 } else {
614 _setDocument(0);
615 }
616 }
617 }
620 class DocTrack
621 {
622 public:
623 DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) :
624 doc(doc),
625 gradientRsrcChanged(gradientRsrcChanged),
626 defsChanged(defsChanged),
627 defsModified(defsModified)
628 {
629 }
631 ~DocTrack()
632 {
633 if (doc) {
634 gradientRsrcChanged.disconnect();
635 defsChanged.disconnect();
636 defsModified.disconnect();
637 }
638 }
640 SPDocument *doc;
641 sigc::connection gradientRsrcChanged;
642 sigc::connection defsChanged;
643 sigc::connection defsModified;
645 private:
646 DocTrack(DocTrack const &); // no copy
647 DocTrack &operator=(DocTrack const &); // no assign
648 };
650 void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document )
651 {
652 SPDocument *oldDoc = 0;
653 if (docPerPanel.find(panel) != docPerPanel.end()) {
654 oldDoc = docPerPanel[panel];
655 if (!oldDoc) {
656 docPerPanel.erase(panel); // Should not be needed, but clean up just in case.
657 }
658 }
659 if (oldDoc != document) {
660 if (oldDoc) {
661 docPerPanel[panel] = 0;
662 bool found = false;
663 for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
664 found = (it->second == document);
665 }
666 if (!found) {
667 for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){
668 if ((*it)->doc == oldDoc) {
669 delete *it;
670 docTrackings.erase(it);
671 break;
672 }
673 }
674 }
675 }
677 if (document) {
678 bool found = false;
679 for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) {
680 found = (it->second == document);
681 }
682 docPerPanel[panel] = document;
683 if (!found) {
684 sigc::connection conn1 = sp_document_resources_changed_connect( document, "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) );
685 sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) );
686 sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) );
688 DocTrack *dt = new DocTrack(document, conn1, conn2, conn3);
689 docTrackings.push_back(dt);
691 if (docPalettes.find(document) == docPalettes.end()) {
692 SwatchPage *docPalette = new SwatchPage();
693 docPalette->_name = "Auto";
694 docPalettes[document] = docPalette;
695 }
696 }
697 }
698 }
700 std::set<SPDocument*> docs;
701 for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
702 docs.insert(it->second);
703 }
704 }
706 void SwatchesPanel::_setDocument( SPDocument *document )
707 {
708 if ( document != _currentDocument ) {
709 _trackDocument(this, document);
710 _currentDocument = document;
711 handleGradientsChange( document );
712 }
713 }
715 static void recalcSwatchContents(SPDocument* doc,
716 std::vector<ColorItem*> &tmpColors,
717 std::map<ColorItem*, guchar*> &previewMappings,
718 std::map<ColorItem*, SPGradient*> &gradMappings)
719 {
720 std::vector<SPGradient*> newList;
722 const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
723 for (const GSList *item = gradients; item; item = item->next) {
724 SPGradient* grad = SP_GRADIENT(item->data);
725 if ( grad->isSwatch() ) {
726 newList.push_back(SP_GRADIENT(item->data));
727 }
728 }
730 if ( !newList.empty() ) {
731 for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
732 {
733 SPGradient* grad = *it;
734 sp_gradient_ensure_vector( grad );
735 SPGradientStop first = grad->vector.stops[0];
736 SPColor color = first.color;
737 guint32 together = color.toRGBA32(first.opacity);
739 SPGradientStop second = (*it)->vector.stops[1];
740 SPColor color2 = second.color;
742 Glib::ustring name( grad->getId() );
743 unsigned int r = SP_RGBA32_R_U(together);
744 unsigned int g = SP_RGBA32_G_U(together);
745 unsigned int b = SP_RGBA32_B_U(together);
746 ColorItem* item = new ColorItem( r, g, b, name );
748 gint width = PREVIEW_PIXBUF_WIDTH;
749 gint height = VBLOCK;
750 guchar* px = g_new( guchar, 3 * height * width );
751 nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
753 sp_gradient_render_vector_block_rgb( grad,
754 px, width, height, 3 * width,
755 0, width, TRUE );
757 previewMappings[item] = px;
759 tmpColors.push_back(item);
760 gradMappings[item] = grad;
761 }
762 }
763 }
765 void SwatchesPanel::handleGradientsChange(SPDocument *document)
766 {
767 SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
768 if (docPalette) {
769 std::vector<ColorItem*> tmpColors;
770 std::map<ColorItem*, guchar*> tmpPrevs;
771 std::map<ColorItem*, SPGradient*> tmpGrads;
772 recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
774 for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) {
775 it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK);
776 }
778 for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) {
779 it->first->setGradient(it->second);
780 }
782 docPalette->_colors.swap(tmpColors);
783 for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) {
784 delete *it;
785 }
788 // Figure out which SwatchesPanel instances are affected and update them.
790 for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) {
791 if (it->second == document) {
792 SwatchesPanel* swp = it->first;
793 std::vector<SwatchPage*> pages = swp->_getSwatchSets();
794 SwatchPage* curr = pages[swp->_currentIndex];
795 if (curr == docPalette) {
796 swp->_rebuild();
797 }
798 }
799 }
800 }
801 }
803 void SwatchesPanel::handleDefsModified(SPDocument *document)
804 {
805 SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0;
806 if (docPalette) {
807 std::vector<ColorItem*> tmpColors;
808 std::map<ColorItem*, guchar*> tmpPrevs;
809 std::map<ColorItem*, SPGradient*> tmpGrads;
810 recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads);
812 int cap = std::min(docPalette->_colors.size(), tmpColors.size());
813 for (int i = 0; i < cap; i++) {
814 ColorItem* newColor = tmpColors[i];
815 ColorItem* oldColor = docPalette->_colors[i];
816 if ( (newColor->def.getType() != oldColor->def.getType()) ||
817 (newColor->def.getR() != oldColor->def.getR()) ||
818 (newColor->def.getG() != oldColor->def.getG()) ||
819 (newColor->def.getB() != oldColor->def.getB()) ) {
820 oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB());
821 }
822 if (tmpGrads.find(newColor) != tmpGrads.end()) {
823 oldColor->setGradient(tmpGrads[newColor]);
824 }
825 if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) {
826 oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK);
827 }
828 }
829 }
830 }
832 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
833 {
834 std::vector<SwatchPage*> tmp;
835 if (docPalettes.find(_currentDocument) != docPalettes.end()) {
836 tmp.push_back(docPalettes[_currentDocument]);
837 }
839 tmp.insert(tmp.end(), possible.begin(), possible.end());
841 return tmp;
842 }
844 void SwatchesPanel::_updateFromSelection()
845 {
846 SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0;
847 if ( docPalette ) {
848 Glib::ustring fillId;
849 Glib::ustring strokeId;
851 SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
852 int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
853 switch (result) {
854 case QUERY_STYLE_SINGLE:
855 case QUERY_STYLE_MULTIPLE_AVERAGED:
856 case QUERY_STYLE_MULTIPLE_SAME:
857 {
858 if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
859 SPPaintServer* server = tmpStyle->getFillPaintServer();
860 if ( SP_IS_GRADIENT(server) ) {
861 SPGradient* target = 0;
862 SPGradient* grad = SP_GRADIENT(server);
863 if (grad->repr->attribute("osb:paint")) {
864 target = grad;
865 } else if ( grad->ref ) {
866 SPGradient *tmp = grad->ref->getObject();
867 if ( tmp && tmp->repr->attribute("osb:paint") ) {
868 target = tmp;
869 }
870 }
871 if ( target ) {
872 gchar const* id = target->repr->attribute("id");
873 if ( id ) {
874 fillId = id;
875 }
876 }
877 }
878 }
879 break;
880 }
881 }
883 result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
884 switch (result) {
885 case QUERY_STYLE_SINGLE:
886 case QUERY_STYLE_MULTIPLE_AVERAGED:
887 case QUERY_STYLE_MULTIPLE_SAME:
888 {
889 if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
890 SPPaintServer* server = tmpStyle->getStrokePaintServer();
891 if ( SP_IS_GRADIENT(server) ) {
892 SPGradient* target = 0;
893 SPGradient* grad = SP_GRADIENT(server);
894 if (grad->repr->attribute("osb:paint")) {
895 target = grad;
896 } else if ( grad->ref ) {
897 SPGradient *tmp = grad->ref->getObject();
898 if ( tmp && tmp->repr->attribute("osb:paint") ) {
899 target = tmp;
900 }
901 }
902 if ( target ) {
903 gchar const* id = target->repr->attribute("id");
904 if ( id ) {
905 strokeId = id;
906 }
907 }
908 }
909 }
910 break;
911 }
912 }
913 sp_style_unref(tmpStyle);
915 for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
916 ColorItem* item = *it;
917 bool isFill = (fillId == item->def.descr);
918 bool isStroke = (strokeId == item->def.descr);
919 item->setState( isFill, isStroke );
920 }
921 }
922 }
924 void SwatchesPanel::_handleAction( int setId, int itemId )
925 {
926 switch( setId ) {
927 case 3:
928 {
929 std::vector<SwatchPage*> pages = _getSwatchSets();
930 if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) {
931 _currentIndex = itemId;
933 if ( !_prefs_path.empty() ) {
934 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
935 prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name);
936 }
938 _rebuild();
939 }
940 }
941 break;
942 }
943 }
945 void SwatchesPanel::_rebuild()
946 {
947 std::vector<SwatchPage*> pages = _getSwatchSets();
948 SwatchPage* curr = pages[_currentIndex];
949 _holder->clear();
951 if ( curr->_prefWidth > 0 ) {
952 _holder->setColumnPref( curr->_prefWidth );
953 }
954 _holder->freezeUpdates();
955 // TODO restore once 'clear' works _holder->addPreview(_clear);
956 _holder->addPreview(_remove);
957 for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
958 _holder->addPreview(*it);
959 }
960 _holder->thawUpdates();
961 }
966 } //namespace Dialogs
967 } //namespace UI
968 } //namespace Inkscape
971 /*
972 Local Variables:
973 mode:c++
974 c-file-style:"stroustrup"
975 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
976 indent-tabs-mode:nil
977 fill-column:99
978 End:
979 */
980 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :