2
* @brief Color swatches dialog
8
* Copyright (C) 2005 Jon A. Cruz
9
* Copyright (C) 2008 John Bintz
11
* Released under GNU GPL, read the file 'COPYING' for more information
17
#include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
18
#include <gtk/gtkdnd.h>
19
#include <gtk/gtkmenu.h>
20
#include <gtk/gtkmenuitem.h>
21
#include <gtk/gtkseparatormenuitem.h>
22
#include <glibmm/i18n.h>
23
#include <gdkmm/pixbuf.h>
26
#include "desktop-handles.h"
27
#include "desktop-style.h"
29
#include "document-private.h"
30
#include "extension/db.h"
34
#include "io/resource.h"
35
#include "message-context.h"
36
#include "path-prefix.h"
37
#include "preferences.h"
39
#include "svg/svg-color.h"
40
#include "sp-gradient-fns.h"
41
#include "sp-gradient.h"
42
#include "sp-gradient-vector.h"
45
#include "widgets/gradient-vector.h"
46
#include "widgets/eek-preview.h"
47
#include "display/nr-plain-stuff.h"
48
#include "sp-gradient-reference.h"
56
void _loadPaletteFile( gchar const *filename );
59
* The color swatch you see on screen as a clickable box.
61
class ColorItem : public Inkscape::UI::Previewable
63
friend void _loadPaletteFile( gchar const *filename );
65
ColorItem( ege::PaintDef::ColorType type );
66
ColorItem( unsigned int r, unsigned int g, unsigned int b,
67
Glib::ustring& name );
69
ColorItem(ColorItem const &other);
70
virtual ColorItem &operator=(ColorItem const &other);
71
virtual Gtk::Widget* getPreview(PreviewStyle style,
75
void buttonClicked(bool secondary = false);
77
void setState( bool fill, bool stroke );
78
bool isFill() { return _isFill; }
79
bool isStroke() { return _isStroke; }
85
static void _dropDataIn( GtkWidget *widget,
86
GdkDragContext *drag_context,
88
GtkSelectionData *data,
93
static void _dragGetColorData( GtkWidget *widget,
94
GdkDragContext *drag_context,
95
GtkSelectionData *data,
100
static void _wireMagicColors( void* p );
101
static void _colorDefChanged(void* data);
103
void _linkTint( ColorItem& other, int percent );
104
void _linkTone( ColorItem& other, int percent, int grayLevel );
107
std::vector<Gtk::Widget*> _previews;
116
std::vector<ColorItem*> _listeners;
121
ColorItem::ColorItem(ege::PaintDef::ColorType type) :
134
ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) :
135
def( r, g, b, name ),
147
ColorItem::~ColorItem()
151
ColorItem::ColorItem(ColorItem const &other) :
152
Inkscape::UI::Previewable()
154
if ( this != &other ) {
159
ColorItem &ColorItem::operator=(ColorItem const &other)
161
if ( this != &other ) {
164
// TODO - correct linkage
165
_linkSrc = other._linkSrc;
171
void ColorItem::setState( bool fill, bool stroke )
173
if ( (_isFill != fill) || (_isStroke != stroke) ) {
177
for ( std::vector<Gtk::Widget*>::iterator it = _previews.begin(); it != _previews.end(); ++it ) {
178
Gtk::Widget* widget = *it;
179
if ( IS_EEK_PREVIEW(widget->gobj()) ) {
180
EekPreview * preview = EEK_PREVIEW(widget->gobj());
182
int val = eek_preview_get_linked( preview );
183
val &= ~(PREVIEW_FILL | PREVIEW_STROKE);
188
val |= PREVIEW_STROKE;
190
eek_preview_set_linked( preview, static_cast<LinkType>(val) );
200
JustForNow() : _prefWidth(0) {}
204
std::vector<ColorItem*> _colors;
207
static std::vector<JustForNow*> possible;
210
static std::vector<std::string> mimeStrings;
211
static std::map<std::string, guint> mimeToInt;
213
static std::map<ColorItem*, guchar*> previewMap;
214
static std::map<ColorItem*, SPGradient*> gradMap; // very temporary workaround.
216
void ColorItem::_dragGetColorData( GtkWidget */*widget*/,
217
GdkDragContext */*drag_context*/,
218
GtkSelectionData *data,
223
ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
225
if ( info < mimeStrings.size() ) {
226
key = mimeStrings[info];
228
g_warning("ERROR: unknown value (%d)", info);
231
if ( !key.empty() ) {
235
item->def.getMIMEData(key, tmp, len, format);
237
GdkAtom dataAtom = gdk_atom_intern( key.c_str(), FALSE );
238
gtk_selection_data_set( data, dataAtom, format, (guchar*)tmp, len );
244
static void dragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data )
246
ColorItem* item = reinterpret_cast<ColorItem*>(data);
249
using Inkscape::IO::Resource::get_path;
250
using Inkscape::IO::Resource::ICONS;
251
using Inkscape::IO::Resource::SYSTEM;
255
if (item->def.getType() != ege::PaintDef::RGB){
256
GError *error = NULL;
258
gsize bytesWritten = 0;
259
gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"),
264
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(localFilename, width, height, FALSE, &error);
265
g_free(localFilename);
266
gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 );
268
GdkPixbuf* pixbuf = 0;
269
if ( gradMap.find(item) == gradMap.end() ){
270
Glib::RefPtr<Gdk::Pixbuf> thumb = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, width, height );
271
guint32 fillWith = (0xff000000 & (item->def.getR() << 24))
272
| (0x00ff0000 & (item->def.getG() << 16))
273
| (0x0000ff00 & (item->def.getB() << 8));
274
thumb->fill( fillWith );
275
pixbuf = thumb->gobj();
277
SPGradient* grad = gradMap[item];
279
guchar* px = g_new( guchar, 3 * height * width );
280
nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
282
sp_gradient_render_vector_block_rgb( grad,
283
px, width, height, 3 * width,
286
pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
287
width, height, width * 3,
288
0, // add delete function
291
gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 );
298
// gboolean dragDropColorData( GtkWidget *widget,
299
// GdkDragContext *drag_context,
303
// gpointer user_data)
310
static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
311
ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
313
item->buttonClicked(false);
317
static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
318
ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
320
item->buttonClicked(true);
324
static gboolean handleEnterNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) {
325
ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
327
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
329
gchar* msg = g_strdup_printf(_("Color: <b>%s</b>; <b>Click</b> to set fill, <b>Shift+click</b> to set stroke"),
330
item->def.descr.c_str());
331
desktop->tipsMessageContext()->set(Inkscape::INFORMATION_MESSAGE, msg);
338
static gboolean handleLeaveNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) {
339
ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
341
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
343
desktop->tipsMessageContext()->clear();
349
static GtkWidget* popupMenu = 0;
350
static std::vector<GtkWidget*> popupExtras;
351
static ColorItem* bounceTarget = 0;
353
static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
355
if ( bounceTarget ) {
356
handleClick( GTK_WIDGET(menuitem), bounceTarget );
360
static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
362
if ( bounceTarget ) {
363
handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
367
static void editGradientImpl( SPGradient* gr )
370
GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
371
gtk_widget_show( dialog );
375
static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
377
if ( bounceTarget ) {
378
SwatchesPanel* swp = bounceTarget->ptr ? reinterpret_cast<SwatchesPanel*>(bounceTarget->ptr) : 0;
379
SPDesktop* desktop = swp ? swp->getDesktop() : 0;
380
SPDocument *doc = desktop ? desktop->doc() : 0;
382
std::string targetName(bounceTarget->def.descr);
383
const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
384
for (const GSList *item = gradients; item; item = item->next) {
385
SPGradient* grad = SP_GRADIENT(item->data);
386
if ( targetName == grad->id ) {
387
editGradientImpl( grad );
395
static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
397
if ( bounceTarget ) {
398
SwatchesPanel* swp = bounceTarget->ptr ? reinterpret_cast<SwatchesPanel*>(bounceTarget->ptr) : 0;
399
SPDesktop* desktop = swp ? swp->getDesktop() : 0;
400
SPDocument *doc = desktop ? desktop->doc() : 0;
402
Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
404
Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient");
405
repr->setAttribute("osb:paint", "solid");
406
Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
407
stop->setAttribute("offset", "0");
408
stop->setAttribute("style", "stop-color:#000;stop-opacity:1;");
409
repr->appendChild(stop);
410
Inkscape::GC::release(stop);
412
SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL);
414
SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr));
416
Inkscape::GC::release(repr);
419
editGradientImpl( gr );
420
// Work-around for timing of gradient addition change. Must follow edit.
422
swp->handleGradientsChange();
428
static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gpointer user_data)
430
gboolean handled = FALSE;
432
if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
434
popupMenu = gtk_menu_new();
435
GtkWidget* child = 0;
437
//TRANSLATORS: An item in context menu on a colour in the swatches
438
child = gtk_menu_item_new_with_label(_("Set fill"));
439
g_signal_connect( G_OBJECT(child),
441
G_CALLBACK(redirClick),
443
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
445
//TRANSLATORS: An item in context menu on a colour in the swatches
446
child = gtk_menu_item_new_with_label(_("Set stroke"));
448
g_signal_connect( G_OBJECT(child),
450
G_CALLBACK(redirSecondaryClick),
452
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
454
child = gtk_separator_menu_item_new();
455
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
456
popupExtras.push_back(child);
458
child = gtk_menu_item_new_with_label(_("Add"));
459
g_signal_connect( G_OBJECT(child),
461
G_CALLBACK(addNewGradient),
463
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
464
popupExtras.push_back(child);
466
child = gtk_menu_item_new_with_label(_("Delete"));
467
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
468
//popupExtras.push_back(child);
469
gtk_widget_set_sensitive( child, FALSE );
471
child = gtk_menu_item_new_with_label(_("Edit..."));
472
g_signal_connect( G_OBJECT(child),
474
G_CALLBACK(editGradient),
476
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
477
popupExtras.push_back(child);
479
child = gtk_separator_menu_item_new();
480
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
481
popupExtras.push_back(child);
483
child = gtk_menu_item_new_with_label(_("Convert"));
484
gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
485
//popupExtras.push_back(child);
486
gtk_widget_set_sensitive( child, FALSE );
488
gtk_widget_show_all(popupMenu);
491
ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
493
SwatchesPanel* swp = item->ptr ? reinterpret_cast<SwatchesPanel*>(item->ptr) : 0;
494
bool show = swp && (swp->getSelectedIndex() == 0);
495
for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) {
496
gtk_widget_set_sensitive(*it, show);
501
gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
510
static void dieDieDie( GtkObject *obj, gpointer user_data )
512
g_message("die die die %p %p", obj, user_data );
515
#include "color.h" // for SP_RGBA32_U_COMPOSE
517
void ColorItem::_dropDataIn( GtkWidget */*widget*/,
518
GdkDragContext */*drag_context*/,
519
gint /*x*/, gint /*y*/,
520
GtkSelectionData */*data*/,
522
guint /*event_time*/,
523
gpointer /*user_data*/)
527
static bool bruteForce( SPDocument* document, Inkscape::XML::Node* node, Glib::ustring const& match, int r, int g, int b )
529
bool changed = false;
532
gchar const * val = node->attribute("inkscape:x-fill-tag");
533
if ( val && (match == val) ) {
534
SPObject *obj = document->getObjectByRepr( node );
537
sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) );
538
SPCSSAttr *css = sp_repr_css_attr_new();
539
sp_repr_css_set_property( css, "fill", c );
541
sp_desktop_apply_css_recursive( (SPItem*)obj, css, true );
542
((SPItem*)obj)->updateRepr();
547
val = node->attribute("inkscape:x-stroke-tag");
548
if ( val && (match == val) ) {
549
SPObject *obj = document->getObjectByRepr( node );
552
sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) );
553
SPCSSAttr *css = sp_repr_css_attr_new();
554
sp_repr_css_set_property( css, "stroke", c );
556
sp_desktop_apply_css_recursive( (SPItem*)obj, css, true );
557
((SPItem*)obj)->updateRepr();
562
Inkscape::XML::Node* first = node->firstChild();
563
changed |= bruteForce( document, first, match, r, g, b );
565
changed |= bruteForce( document, node->next(), match, r, g, b );
571
void ColorItem::_colorDefChanged(void* data)
573
ColorItem* item = reinterpret_cast<ColorItem*>(data);
575
for ( std::vector<Gtk::Widget*>::iterator it = item->_previews.begin(); it != item->_previews.end(); ++it ) {
576
Gtk::Widget* widget = *it;
577
if ( IS_EEK_PREVIEW(widget->gobj()) ) {
578
EekPreview * preview = EEK_PREVIEW(widget->gobj());
579
eek_preview_set_color( preview,
580
(item->def.getR() << 8) | item->def.getR(),
581
(item->def.getG() << 8) | item->def.getG(),
582
(item->def.getB() << 8) | item->def.getB() );
584
eek_preview_set_linked( preview, (LinkType)((item->_linkSrc ? PREVIEW_LINK_IN:0)
585
| (item->_listeners.empty() ? 0:PREVIEW_LINK_OUT)
586
| (item->_isLive ? PREVIEW_LINK_OTHER:0)) );
588
widget->queue_draw();
592
for ( std::vector<ColorItem*>::iterator it = item->_listeners.begin(); it != item->_listeners.end(); ++it ) {
593
guint r = item->def.getR();
594
guint g = item->def.getG();
595
guint b = item->def.getB();
597
if ( (*it)->_linkIsTone ) {
598
r = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * r) ) / 100;
599
g = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * g) ) / 100;
600
b = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * b) ) / 100;
602
r = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * r) ) / 100;
603
g = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * g) ) / 100;
604
b = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * b) ) / 100;
607
(*it)->def.setRGB( r, g, b );
611
// Look for objects using this color
613
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
615
SPDocument* document = sp_desktop_document( desktop );
616
Inkscape::XML::Node *rroot = sp_document_repr_root( document );
619
// Find where this thing came from
620
Glib::ustring paletteName;
623
for ( std::vector<JustForNow*>::iterator it2 = possible.begin(); it2 != possible.end() && !found; ++it2 ) {
624
JustForNow* curr = *it2;
626
for ( std::vector<ColorItem*>::iterator zz = curr->_colors.begin(); zz != curr->_colors.end(); ++zz ) {
629
paletteName = curr->_name;
637
if ( !paletteName.empty() ) {
638
gchar* str = g_strdup_printf("%d|", index);
639
paletteName.insert( 0, str );
643
if ( bruteForce( document, rroot, paletteName, item->def.getR(), item->def.getG(), item->def.getB() ) ) {
644
sp_document_done( document , SP_VERB_DIALOG_SWATCHES,
645
_("Change color definition"));
655
Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, ::PreviewSize size, guint ratio)
657
Gtk::Widget* widget = 0;
658
if ( style == PREVIEW_STYLE_BLURB) {
659
Gtk::Label *lbl = new Gtk::Label(def.descr);
660
lbl->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER);
663
// Glib::ustring blank(" ");
664
// if ( size == Inkscape::ICON_SIZE_MENU || size == Inkscape::ICON_SIZE_DECORATION ) {
668
GtkWidget* eekWidget = eek_preview_new();
669
EekPreview * preview = EEK_PREVIEW(eekWidget);
670
Gtk::Widget* newBlot = Glib::wrap(eekWidget);
672
if ( previewMap.find(this) == previewMap.end() ){
673
eek_preview_set_color( preview, (def.getR() << 8) | def.getR(),
674
(def.getG() << 8) | def.getG(),
675
(def.getB() << 8) | def.getB());
677
guchar* px = previewMap[this];
680
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
681
width, height, width * 3,
682
0, // add delete function
684
eek_preview_set_pixbuf( preview, pixbuf );
686
if ( def.getType() != ege::PaintDef::RGB ) {
687
using Inkscape::IO::Resource::get_path;
688
using Inkscape::IO::Resource::ICONS;
689
using Inkscape::IO::Resource::SYSTEM;
690
GError *error = NULL;
692
gsize bytesWritten = 0;
693
gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"),
698
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(localFilename, &error);
700
g_warning("Null pixbuf for %p [%s]", localFilename, localFilename );
702
g_free(localFilename);
704
eek_preview_set_pixbuf( preview, pixbuf );
707
eek_preview_set_details( preview, (::PreviewStyle)style, (::ViewType)view, (::PreviewSize)size, ratio );
708
eek_preview_set_linked( preview, (LinkType)((_linkSrc ? PREVIEW_LINK_IN:0)
709
| (_listeners.empty() ? 0:PREVIEW_LINK_OUT)
710
| (_isLive ? PREVIEW_LINK_OTHER:0)) );
712
def.addCallback( _colorDefChanged, this );
714
GValue val = {0, {{0}, {0}}};
715
g_value_init( &val, G_TYPE_BOOLEAN );
716
g_value_set_boolean( &val, FALSE );
717
g_object_set_property( G_OBJECT(preview), "focus-on-click", &val );
720
Gtk::Button *btn = new Gtk::Button(blank);
722
color.set_rgb((_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b);
723
btn->modify_bg(Gtk::STATE_NORMAL, color);
724
btn->modify_bg(Gtk::STATE_ACTIVE, color);
725
btn->modify_bg(Gtk::STATE_PRELIGHT, color);
726
btn->modify_bg(Gtk::STATE_SELECTED, color);
728
Gtk::Widget* newBlot = btn;
731
tips.set_tip((*newBlot), def.descr);
734
newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) );
736
sigc::signal<void> type_signal_something;
739
g_signal_connect( G_OBJECT(newBlot->gobj()),
741
G_CALLBACK(handleClick),
744
g_signal_connect( G_OBJECT(newBlot->gobj()),
746
G_CALLBACK(handleSecondaryClick),
749
g_signal_connect( G_OBJECT(newBlot->gobj()),
750
"button-press-event",
751
G_CALLBACK(handleButtonPress),
755
std::vector<std::string> listing = def.getMIMETypes();
756
int entryCount = listing.size();
757
GtkTargetEntry* entries = new GtkTargetEntry[entryCount];
758
GtkTargetEntry* curr = entries;
759
for ( std::vector<std::string>::iterator it = listing.begin(); it != listing.end(); ++it ) {
760
curr->target = g_strdup(it->c_str());
762
if ( mimeToInt.find(*it) == mimeToInt.end() ){
763
// these next lines are order-dependent:
764
mimeToInt[*it] = mimeStrings.size();
765
mimeStrings.push_back(*it);
767
curr->info = mimeToInt[curr->target];
770
gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()),
773
GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
774
for ( int i = 0; i < entryCount; i++ ) {
775
g_free(entries[i].target);
780
g_signal_connect( G_OBJECT(newBlot->gobj()),
782
G_CALLBACK(ColorItem::_dragGetColorData),
785
g_signal_connect( G_OBJECT(newBlot->gobj()),
787
G_CALLBACK(dragBegin),
790
g_signal_connect( G_OBJECT(newBlot->gobj()),
791
"enter-notify-event",
792
G_CALLBACK(handleEnterNotify),
795
g_signal_connect( G_OBJECT(newBlot->gobj()),
796
"leave-notify-event",
797
G_CALLBACK(handleLeaveNotify),
800
// g_signal_connect( G_OBJECT(newBlot->gobj()),
802
// G_CALLBACK(dragDropColorData),
805
if ( def.isEditable() )
807
// gtk_drag_dest_set( GTK_WIDGET(newBlot->gobj()),
808
// GTK_DEST_DEFAULT_ALL,
810
// G_N_ELEMENTS(destColorTargets),
811
// GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
814
// g_signal_connect( G_OBJECT(newBlot->gobj()),
815
// "drag-data-received",
816
// G_CALLBACK(_dropDataIn),
820
g_signal_connect( G_OBJECT(newBlot->gobj()),
822
G_CALLBACK(dieDieDie),
829
_previews.push_back( widget );
834
void ColorItem::buttonClicked(bool secondary)
836
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
838
char const * attrName = secondary ? "stroke" : "fill";
840
SPCSSAttr *css = sp_repr_css_attr_new();
842
switch (def.getType()) {
843
case ege::PaintDef::CLEAR: {
844
// TODO actually make this clear
845
sp_repr_css_set_property( css, attrName, "none" );
846
descr = secondary? _("Remove stroke color") : _("Remove fill color");
849
case ege::PaintDef::NONE: {
850
sp_repr_css_set_property( css, attrName, "none" );
851
descr = secondary? _("Set stroke color to none") : _("Set fill color to none");
854
case ege::PaintDef::RGB: {
855
Glib::ustring colorspec;
856
if ( gradMap.find(this) == gradMap.end() ){
858
guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff;
859
sp_svg_write_color(c, sizeof(c), rgba);
862
SPGradient* grad = gradMap[this];
864
colorspec += grad->id;
867
sp_repr_css_set_property( css, attrName, colorspec.c_str() );
868
descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch");
872
sp_desktop_set_style(desktop, css);
873
sp_repr_css_attr_unref(css);
875
sp_document_done( sp_desktop_document(desktop), SP_VERB_DIALOG_SWATCHES, descr.c_str() );
879
static char* trim( char* str ) {
881
while ( *str && (*str == ' ' || *str == '\t') ) {
889
while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
895
void skipWhitespace( char*& str ) {
896
while ( *str == ' ' || *str == '\t' ) {
901
bool parseNum( char*& str, int& val ) {
903
while ( '0' <= *str && *str <= '9' ) {
904
val = val * 10 + (*str - '0');
907
bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
912
static bool getBlock( std::string& dst, guchar ch, std::string const str )
915
std::string::size_type pos = str.find(ch);
916
if ( pos != std::string::npos )
918
std::string::size_type pos2 = str.find( '(', pos );
919
if ( pos2 != std::string::npos ) {
920
std::string::size_type endPos = str.find( ')', pos2 );
921
if ( endPos != std::string::npos ) {
922
dst = str.substr( pos2 + 1, (endPos - pos2 - 1) );
930
static bool popVal( guint64& numVal, std::string& str )
933
std::string::size_type endPos = str.find(',');
934
if ( endPos == std::string::npos ) {
935
endPos = str.length();
938
if ( endPos != std::string::npos && endPos > 0 ) {
939
std::string xxx = str.substr( 0, endPos );
940
const gchar* ptr = xxx.c_str();
942
numVal = g_ascii_strtoull( ptr, &endPtr, 10 );
943
if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
945
} else if ( (numVal == 0) && (endPtr == ptr) ) {
949
str.erase( 0, endPos + 1 );
956
void ColorItem::_wireMagicColors( void* p )
958
JustForNow* onceMore = reinterpret_cast<JustForNow*>(p);
961
for ( std::vector<ColorItem*>::iterator it = onceMore->_colors.begin(); it != onceMore->_colors.end(); ++it )
963
std::string::size_type pos = (*it)->def.descr.find("*{");
964
if ( pos != std::string::npos )
966
std::string subby = (*it)->def.descr.substr( pos + 2 );
967
std::string::size_type endPos = subby.find("}*");
968
if ( endPos != std::string::npos )
970
subby.erase( endPos );
971
//g_message("FOUND MAGIC at '%s'", (*it)->def.descr.c_str());
972
//g_message(" '%s'", subby.c_str());
974
if ( subby.find('E') != std::string::npos )
976
(*it)->def.setEditable( true );
979
if ( subby.find('L') != std::string::npos )
981
(*it)->_isLive = true;
985
// Tint. index + 1 more val.
986
if ( getBlock( part, 'T', subby ) ) {
987
guint64 colorIndex = 0;
988
if ( popVal( colorIndex, part ) ) {
990
if ( popVal( percent, part ) ) {
991
(*it)->_linkTint( *(onceMore->_colors[colorIndex]), percent );
996
// Shade/tone. index + 1 or 2 more val.
997
if ( getBlock( part, 'S', subby ) ) {
998
guint64 colorIndex = 0;
999
if ( popVal( colorIndex, part ) ) {
1000
guint64 percent = 0;
1001
if ( popVal( percent, part ) ) {
1002
guint64 grayLevel = 0;
1003
if ( !popVal( grayLevel, part ) ) {
1006
(*it)->_linkTone( *(onceMore->_colors[colorIndex]), percent, grayLevel );
1018
void ColorItem::_linkTint( ColorItem& other, int percent )
1022
other._listeners.push_back(this);
1023
_linkIsTone = false;
1024
_linkPercent = percent;
1025
if ( _linkPercent > 100 )
1027
if ( _linkPercent < 0 )
1032
ColorItem::_colorDefChanged(&other);
1036
void ColorItem::_linkTone( ColorItem& other, int percent, int grayLevel )
1040
other._listeners.push_back(this);
1042
_linkPercent = percent;
1043
if ( _linkPercent > 100 )
1045
if ( _linkPercent < 0 )
1047
_linkGray = grayLevel;
1050
ColorItem::_colorDefChanged(&other);
1055
void _loadPaletteFile( gchar const *filename )
1058
FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
1060
char* result = fgets( block, sizeof(block), f );
1062
if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
1063
bool inHeader = true;
1064
bool hasErr = false;
1066
JustForNow *onceMore = new JustForNow();
1069
result = fgets( block, sizeof(block), f );
1070
block[sizeof(block) - 1] = 0;
1072
if ( block[0] == '#' ) {
1076
// very simple check for header versus entry
1077
while ( *ptr == ' ' || *ptr == '\t' ) {
1080
if ( (*ptr == 0) || (*ptr == '\r') || (*ptr == '\n') ) {
1081
// blank line. skip it.
1082
} else if ( '0' <= *ptr && *ptr <= '9' ) {
1083
// should be an entry link
1086
Glib::ustring name("");
1090
skipWhitespace(ptr);
1092
hasErr = parseNum(ptr, r);
1094
skipWhitespace(ptr);
1095
hasErr = parseNum(ptr, g);
1098
skipWhitespace(ptr);
1099
hasErr = parseNum(ptr, b);
1101
if ( !hasErr && *ptr ) {
1102
char* n = trim(ptr);
1108
// Add the entry now
1109
Glib::ustring nameStr(name);
1110
ColorItem* item = new ColorItem( r, g, b, nameStr );
1111
onceMore->_colors.push_back(item);
1118
// Hmmm... probably bad. Not quite the format we want?
1121
char* sep = strchr(result, ':');
1124
char* val = trim(sep + 1);
1125
char* name = trim(result);
1127
if ( strcmp( "Name", name ) == 0 )
1129
onceMore->_name = val;
1131
else if ( strcmp( "Columns", name ) == 0 )
1134
guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
1135
if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
1137
} else if ( (numVal == 0) && (endPtr == val) ) {
1138
// failed conversion
1140
onceMore->_prefWidth = numVal;
1155
} while ( result && !hasErr );
1157
possible.push_back(onceMore);
1158
#if ENABLE_MAGIC_COLORS
1159
ColorItem::_wireMagicColors( onceMore );
1160
#endif // ENABLE_MAGIC_COLORS
1171
static void loadEmUp()
1173
static bool beenHere = false;
1177
std::list<gchar *> sources;
1178
sources.push_back( profile_path("palettes") );
1179
sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
1180
sources.push_back( g_strdup(CREATE_PALETTESDIR) );
1182
// Use this loop to iterate through a list of possible document locations.
1183
while (!sources.empty()) {
1184
gchar *dirname = sources.front();
1186
if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
1187
&& Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
1189
GDir *directory = g_dir_open(dirname, 0, &err);
1191
gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1192
g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
1195
gchar *filename = 0;
1196
while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
1197
gchar* lower = g_ascii_strdown( filename, -1 );
1198
// if ( g_str_has_suffix(lower, ".gpl") ) {
1199
gchar* full = g_build_filename(dirname, filename, NULL);
1200
if ( !Inkscape::IO::file_test( full, G_FILE_TEST_IS_DIR ) ) {
1201
_loadPaletteFile(full);
1207
g_dir_close(directory);
1213
sources.pop_front();
1226
SwatchesPanel& SwatchesPanel::getInstance()
1228
return *new SwatchesPanel();
1235
SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
1236
Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
1242
_currentDocument(0),
1245
Gtk::RadioMenuItem* hotItem = 0;
1246
_holder = new PreviewHolder();
1247
_clear = new ColorItem( ege::PaintDef::CLEAR );
1249
_remove = new ColorItem( ege::PaintDef::NONE );
1250
_remove->ptr = this;
1252
JustForNow *docPalette = new JustForNow();
1254
docPalette->_name = "Auto";
1255
possible.push_back(docPalette);
1260
if ( !possible.empty() ) {
1261
JustForNow* first = 0;
1263
Glib::ustring targetName;
1264
if ( !_prefs_path.empty() ) {
1265
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1266
targetName = prefs->getString(_prefs_path + "/palette");
1267
if (!targetName.empty()) {
1268
for ( std::vector<JustForNow*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) {
1269
if ( (*iter)->_name == targetName ) {
1279
first = possible.front();
1282
_currentIndex = index;
1287
Gtk::RadioMenuItem::Group groupOne;
1290
for ( std::vector<JustForNow*>::iterator it = possible.begin(); it != possible.end(); it++ ) {
1291
JustForNow* curr = *it;
1292
Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
1293
if ( curr == first ) {
1296
_regItem( single, 3, i );
1302
_getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
1303
_setTargetFillable(_holder);
1305
show_all_children();
1307
restorePanelPrefs();
1309
hotItem->set_active();
1313
SwatchesPanel::~SwatchesPanel()
1315
_documentConnection.disconnect();
1316
_resourceConnection.disconnect();
1317
_selChanged.disconnect();
1318
_setModified.disconnect();
1319
_subselChanged.disconnect();
1332
void SwatchesPanel::setOrientation( Gtk::AnchorType how )
1334
// Must call the parent class or bad things might happen
1335
Inkscape::UI::Widget::Panel::setOrientation( how );
1339
_holder->setOrientation( Gtk::ANCHOR_SOUTH );
1343
void SwatchesPanel::setDesktop( SPDesktop* desktop )
1345
if ( desktop != _currentDesktop ) {
1346
if ( _currentDesktop ) {
1347
_documentConnection.disconnect();
1348
_selChanged.disconnect();
1349
_setModified.disconnect();
1350
_subselChanged.disconnect();
1353
_currentDesktop = desktop;
1356
_currentDesktop->selection->connectChanged(
1357
sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
1359
_currentDesktop->selection->connectModified(
1360
sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
1362
_currentDesktop->connectToolSubselectionChanged(
1363
sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
1365
sigc::bound_mem_functor1<void, Inkscape::UI::Dialogs::SwatchesPanel, SPDocument*> first = sigc::mem_fun(*this, &SwatchesPanel::_setDocument);
1366
sigc::slot<void, SPDocument*> base2 = first;
1367
sigc::slot<void,SPDesktop*, SPDocument*> slot2 = sigc::hide<0>( base2 );
1368
_documentConnection = desktop->connectDocumentReplaced( slot2 );
1370
_setDocument( desktop->doc() );
1377
void SwatchesPanel::_setDocument( SPDocument *document )
1379
if ( document != _currentDocument ) {
1380
if ( _currentDocument ) {
1381
_resourceConnection.disconnect();
1383
_currentDocument = document;
1384
if ( _currentDocument ) {
1385
_resourceConnection = sp_document_resources_changed_connect(document,
1387
sigc::mem_fun(*this, &SwatchesPanel::handleGradientsChange));
1390
handleGradientsChange();
1394
void SwatchesPanel::handleGradientsChange()
1396
std::vector<SPGradient*> newList;
1398
const GSList *gradients = sp_document_get_resource_list(_currentDocument, "gradient");
1399
for (const GSList *item = gradients; item; item = item->next) {
1400
SPGradient* grad = SP_GRADIENT(item->data);
1401
if ( grad->has_stops ) {
1402
newList.push_back(SP_GRADIENT(item->data));
1407
JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr);
1408
// TODO delete pointed to objects
1409
docPalette->_colors.clear();
1410
if ( !newList.empty() ) {
1411
for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it )
1413
SPGradient* grad = *it;
1414
if ( grad->repr->attribute("osb:paint") ) {
1415
sp_gradient_ensure_vector( grad );
1416
SPGradientStop first = grad->vector.stops[0];
1417
SPColor color = first.color;
1418
guint32 together = color.toRGBA32(first.opacity);
1420
// At the moment we can't trust the count of 1 vs 2 stops.
1421
SPGradientStop second = (*it)->vector.stops[1];
1422
SPColor color2 = second.color;
1423
guint32 together2 = color2.toRGBA32(second.opacity);
1425
if ( (grad->vector.stops.size() <= 2) && (together == together2) ) {
1426
// Treat as solid-color
1427
Glib::ustring name( grad->id );
1428
unsigned int r = SP_RGBA32_R_U(together);
1429
unsigned int g = SP_RGBA32_G_U(together);
1430
unsigned int b = SP_RGBA32_B_U(together);
1431
ColorItem* item = new ColorItem( r, g, b, name );
1433
docPalette->_colors.push_back(item);
1434
gradMap[item] = grad;
1436
// Treat as gradient
1437
Glib::ustring name( grad->id );
1438
unsigned int r = SP_RGBA32_R_U(together);
1439
unsigned int g = SP_RGBA32_G_U(together);
1440
unsigned int b = SP_RGBA32_B_U(together);
1441
ColorItem* item = new ColorItem( r, g, b, name );
1443
docPalette->_colors.push_back(item);
1446
gint height = VBLOCK;
1447
guchar* px = g_new( guchar, 3 * height * width );
1448
nr_render_checkerboard_rgb( px, width, VBLOCK, 3 * width, 0, 0 );
1450
sp_gradient_render_vector_block_rgb( grad,
1451
px, width, height, 3 * width,
1454
previewMap[item] = px;
1455
gradMap[item] = grad;
1460
JustForNow* curr = possible[_currentIndex];
1461
if (curr == docPalette) {
1467
void SwatchesPanel::_updateFromSelection()
1470
JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr);
1472
Glib::ustring fillId;
1473
Glib::ustring strokeId;
1475
SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
1476
int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
1478
case QUERY_STYLE_SINGLE:
1479
case QUERY_STYLE_MULTIPLE_AVERAGED:
1480
case QUERY_STYLE_MULTIPLE_SAME:
1482
if (tmpStyle->fill.set && tmpStyle->fill.isPaintserver()) {
1483
SPPaintServer* server = tmpStyle->getFillPaintServer();
1484
if ( SP_IS_GRADIENT(server) ) {
1485
SPGradient* target = 0;
1486
SPGradient* grad = SP_GRADIENT(server);
1487
if (grad->repr->attribute("osb:paint")) {
1489
} else if ( grad->ref ) {
1490
SPGradient *tmp = grad->ref->getObject();
1491
if ( tmp && tmp->repr->attribute("osb:paint") ) {
1496
gchar const* id = target->repr->attribute("id");
1507
result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
1509
case QUERY_STYLE_SINGLE:
1510
case QUERY_STYLE_MULTIPLE_AVERAGED:
1511
case QUERY_STYLE_MULTIPLE_SAME:
1513
if (tmpStyle->stroke.set && tmpStyle->stroke.isPaintserver()) {
1514
SPPaintServer* server = tmpStyle->getStrokePaintServer();
1515
if ( SP_IS_GRADIENT(server) ) {
1516
SPGradient* target = 0;
1517
SPGradient* grad = SP_GRADIENT(server);
1518
if (grad->repr->attribute("osb:paint")) {
1520
} else if ( grad->ref ) {
1521
SPGradient *tmp = grad->ref->getObject();
1522
if ( tmp && tmp->repr->attribute("osb:paint") ) {
1527
gchar const* id = target->repr->attribute("id");
1537
sp_style_unref(tmpStyle);
1539
for ( std::vector<ColorItem*>::iterator it = docPalette->_colors.begin(); it != docPalette->_colors.end(); ++it ) {
1540
ColorItem* item = *it;
1541
bool isFill = (fillId == item->def.descr);
1542
bool isStroke = (strokeId == item->def.descr);
1543
item->setState( isFill, isStroke );
1548
void SwatchesPanel::_handleAction( int setId, int itemId )
1553
if ( itemId >= 0 && itemId < static_cast<int>(possible.size()) ) {
1554
_currentIndex = itemId;
1556
if ( !_prefs_path.empty() ) {
1557
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1558
prefs->setString(_prefs_path + "/palette", possible[_currentIndex]->_name);
1568
void SwatchesPanel::_rebuild()
1570
JustForNow* curr = possible[_currentIndex];
1573
if ( curr->_prefWidth > 0 ) {
1574
_holder->setColumnPref( curr->_prefWidth );
1576
_holder->freezeUpdates();
1577
// TODO restore once 'clear' works _holder->addPreview(_clear);
1578
_holder->addPreview(_remove);
1579
for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
1580
_holder->addPreview(*it);
1582
_holder->thawUpdates();
1588
} //namespace Dialogs
1590
} //namespace Inkscape
1596
c-file-style:"stroustrup"
1597
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1598
indent-tabs-mode:nil
1602
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :