~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/ui/dialog/swatches.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 * @brief Color swatches dialog
 
3
 */
 
4
/* Authors:
 
5
 *   Jon A. Cruz
 
6
 *   John Bintz
 
7
 *
 
8
 * Copyright (C) 2005 Jon A. Cruz
 
9
 * Copyright (C) 2008 John Bintz
 
10
 *
 
11
 * Released under GNU GPL, read the file 'COPYING' for more information
 
12
 */
 
13
 
 
14
#include <errno.h>
 
15
#include <map>
 
16
 
 
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>
 
24
 
 
25
#include "desktop.h"
 
26
#include "desktop-handles.h"
 
27
#include "desktop-style.h"
 
28
#include "document.h"
 
29
#include "document-private.h"
 
30
#include "extension/db.h"
 
31
#include "inkscape.h"
 
32
#include "inkscape.h"
 
33
#include "io/sys.h"
 
34
#include "io/resource.h"
 
35
#include "message-context.h"
 
36
#include "path-prefix.h"
 
37
#include "preferences.h"
 
38
#include "sp-item.h"
 
39
#include "svg/svg-color.h"
 
40
#include "sp-gradient-fns.h"
 
41
#include "sp-gradient.h"
 
42
#include "sp-gradient-vector.h"
 
43
#include "swatches.h"
 
44
#include "style.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"
 
49
 
 
50
namespace Inkscape {
 
51
namespace UI {
 
52
namespace Dialogs {
 
53
 
 
54
#define VBLOCK 16
 
55
 
 
56
void _loadPaletteFile( gchar const *filename );
 
57
 
 
58
/**
 
59
 * The color swatch you see on screen as a clickable box.
 
60
 */
 
61
class ColorItem : public Inkscape::UI::Previewable
 
62
{
 
63
    friend void _loadPaletteFile( gchar const *filename );
 
64
public:
 
65
    ColorItem( ege::PaintDef::ColorType type );
 
66
    ColorItem( unsigned int r, unsigned int g, unsigned int b,
 
67
               Glib::ustring& name );
 
68
    virtual ~ColorItem();
 
69
    ColorItem(ColorItem const &other);
 
70
    virtual ColorItem &operator=(ColorItem const &other);
 
71
    virtual Gtk::Widget* getPreview(PreviewStyle style,
 
72
                                    ViewType view,
 
73
                                    ::PreviewSize size,
 
74
                                    guint ratio);
 
75
    void buttonClicked(bool secondary = false);
 
76
 
 
77
    void setState( bool fill, bool stroke );
 
78
    bool isFill() { return _isFill; }
 
79
    bool isStroke() { return _isStroke; }
 
80
 
 
81
    ege::PaintDef def;
 
82
    void* ptr;
 
83
 
 
84
private:
 
85
    static void _dropDataIn( GtkWidget *widget,
 
86
                             GdkDragContext *drag_context,
 
87
                             gint x, gint y,
 
88
                             GtkSelectionData *data,
 
89
                             guint info,
 
90
                             guint event_time,
 
91
                             gpointer user_data);
 
92
 
 
93
    static void _dragGetColorData( GtkWidget *widget,
 
94
                                   GdkDragContext *drag_context,
 
95
                                   GtkSelectionData *data,
 
96
                                   guint info,
 
97
                                   guint time,
 
98
                                   gpointer user_data);
 
99
 
 
100
    static void _wireMagicColors( void* p );
 
101
    static void _colorDefChanged(void* data);
 
102
 
 
103
    void _linkTint( ColorItem& other, int percent );
 
104
    void _linkTone( ColorItem& other, int percent, int grayLevel );
 
105
 
 
106
    Gtk::Tooltips tips;
 
107
    std::vector<Gtk::Widget*> _previews;
 
108
 
 
109
    bool _isFill;
 
110
    bool _isStroke;
 
111
    bool _isLive;
 
112
    bool _linkIsTone;
 
113
    int _linkPercent;
 
114
    int _linkGray;
 
115
    ColorItem* _linkSrc;
 
116
    std::vector<ColorItem*> _listeners;
 
117
};
 
118
 
 
119
 
 
120
 
 
121
ColorItem::ColorItem(ege::PaintDef::ColorType type) :
 
122
    def(type),
 
123
    ptr(0),
 
124
    _isFill(false),
 
125
    _isStroke(false),
 
126
    _isLive(false),
 
127
    _linkIsTone(false),
 
128
    _linkPercent(0),
 
129
    _linkGray(0),
 
130
    _linkSrc(0)
 
131
{
 
132
}
 
133
 
 
134
ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) :
 
135
    def( r, g, b, name ),
 
136
    ptr(0),
 
137
    _isFill(false),
 
138
    _isStroke(false),
 
139
    _isLive(false),
 
140
    _linkIsTone(false),
 
141
    _linkPercent(0),
 
142
    _linkGray(0),
 
143
    _linkSrc(0)
 
144
{
 
145
}
 
146
 
 
147
ColorItem::~ColorItem()
 
148
{
 
149
}
 
150
 
 
151
ColorItem::ColorItem(ColorItem const &other) :
 
152
    Inkscape::UI::Previewable()
 
153
{
 
154
    if ( this != &other ) {
 
155
        *this = other;
 
156
    }
 
157
}
 
158
 
 
159
ColorItem &ColorItem::operator=(ColorItem const &other)
 
160
{
 
161
    if ( this != &other ) {
 
162
        def = other.def;
 
163
 
 
164
        // TODO - correct linkage
 
165
        _linkSrc = other._linkSrc;
 
166
        g_message("Erk!");
 
167
    }
 
168
    return *this;
 
169
}
 
170
 
 
171
void ColorItem::setState( bool fill, bool stroke )
 
172
{
 
173
    if ( (_isFill != fill) || (_isStroke != stroke) ) {
 
174
        _isFill = fill;
 
175
        _isStroke = stroke;
 
176
 
 
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());
 
181
 
 
182
                int val = eek_preview_get_linked( preview );
 
183
                val &= ~(PREVIEW_FILL | PREVIEW_STROKE);
 
184
                if ( _isFill ) {
 
185
                    val |= PREVIEW_FILL;
 
186
                }
 
187
                if ( _isStroke ) {
 
188
                    val |= PREVIEW_STROKE;
 
189
                }
 
190
                eek_preview_set_linked( preview, static_cast<LinkType>(val) );
 
191
            }
 
192
        }
 
193
    }
 
194
}
 
195
 
 
196
 
 
197
class JustForNow
 
198
{
 
199
public:
 
200
    JustForNow() : _prefWidth(0) {}
 
201
 
 
202
    Glib::ustring _name;
 
203
    int _prefWidth;
 
204
    std::vector<ColorItem*> _colors;
 
205
};
 
206
 
 
207
static std::vector<JustForNow*> possible;
 
208
 
 
209
 
 
210
static std::vector<std::string> mimeStrings;
 
211
static std::map<std::string, guint> mimeToInt;
 
212
 
 
213
static std::map<ColorItem*, guchar*> previewMap;
 
214
static std::map<ColorItem*, SPGradient*> gradMap; // very temporary workaround.
 
215
 
 
216
void ColorItem::_dragGetColorData( GtkWidget */*widget*/,
 
217
                                   GdkDragContext */*drag_context*/,
 
218
                                   GtkSelectionData *data,
 
219
                                   guint info,
 
220
                                   guint /*time*/,
 
221
                                   gpointer user_data)
 
222
{
 
223
    ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
 
224
    std::string key;
 
225
    if ( info < mimeStrings.size() ) {
 
226
        key = mimeStrings[info];
 
227
    } else {
 
228
        g_warning("ERROR: unknown value (%d)", info);
 
229
    }
 
230
 
 
231
    if ( !key.empty() ) {
 
232
        char* tmp = 0;
 
233
        int len = 0;
 
234
        int format = 0;
 
235
        item->def.getMIMEData(key, tmp, len, format);
 
236
        if ( tmp ) {
 
237
            GdkAtom dataAtom = gdk_atom_intern( key.c_str(), FALSE );
 
238
            gtk_selection_data_set( data, dataAtom, format, (guchar*)tmp, len );
 
239
            delete[] tmp;
 
240
        }
 
241
    }
 
242
}
 
243
 
 
244
static void dragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data )
 
245
{
 
246
    ColorItem* item = reinterpret_cast<ColorItem*>(data);
 
247
    if ( item )
 
248
    {
 
249
        using Inkscape::IO::Resource::get_path;
 
250
        using Inkscape::IO::Resource::ICONS;
 
251
        using Inkscape::IO::Resource::SYSTEM;
 
252
        int width = 32;
 
253
        int height = 24;
 
254
 
 
255
        if (item->def.getType() != ege::PaintDef::RGB){
 
256
            GError *error = NULL;
 
257
            gsize bytesRead = 0;
 
258
            gsize bytesWritten = 0;
 
259
            gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"),
 
260
                                                 -1,
 
261
                                                 &bytesRead,
 
262
                                                 &bytesWritten,
 
263
                                                 &error);
 
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 );
 
267
        } else {
 
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();
 
276
            } else {
 
277
                SPGradient* grad = gradMap[item];
 
278
 
 
279
                guchar* px = g_new( guchar, 3 * height * width );
 
280
                nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 );
 
281
 
 
282
                sp_gradient_render_vector_block_rgb( grad,
 
283
                                                     px, width, height, 3 * width,
 
284
                                                     0, width, TRUE );
 
285
 
 
286
                pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
 
287
                                                   width, height, width * 3,
 
288
                                                   0, // add delete function
 
289
                                                   0 );
 
290
            }
 
291
            gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 );
 
292
        }
 
293
    }
 
294
 
 
295
}
 
296
 
 
297
//"drag-drop"
 
298
// gboolean dragDropColorData( GtkWidget *widget,
 
299
//                             GdkDragContext *drag_context,
 
300
//                             gint x,
 
301
//                             gint y,
 
302
//                             guint time,
 
303
//                             gpointer user_data)
 
304
// {
 
305
// // TODO finish
 
306
 
 
307
//     return TRUE;
 
308
// }
 
309
 
 
310
static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) {
 
311
    ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
 
312
    if ( item ) {
 
313
        item->buttonClicked(false);
 
314
    }
 
315
}
 
316
 
 
317
static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) {
 
318
    ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
 
319
    if ( item ) {
 
320
        item->buttonClicked(true);
 
321
    }
 
322
}
 
323
 
 
324
static gboolean handleEnterNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) {
 
325
    ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
 
326
    if ( item ) {
 
327
        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
328
        if ( 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);
 
332
            g_free(msg);
 
333
        }
 
334
    }
 
335
    return FALSE;
 
336
}
 
337
 
 
338
static gboolean handleLeaveNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) {
 
339
    ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
 
340
    if ( item ) {
 
341
        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
342
        if ( desktop ) {
 
343
            desktop->tipsMessageContext()->clear();
 
344
        }
 
345
    }
 
346
    return FALSE;
 
347
}
 
348
 
 
349
static GtkWidget* popupMenu = 0;
 
350
static std::vector<GtkWidget*> popupExtras;
 
351
static ColorItem* bounceTarget = 0;
 
352
 
 
353
static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
 
354
{
 
355
    if ( bounceTarget ) {
 
356
        handleClick( GTK_WIDGET(menuitem), bounceTarget );
 
357
    }
 
358
}
 
359
 
 
360
static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ )
 
361
{
 
362
    if ( bounceTarget ) {
 
363
        handleSecondaryClick( GTK_WIDGET(menuitem), 0, bounceTarget );
 
364
    }
 
365
}
 
366
 
 
367
static void editGradientImpl( SPGradient* gr )
 
368
{
 
369
    if ( gr ) {
 
370
        GtkWidget *dialog = sp_gradient_vector_editor_new( gr );
 
371
        gtk_widget_show( dialog );
 
372
    }
 
373
}
 
374
 
 
375
static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
 
376
{
 
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;
 
381
        if (doc) {
 
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 );
 
388
                    break;
 
389
                }
 
390
            }
 
391
        }
 
392
    }
 
393
}
 
394
 
 
395
static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ )
 
396
{
 
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;
 
401
        if (doc) {
 
402
            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
 
403
 
 
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);
 
411
 
 
412
            SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL);
 
413
 
 
414
            SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr));
 
415
 
 
416
            Inkscape::GC::release(repr);
 
417
 
 
418
 
 
419
            editGradientImpl( gr );
 
420
            // Work-around for timing of gradient addition change. Must follow edit.
 
421
            if ( swp ) {
 
422
                swp->handleGradientsChange();
 
423
            }
 
424
        }
 
425
    }
 
426
}
 
427
 
 
428
static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gpointer user_data)
 
429
{
 
430
    gboolean handled = FALSE;
 
431
 
 
432
    if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) {
 
433
        if ( !popupMenu ) {
 
434
            popupMenu = gtk_menu_new();
 
435
            GtkWidget* child = 0;
 
436
 
 
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),
 
440
                              "activate",
 
441
                              G_CALLBACK(redirClick),
 
442
                              user_data);
 
443
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
444
 
 
445
            //TRANSLATORS: An item in context menu on a colour in the swatches
 
446
            child = gtk_menu_item_new_with_label(_("Set stroke"));
 
447
 
 
448
            g_signal_connect( G_OBJECT(child),
 
449
                              "activate",
 
450
                              G_CALLBACK(redirSecondaryClick),
 
451
                              user_data);
 
452
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
453
 
 
454
            child = gtk_separator_menu_item_new();
 
455
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
456
            popupExtras.push_back(child);
 
457
 
 
458
            child = gtk_menu_item_new_with_label(_("Add"));
 
459
            g_signal_connect( G_OBJECT(child),
 
460
                              "activate",
 
461
                              G_CALLBACK(addNewGradient),
 
462
                              user_data );
 
463
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
464
            popupExtras.push_back(child);
 
465
 
 
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 );
 
470
 
 
471
            child = gtk_menu_item_new_with_label(_("Edit..."));
 
472
            g_signal_connect( G_OBJECT(child),
 
473
                              "activate",
 
474
                              G_CALLBACK(editGradient),
 
475
                              user_data );
 
476
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
477
            popupExtras.push_back(child);
 
478
 
 
479
            child = gtk_separator_menu_item_new();
 
480
            gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child);
 
481
            popupExtras.push_back(child);
 
482
 
 
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 );
 
487
 
 
488
            gtk_widget_show_all(popupMenu);
 
489
        }
 
490
 
 
491
        ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
 
492
        if ( item ) {
 
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);
 
497
            }
 
498
 
 
499
            bounceTarget = item;
 
500
            if ( popupMenu ) {
 
501
                gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
 
502
                handled = TRUE;
 
503
            }
 
504
        }
 
505
    }
 
506
 
 
507
    return handled;
 
508
}
 
509
 
 
510
static void dieDieDie( GtkObject *obj, gpointer user_data )
 
511
{
 
512
    g_message("die die die %p  %p", obj, user_data );
 
513
}
 
514
 
 
515
#include "color.h" // for SP_RGBA32_U_COMPOSE
 
516
 
 
517
void ColorItem::_dropDataIn( GtkWidget */*widget*/,
 
518
                             GdkDragContext */*drag_context*/,
 
519
                             gint /*x*/, gint /*y*/,
 
520
                             GtkSelectionData */*data*/,
 
521
                             guint /*info*/,
 
522
                             guint /*event_time*/,
 
523
                             gpointer /*user_data*/)
 
524
{
 
525
}
 
526
 
 
527
static bool bruteForce( SPDocument* document, Inkscape::XML::Node* node, Glib::ustring const& match, int r, int g, int b )
 
528
{
 
529
    bool changed = false;
 
530
 
 
531
    if ( node ) {
 
532
        gchar const * val = node->attribute("inkscape:x-fill-tag");
 
533
        if ( val  && (match == val) ) {
 
534
            SPObject *obj = document->getObjectByRepr( node );
 
535
 
 
536
            gchar c[64] = {0};
 
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 );
 
540
 
 
541
            sp_desktop_apply_css_recursive( (SPItem*)obj, css, true );
 
542
            ((SPItem*)obj)->updateRepr();
 
543
 
 
544
            changed = true;
 
545
        }
 
546
 
 
547
        val = node->attribute("inkscape:x-stroke-tag");
 
548
        if ( val  && (match == val) ) {
 
549
            SPObject *obj = document->getObjectByRepr( node );
 
550
 
 
551
            gchar c[64] = {0};
 
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 );
 
555
 
 
556
            sp_desktop_apply_css_recursive( (SPItem*)obj, css, true );
 
557
            ((SPItem*)obj)->updateRepr();
 
558
 
 
559
            changed = true;
 
560
        }
 
561
 
 
562
        Inkscape::XML::Node* first = node->firstChild();
 
563
        changed |= bruteForce( document, first, match, r, g, b );
 
564
 
 
565
        changed |= bruteForce( document, node->next(), match, r, g, b );
 
566
    }
 
567
 
 
568
    return changed;
 
569
}
 
570
 
 
571
void ColorItem::_colorDefChanged(void* data)
 
572
{
 
573
    ColorItem* item = reinterpret_cast<ColorItem*>(data);
 
574
    if ( item ) {
 
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() );
 
583
 
 
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)) );
 
587
 
 
588
                widget->queue_draw();
 
589
            }
 
590
        }
 
591
 
 
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();
 
596
 
 
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;
 
601
            } else {
 
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;
 
605
            }
 
606
 
 
607
            (*it)->def.setRGB( r, g, b );
 
608
        }
 
609
 
 
610
 
 
611
        // Look for objects using this color
 
612
        {
 
613
            SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
614
            if ( desktop ) {
 
615
                SPDocument* document = sp_desktop_document( desktop );
 
616
                Inkscape::XML::Node *rroot =  sp_document_repr_root( document );
 
617
                if ( rroot ) {
 
618
 
 
619
                    // Find where this thing came from
 
620
                    Glib::ustring paletteName;
 
621
                    bool found = false;
 
622
                    int index = 0;
 
623
                    for ( std::vector<JustForNow*>::iterator it2 = possible.begin(); it2 != possible.end() && !found; ++it2 ) {
 
624
                        JustForNow* curr = *it2;
 
625
                        index = 0;
 
626
                        for ( std::vector<ColorItem*>::iterator zz = curr->_colors.begin(); zz != curr->_colors.end(); ++zz ) {
 
627
                            if ( item == *zz ) {
 
628
                                found = true;
 
629
                                paletteName = curr->_name;
 
630
                                break;
 
631
                            } else {
 
632
                                index++;
 
633
                            }
 
634
                        }
 
635
                    }
 
636
 
 
637
                    if ( !paletteName.empty() ) {
 
638
                        gchar* str = g_strdup_printf("%d|", index);
 
639
                        paletteName.insert( 0, str );
 
640
                        g_free(str);
 
641
                        str = 0;
 
642
 
 
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"));
 
646
                        }
 
647
                    }
 
648
                }
 
649
            }
 
650
        }
 
651
    }
 
652
}
 
653
 
 
654
 
 
655
Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, ::PreviewSize size, guint ratio)
 
656
{
 
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);
 
661
        widget = lbl;
 
662
    } else {
 
663
//         Glib::ustring blank("          ");
 
664
//         if ( size == Inkscape::ICON_SIZE_MENU || size == Inkscape::ICON_SIZE_DECORATION ) {
 
665
//             blank = " ";
 
666
//         }
 
667
 
 
668
        GtkWidget* eekWidget = eek_preview_new();
 
669
        EekPreview * preview = EEK_PREVIEW(eekWidget);
 
670
        Gtk::Widget* newBlot = Glib::wrap(eekWidget);
 
671
 
 
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());
 
676
        } else {
 
677
            guchar* px = previewMap[this];
 
678
            int width = 128;
 
679
            int height = 16;
 
680
            GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8,
 
681
                                                          width, height, width * 3,
 
682
                                                          0, // add delete function
 
683
                                                          0 );
 
684
            eek_preview_set_pixbuf( preview, pixbuf );
 
685
        }
 
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;
 
691
            gsize bytesRead = 0;
 
692
            gsize bytesWritten = 0;
 
693
            gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"),
 
694
                                                 -1,
 
695
                                                 &bytesRead,
 
696
                                                 &bytesWritten,
 
697
                                                 &error);
 
698
            GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(localFilename, &error);
 
699
            if (!pixbuf) {
 
700
                g_warning("Null pixbuf for %p [%s]", localFilename, localFilename );
 
701
            }
 
702
            g_free(localFilename);
 
703
 
 
704
            eek_preview_set_pixbuf( preview, pixbuf );
 
705
        }
 
706
 
 
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)) );
 
711
 
 
712
        def.addCallback( _colorDefChanged, this );
 
713
 
 
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 );
 
718
 
 
719
/*
 
720
        Gtk::Button *btn = new Gtk::Button(blank);
 
721
        Gdk::Color color;
 
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);
 
727
 
 
728
        Gtk::Widget* newBlot = btn;
 
729
*/
 
730
 
 
731
        tips.set_tip((*newBlot), def.descr);
 
732
 
 
733
/*
 
734
        newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) );
 
735
 
 
736
        sigc::signal<void> type_signal_something;
 
737
*/
 
738
 
 
739
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
740
                          "clicked",
 
741
                          G_CALLBACK(handleClick),
 
742
                          this);
 
743
 
 
744
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
745
                          "alt-clicked",
 
746
                          G_CALLBACK(handleSecondaryClick),
 
747
                          this);
 
748
 
 
749
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
750
                          "button-press-event",
 
751
                          G_CALLBACK(handleButtonPress),
 
752
                          this);
 
753
 
 
754
        {
 
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());
 
761
                curr->flags = 0;
 
762
                if ( mimeToInt.find(*it) == mimeToInt.end() ){
 
763
                    // these next lines are order-dependent:
 
764
                    mimeToInt[*it] = mimeStrings.size();
 
765
                    mimeStrings.push_back(*it);
 
766
                }
 
767
                curr->info = mimeToInt[curr->target];
 
768
                curr++;
 
769
            }
 
770
            gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()),
 
771
                                 GDK_BUTTON1_MASK,
 
772
                                 entries, entryCount,
 
773
                                 GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
 
774
            for ( int i = 0; i < entryCount; i++ ) {
 
775
                g_free(entries[i].target);
 
776
            }
 
777
            delete[] entries;
 
778
        }
 
779
 
 
780
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
781
                          "drag-data-get",
 
782
                          G_CALLBACK(ColorItem::_dragGetColorData),
 
783
                          this);
 
784
 
 
785
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
786
                          "drag-begin",
 
787
                          G_CALLBACK(dragBegin),
 
788
                          this );
 
789
 
 
790
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
791
                          "enter-notify-event",
 
792
                          G_CALLBACK(handleEnterNotify),
 
793
                          this);
 
794
 
 
795
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
796
                          "leave-notify-event",
 
797
                          G_CALLBACK(handleLeaveNotify),
 
798
                          this);
 
799
 
 
800
//         g_signal_connect( G_OBJECT(newBlot->gobj()),
 
801
//                           "drag-drop",
 
802
//                           G_CALLBACK(dragDropColorData),
 
803
//                           this);
 
804
 
 
805
        if ( def.isEditable() )
 
806
        {
 
807
//             gtk_drag_dest_set( GTK_WIDGET(newBlot->gobj()),
 
808
//                                GTK_DEST_DEFAULT_ALL,
 
809
//                                destColorTargets,
 
810
//                                G_N_ELEMENTS(destColorTargets),
 
811
//                                GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
 
812
 
 
813
 
 
814
//             g_signal_connect( G_OBJECT(newBlot->gobj()),
 
815
//                               "drag-data-received",
 
816
//                               G_CALLBACK(_dropDataIn),
 
817
//                               this );
 
818
        }
 
819
 
 
820
        g_signal_connect( G_OBJECT(newBlot->gobj()),
 
821
                          "destroy",
 
822
                          G_CALLBACK(dieDieDie),
 
823
                          this);
 
824
 
 
825
 
 
826
        widget = newBlot;
 
827
    }
 
828
 
 
829
    _previews.push_back( widget );
 
830
 
 
831
    return widget;
 
832
}
 
833
 
 
834
void ColorItem::buttonClicked(bool secondary)
 
835
{
 
836
    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
837
    if (desktop) {
 
838
        char const * attrName = secondary ? "stroke" : "fill";
 
839
 
 
840
        SPCSSAttr *css = sp_repr_css_attr_new();
 
841
        Glib::ustring descr;
 
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");
 
847
                break;
 
848
            }
 
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");
 
852
                break;
 
853
            }
 
854
            case ege::PaintDef::RGB: {
 
855
                Glib::ustring colorspec;
 
856
                if ( gradMap.find(this) == gradMap.end() ){
 
857
                    gchar c[64];
 
858
                    guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff;
 
859
                    sp_svg_write_color(c, sizeof(c), rgba);
 
860
                    colorspec = c;
 
861
                } else {
 
862
                    SPGradient* grad = gradMap[this];
 
863
                    colorspec = "url(#";
 
864
                    colorspec += grad->id;
 
865
                    colorspec += ")";
 
866
                }
 
867
                sp_repr_css_set_property( css, attrName, colorspec.c_str() );
 
868
                descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch");
 
869
                break;
 
870
            }
 
871
        }
 
872
        sp_desktop_set_style(desktop, css);
 
873
        sp_repr_css_attr_unref(css);
 
874
 
 
875
        sp_document_done( sp_desktop_document(desktop), SP_VERB_DIALOG_SWATCHES, descr.c_str() );
 
876
    }
 
877
}
 
878
 
 
879
static char* trim( char* str ) {
 
880
    char* ret = str;
 
881
    while ( *str && (*str == ' ' || *str == '\t') ) {
 
882
        str++;
 
883
    }
 
884
    ret = str;
 
885
    while ( *str ) {
 
886
        str++;
 
887
    }
 
888
    str--;
 
889
    while ( str > ret && (( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n') ) {
 
890
        *str-- = 0;
 
891
    }
 
892
    return ret;
 
893
}
 
894
 
 
895
void skipWhitespace( char*& str ) {
 
896
    while ( *str == ' ' || *str == '\t' ) {
 
897
        str++;
 
898
    }
 
899
}
 
900
 
 
901
bool parseNum( char*& str, int& val ) {
 
902
    val = 0;
 
903
    while ( '0' <= *str && *str <= '9' ) {
 
904
        val = val * 10 + (*str - '0');
 
905
        str++;
 
906
    }
 
907
    bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
 
908
    return retval;
 
909
}
 
910
 
 
911
 
 
912
static bool getBlock( std::string& dst, guchar ch, std::string const str )
 
913
{
 
914
    bool good = false;
 
915
    std::string::size_type pos = str.find(ch);
 
916
    if ( pos != std::string::npos )
 
917
    {
 
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) );
 
923
                good = true;
 
924
            }
 
925
        }
 
926
    }
 
927
    return good;
 
928
}
 
929
 
 
930
static bool popVal( guint64& numVal, std::string& str )
 
931
{
 
932
    bool good = false;
 
933
    std::string::size_type endPos = str.find(',');
 
934
    if ( endPos == std::string::npos ) {
 
935
        endPos = str.length();
 
936
    }
 
937
 
 
938
    if ( endPos != std::string::npos && endPos > 0 ) {
 
939
        std::string xxx = str.substr( 0, endPos );
 
940
        const gchar* ptr = xxx.c_str();
 
941
        gchar* endPtr = 0;
 
942
        numVal = g_ascii_strtoull( ptr, &endPtr, 10 );
 
943
        if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
 
944
            // overflow
 
945
        } else if ( (numVal == 0) && (endPtr == ptr) ) {
 
946
            // failed conversion
 
947
        } else {
 
948
            good = true;
 
949
            str.erase( 0, endPos + 1 );
 
950
        }
 
951
    }
 
952
 
 
953
    return good;
 
954
}
 
955
 
 
956
void ColorItem::_wireMagicColors( void* p )
 
957
{
 
958
    JustForNow* onceMore = reinterpret_cast<JustForNow*>(p);
 
959
    if ( onceMore )
 
960
    {
 
961
        for ( std::vector<ColorItem*>::iterator it = onceMore->_colors.begin(); it != onceMore->_colors.end(); ++it )
 
962
        {
 
963
            std::string::size_type pos = (*it)->def.descr.find("*{");
 
964
            if ( pos != std::string::npos )
 
965
            {
 
966
                std::string subby = (*it)->def.descr.substr( pos + 2 );
 
967
                std::string::size_type endPos = subby.find("}*");
 
968
                if ( endPos != std::string::npos )
 
969
                {
 
970
                    subby.erase( endPos );
 
971
                    //g_message("FOUND MAGIC at '%s'", (*it)->def.descr.c_str());
 
972
                    //g_message("               '%s'", subby.c_str());
 
973
 
 
974
                    if ( subby.find('E') != std::string::npos )
 
975
                    {
 
976
                        (*it)->def.setEditable( true );
 
977
                    }
 
978
 
 
979
                    if ( subby.find('L') != std::string::npos )
 
980
                    {
 
981
                        (*it)->_isLive = true;
 
982
                    }
 
983
 
 
984
                    std::string part;
 
985
                    // Tint. index + 1 more val.
 
986
                    if ( getBlock( part, 'T', subby ) ) {
 
987
                        guint64 colorIndex = 0;
 
988
                        if ( popVal( colorIndex, part ) ) {
 
989
                            guint64 percent = 0;
 
990
                            if ( popVal( percent, part ) ) {
 
991
                                (*it)->_linkTint( *(onceMore->_colors[colorIndex]), percent );
 
992
                            }
 
993
                        }
 
994
                    }
 
995
 
 
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 ) ) {
 
1004
                                    grayLevel = 0;
 
1005
                                }
 
1006
                                (*it)->_linkTone( *(onceMore->_colors[colorIndex]), percent, grayLevel );
 
1007
                            }
 
1008
                        }
 
1009
                    }
 
1010
 
 
1011
                }
 
1012
            }
 
1013
        }
 
1014
    }
 
1015
}
 
1016
 
 
1017
 
 
1018
void ColorItem::_linkTint( ColorItem& other, int percent )
 
1019
{
 
1020
    if ( !_linkSrc )
 
1021
    {
 
1022
        other._listeners.push_back(this);
 
1023
        _linkIsTone = false;
 
1024
        _linkPercent = percent;
 
1025
        if ( _linkPercent > 100 )
 
1026
            _linkPercent = 100;
 
1027
        if ( _linkPercent < 0 )
 
1028
            _linkPercent = 0;
 
1029
        _linkGray = 0;
 
1030
        _linkSrc = &other;
 
1031
 
 
1032
        ColorItem::_colorDefChanged(&other);
 
1033
    }
 
1034
}
 
1035
 
 
1036
void ColorItem::_linkTone( ColorItem& other, int percent, int grayLevel )
 
1037
{
 
1038
    if ( !_linkSrc )
 
1039
    {
 
1040
        other._listeners.push_back(this);
 
1041
        _linkIsTone = true;
 
1042
        _linkPercent = percent;
 
1043
        if ( _linkPercent > 100 )
 
1044
            _linkPercent = 100;
 
1045
        if ( _linkPercent < 0 )
 
1046
            _linkPercent = 0;
 
1047
        _linkGray = grayLevel;
 
1048
        _linkSrc = &other;
 
1049
 
 
1050
        ColorItem::_colorDefChanged(&other);
 
1051
    }
 
1052
}
 
1053
 
 
1054
 
 
1055
void _loadPaletteFile( gchar const *filename )
 
1056
{
 
1057
    char block[1024];
 
1058
    FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
 
1059
    if ( f ) {
 
1060
        char* result = fgets( block, sizeof(block), f );
 
1061
        if ( result ) {
 
1062
            if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
 
1063
                bool inHeader = true;
 
1064
                bool hasErr = false;
 
1065
 
 
1066
                JustForNow *onceMore = new JustForNow();
 
1067
 
 
1068
                do {
 
1069
                    result = fgets( block, sizeof(block), f );
 
1070
                    block[sizeof(block) - 1] = 0;
 
1071
                    if ( result ) {
 
1072
                        if ( block[0] == '#' ) {
 
1073
                            // ignore comment
 
1074
                        } else {
 
1075
                            char *ptr = block;
 
1076
                            // very simple check for header versus entry
 
1077
                            while ( *ptr == ' ' || *ptr == '\t' ) {
 
1078
                                ptr++;
 
1079
                            }
 
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
 
1084
                                inHeader = false;
 
1085
                                ptr = block;
 
1086
                                Glib::ustring name("");
 
1087
                                int r = 0;
 
1088
                                int g = 0;
 
1089
                                int b = 0;
 
1090
                                skipWhitespace(ptr);
 
1091
                                if ( *ptr ) {
 
1092
                                    hasErr = parseNum(ptr, r);
 
1093
                                    if ( !hasErr ) {
 
1094
                                        skipWhitespace(ptr);
 
1095
                                        hasErr = parseNum(ptr, g);
 
1096
                                    }
 
1097
                                    if ( !hasErr ) {
 
1098
                                        skipWhitespace(ptr);
 
1099
                                        hasErr = parseNum(ptr, b);
 
1100
                                    }
 
1101
                                    if ( !hasErr && *ptr ) {
 
1102
                                        char* n = trim(ptr);
 
1103
                                        if (n != NULL) {
 
1104
                                            name = n;
 
1105
                                        }
 
1106
                                    }
 
1107
                                    if ( !hasErr ) {
 
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);
 
1112
                                    }
 
1113
                                } else {
 
1114
                                    hasErr = true;
 
1115
                                }
 
1116
                            } else {
 
1117
                                if ( !inHeader ) {
 
1118
                                    // Hmmm... probably bad. Not quite the format we want?
 
1119
                                    hasErr = true;
 
1120
                                } else {
 
1121
                                    char* sep = strchr(result, ':');
 
1122
                                    if ( sep ) {
 
1123
                                        *sep = 0;
 
1124
                                        char* val = trim(sep + 1);
 
1125
                                        char* name = trim(result);
 
1126
                                        if ( *name ) {
 
1127
                                            if ( strcmp( "Name", name ) == 0 )
 
1128
                                            {
 
1129
                                                onceMore->_name = val;
 
1130
                                            }
 
1131
                                            else if ( strcmp( "Columns", name ) == 0 )
 
1132
                                            {
 
1133
                                                gchar* endPtr = 0;
 
1134
                                                guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
 
1135
                                                if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
 
1136
                                                    // overflow
 
1137
                                                } else if ( (numVal == 0) && (endPtr == val) ) {
 
1138
                                                    // failed conversion
 
1139
                                                } else {
 
1140
                                                    onceMore->_prefWidth = numVal;
 
1141
                                                }
 
1142
                                            }
 
1143
                                        } else {
 
1144
                                            // error
 
1145
                                            hasErr = true;
 
1146
                                        }
 
1147
                                    } else {
 
1148
                                        // error
 
1149
                                        hasErr = true;
 
1150
                                    }
 
1151
                                }
 
1152
                            }
 
1153
                        }
 
1154
                    }
 
1155
                } while ( result && !hasErr );
 
1156
                if ( !hasErr ) {
 
1157
                    possible.push_back(onceMore);
 
1158
#if ENABLE_MAGIC_COLORS
 
1159
                    ColorItem::_wireMagicColors( onceMore );
 
1160
#endif // ENABLE_MAGIC_COLORS
 
1161
                } else {
 
1162
                    delete onceMore;
 
1163
                }
 
1164
            }
 
1165
        }
 
1166
 
 
1167
        fclose(f);
 
1168
    }
 
1169
}
 
1170
 
 
1171
static void loadEmUp()
 
1172
{
 
1173
    static bool beenHere = false;
 
1174
    if ( !beenHere ) {
 
1175
        beenHere = true;
 
1176
 
 
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) );
 
1181
 
 
1182
        // Use this loop to iterate through a list of possible document locations.
 
1183
        while (!sources.empty()) {
 
1184
            gchar *dirname = sources.front();
 
1185
 
 
1186
            if ( Inkscape::IO::file_test( dirname, G_FILE_TEST_EXISTS )
 
1187
                && Inkscape::IO::file_test( dirname, G_FILE_TEST_IS_DIR )) {
 
1188
                GError *err = 0;
 
1189
                GDir *directory = g_dir_open(dirname, 0, &err);
 
1190
                if (!directory) {
 
1191
                    gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
 
1192
                    g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
 
1193
                    g_free(safeDir);
 
1194
                } else {
 
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);
 
1202
                            }
 
1203
                            g_free(full);
 
1204
//                      }
 
1205
                        g_free(lower);
 
1206
                    }
 
1207
                    g_dir_close(directory);
 
1208
                }
 
1209
            }
 
1210
 
 
1211
            // toss the dirname
 
1212
            g_free(dirname);
 
1213
            sources.pop_front();
 
1214
        }
 
1215
    }
 
1216
}
 
1217
 
 
1218
 
 
1219
 
 
1220
 
 
1221
 
 
1222
 
 
1223
 
 
1224
 
 
1225
 
 
1226
SwatchesPanel& SwatchesPanel::getInstance()
 
1227
{
 
1228
    return *new SwatchesPanel();
 
1229
}
 
1230
 
 
1231
 
 
1232
/**
 
1233
 * Constructor
 
1234
 */
 
1235
SwatchesPanel::SwatchesPanel(gchar const* prefsPath) :
 
1236
    Inkscape::UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SWATCHES, "", true),
 
1237
    _holder(0),
 
1238
    _clear(0),
 
1239
    _remove(0),
 
1240
    _currentIndex(0),
 
1241
    _currentDesktop(0),
 
1242
    _currentDocument(0),
 
1243
    _ptr(0)
 
1244
{
 
1245
    Gtk::RadioMenuItem* hotItem = 0;
 
1246
    _holder = new PreviewHolder();
 
1247
    _clear = new ColorItem( ege::PaintDef::CLEAR );
 
1248
    _clear->ptr = this;
 
1249
    _remove = new ColorItem( ege::PaintDef::NONE );
 
1250
    _remove->ptr = this;
 
1251
    {
 
1252
        JustForNow *docPalette = new JustForNow();
 
1253
 
 
1254
        docPalette->_name = "Auto";
 
1255
        possible.push_back(docPalette);
 
1256
 
 
1257
        _ptr = docPalette;
 
1258
    }
 
1259
    loadEmUp();
 
1260
    if ( !possible.empty() ) {
 
1261
        JustForNow* first = 0;
 
1262
        int index = 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 ) {
 
1270
                        first = *iter;
 
1271
                        break;
 
1272
                    }
 
1273
                    index++;
 
1274
                }
 
1275
            }
 
1276
        }
 
1277
 
 
1278
        if ( !first ) {
 
1279
            first = possible.front();
 
1280
            _currentIndex = 0;
 
1281
        } else {
 
1282
            _currentIndex = index;
 
1283
        }
 
1284
 
 
1285
        _rebuild();
 
1286
 
 
1287
        Gtk::RadioMenuItem::Group groupOne;
 
1288
 
 
1289
        int i = 0;
 
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 ) {
 
1294
                hotItem = single;
 
1295
            }
 
1296
            _regItem( single, 3, i );
 
1297
            i++;
 
1298
        }
 
1299
    }
 
1300
 
 
1301
 
 
1302
    _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
 
1303
    _setTargetFillable(_holder);
 
1304
 
 
1305
    show_all_children();
 
1306
 
 
1307
    restorePanelPrefs();
 
1308
    if ( hotItem ) {
 
1309
        hotItem->set_active();
 
1310
    }
 
1311
}
 
1312
 
 
1313
SwatchesPanel::~SwatchesPanel()
 
1314
{
 
1315
    _documentConnection.disconnect();
 
1316
    _resourceConnection.disconnect();
 
1317
    _selChanged.disconnect();
 
1318
    _setModified.disconnect();
 
1319
    _subselChanged.disconnect();
 
1320
 
 
1321
    if ( _clear ) {
 
1322
        delete _clear;
 
1323
    }
 
1324
    if ( _remove ) {
 
1325
        delete _remove;
 
1326
    }
 
1327
    if ( _holder ) {
 
1328
        delete _holder;
 
1329
    }
 
1330
}
 
1331
 
 
1332
void SwatchesPanel::setOrientation( Gtk::AnchorType how )
 
1333
{
 
1334
    // Must call the parent class or bad things might happen
 
1335
    Inkscape::UI::Widget::Panel::setOrientation( how );
 
1336
 
 
1337
    if ( _holder )
 
1338
    {
 
1339
        _holder->setOrientation( Gtk::ANCHOR_SOUTH );
 
1340
    }
 
1341
}
 
1342
 
 
1343
void SwatchesPanel::setDesktop( SPDesktop* desktop )
 
1344
{
 
1345
    if ( desktop != _currentDesktop ) {
 
1346
        if ( _currentDesktop ) {
 
1347
            _documentConnection.disconnect();
 
1348
            _selChanged.disconnect();
 
1349
            _setModified.disconnect();
 
1350
            _subselChanged.disconnect();
 
1351
        }
 
1352
 
 
1353
        _currentDesktop = desktop;
 
1354
 
 
1355
        if ( desktop ) {
 
1356
            _currentDesktop->selection->connectChanged(
 
1357
                sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
 
1358
 
 
1359
            _currentDesktop->selection->connectModified(
 
1360
                sigc::hide(sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection))));
 
1361
 
 
1362
            _currentDesktop->connectToolSubselectionChanged(
 
1363
                sigc::hide(sigc::mem_fun(*this, &SwatchesPanel::_updateFromSelection)));
 
1364
 
 
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 );
 
1369
 
 
1370
            _setDocument( desktop->doc() );
 
1371
        } else {
 
1372
            _setDocument(0);
 
1373
        }
 
1374
    }
 
1375
}
 
1376
 
 
1377
void SwatchesPanel::_setDocument( SPDocument *document )
 
1378
{
 
1379
    if ( document != _currentDocument ) {
 
1380
        if ( _currentDocument ) {
 
1381
            _resourceConnection.disconnect();
 
1382
        }
 
1383
        _currentDocument = document;
 
1384
        if ( _currentDocument ) {
 
1385
            _resourceConnection = sp_document_resources_changed_connect(document,
 
1386
                                                                        "gradient",
 
1387
                                                                        sigc::mem_fun(*this, &SwatchesPanel::handleGradientsChange));
 
1388
        }
 
1389
 
 
1390
        handleGradientsChange();
 
1391
    }
 
1392
}
 
1393
 
 
1394
void SwatchesPanel::handleGradientsChange()
 
1395
{
 
1396
    std::vector<SPGradient*> newList;
 
1397
 
 
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));
 
1403
        }
 
1404
    }
 
1405
 
 
1406
    if ( _ptr ) {
 
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 )
 
1412
            {
 
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);
 
1419
 
 
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);
 
1424
 
 
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 );
 
1432
                        item->ptr = this;
 
1433
                        docPalette->_colors.push_back(item);
 
1434
                        gradMap[item] = grad;
 
1435
                    } else {
 
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 );
 
1442
                        item->ptr = this;
 
1443
                        docPalette->_colors.push_back(item);
 
1444
 
 
1445
                        gint width = 128;
 
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 );
 
1449
 
 
1450
                        sp_gradient_render_vector_block_rgb( grad,
 
1451
                                                             px, width, height, 3 * width,
 
1452
                                                             0, width, TRUE );
 
1453
 
 
1454
                        previewMap[item] = px;
 
1455
                        gradMap[item] = grad;
 
1456
                    }
 
1457
                }
 
1458
            }
 
1459
        }
 
1460
        JustForNow* curr = possible[_currentIndex];
 
1461
        if (curr == docPalette) {
 
1462
            _rebuild();
 
1463
        }
 
1464
    }
 
1465
}
 
1466
 
 
1467
void SwatchesPanel::_updateFromSelection()
 
1468
{
 
1469
    if ( _ptr ) {
 
1470
        JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr);
 
1471
 
 
1472
        Glib::ustring fillId;
 
1473
        Glib::ustring strokeId;
 
1474
 
 
1475
        SPStyle *tmpStyle = sp_style_new( sp_desktop_document(_currentDesktop) );
 
1476
        int result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_FILL );
 
1477
        switch (result) {
 
1478
            case QUERY_STYLE_SINGLE:
 
1479
            case QUERY_STYLE_MULTIPLE_AVERAGED:
 
1480
            case QUERY_STYLE_MULTIPLE_SAME:
 
1481
            {
 
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")) {
 
1488
                            target = grad;
 
1489
                        } else if ( grad->ref ) {
 
1490
                            SPGradient *tmp = grad->ref->getObject();
 
1491
                            if ( tmp && tmp->repr->attribute("osb:paint") ) {
 
1492
                                target = tmp;
 
1493
                            }
 
1494
                        }
 
1495
                        if ( target ) {
 
1496
                            gchar const* id = target->repr->attribute("id");
 
1497
                            if ( id ) {
 
1498
                                fillId = id;
 
1499
                            }
 
1500
                        }
 
1501
                    }
 
1502
                }
 
1503
                break;
 
1504
            }
 
1505
        }
 
1506
 
 
1507
        result = sp_desktop_query_style( _currentDesktop, tmpStyle, QUERY_STYLE_PROPERTY_STROKE );
 
1508
        switch (result) {
 
1509
            case QUERY_STYLE_SINGLE:
 
1510
            case QUERY_STYLE_MULTIPLE_AVERAGED:
 
1511
            case QUERY_STYLE_MULTIPLE_SAME:
 
1512
            {
 
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")) {
 
1519
                            target = grad;
 
1520
                        } else if ( grad->ref ) {
 
1521
                            SPGradient *tmp = grad->ref->getObject();
 
1522
                            if ( tmp && tmp->repr->attribute("osb:paint") ) {
 
1523
                                target = tmp;
 
1524
                            }
 
1525
                        }
 
1526
                        if ( target ) {
 
1527
                            gchar const* id = target->repr->attribute("id");
 
1528
                            if ( id ) {
 
1529
                                strokeId = id;
 
1530
                            }
 
1531
                        }
 
1532
                    }
 
1533
                }
 
1534
                break;
 
1535
            }
 
1536
        }
 
1537
        sp_style_unref(tmpStyle);
 
1538
 
 
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 );
 
1544
        }
 
1545
    }
 
1546
}
 
1547
 
 
1548
void SwatchesPanel::_handleAction( int setId, int itemId )
 
1549
{
 
1550
    switch( setId ) {
 
1551
        case 3:
 
1552
        {
 
1553
            if ( itemId >= 0 && itemId < static_cast<int>(possible.size()) ) {
 
1554
                _currentIndex = itemId;
 
1555
 
 
1556
                if ( !_prefs_path.empty() ) {
 
1557
                    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
1558
                    prefs->setString(_prefs_path + "/palette", possible[_currentIndex]->_name);
 
1559
                }
 
1560
 
 
1561
                _rebuild();
 
1562
            }
 
1563
        }
 
1564
        break;
 
1565
    }
 
1566
}
 
1567
 
 
1568
void SwatchesPanel::_rebuild()
 
1569
{
 
1570
    JustForNow* curr = possible[_currentIndex];
 
1571
    _holder->clear();
 
1572
 
 
1573
    if ( curr->_prefWidth > 0 ) {
 
1574
        _holder->setColumnPref( curr->_prefWidth );
 
1575
    }
 
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);
 
1581
    }
 
1582
    _holder->thawUpdates();
 
1583
}
 
1584
 
 
1585
 
 
1586
 
 
1587
 
 
1588
} //namespace Dialogs
 
1589
} //namespace UI
 
1590
} //namespace Inkscape
 
1591
 
 
1592
 
 
1593
/*
 
1594
  Local Variables:
 
1595
  mode:c++
 
1596
  c-file-style:"stroustrup"
 
1597
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1598
  indent-tabs-mode:nil
 
1599
  fill-column:99
 
1600
  End:
 
1601
*/
 
1602
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :