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