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*/ )
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( SPDesktop* desktop, SPGradient* gr )
117 {
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 }
148 }
150 static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
151 {
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 }
168 }
170 void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData )
171 {
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 }
192 }
194 void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ )
195 {
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 }
214 }
216 static SwatchesPanel* findContainingPanel( GtkWidget *widget )
217 {
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;
232 }
234 static void removeit( GtkWidget *widget, gpointer data )
235 {
236 gtk_container_remove( GTK_CONTAINER(data), widget );
237 }
239 gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data )
240 {
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;
354 }
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;
371 }
373 void skipWhitespace( char*& str ) {
374 while ( *str == ' ' || *str == '\t' ) {
375 str++;
376 }
377 }
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;
387 }
390 void _loadPaletteFile( gchar const *filename )
391 {
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 = 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 }
504 }
506 static void loadEmUp()
507 {
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 }
551 }
561 SwatchesPanel& SwatchesPanel::getInstance()
562 {
563 return *new SwatchesPanel();
564 }
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)
578 {
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 }
648 }
650 SwatchesPanel::~SwatchesPanel()
651 {
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 }
666 }
668 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
669 {
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 }
677 }
679 void SwatchesPanel::setDesktop( SPDesktop* desktop )
680 {
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 }
709 }
712 class DocTrack
713 {
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 )
743 {
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 }
796 }
798 void SwatchesPanel::_setDocument( SPDocument *document )
799 {
800 if ( document != _currentDocument ) {
801 _trackDocument(this, document);
802 _currentDocument = document;
803 handleGradientsChange( document );
804 }
805 }
807 static void recalcSwatchContents(SPDocument* doc,
808 std::vector<ColorItem*> &tmpColors,
809 std::map<ColorItem*, guchar*> &previewMappings,
810 std::map<ColorItem*, SPGradient*> &gradMappings)
811 {
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 }
856 }
858 void SwatchesPanel::handleGradientsChange(SPDocument *document)
859 {
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 }
894 }
896 void SwatchesPanel::handleDefsModified(SPDocument *document)
897 {
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 }
927 }
929 std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const
930 {
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;
939 }
941 void SwatchesPanel::_updateFromSelection()
942 {
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 }
1020 }
1022 void SwatchesPanel::_handleAction( int setId, int itemId )
1023 {
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 }
1041 }
1043 void SwatchesPanel::_rebuild()
1044 {
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();
1059 }
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:encoding=utf-8:textwidth=99 :