Code

remove unneeded document parameter from constructor
[inkscape.git] / src / dialogs / swatches.cpp
1 /*
2  * A simple panel for color swatches
3  *
4  * Authors:
5  *   Jon A. Cruz
6  *
7  * Copyright (C) 2005 Jon A. Cruz
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #include <errno.h>
17 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
18 #include <gtk/gtkdnd.h>
20 #include <glibmm/i18n.h>
21 #include "inkscape.h"
22 #include "document.h"
23 #include "desktop-handles.h"
24 #include "extension/db.h"
25 #include "inkscape.h"
26 #include "svg/svg.h"
27 #include "desktop-style.h"
28 #include "io/sys.h"
29 #include "path-prefix.h"
30 #include "swatches.h"
32 #include "eek-preview.h"
34 namespace Inkscape {
35 namespace UI {
36 namespace Dialogs {
38 SwatchesPanel* SwatchesPanel::instance = 0;
41 ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) :
42     _r(r),
43     _g(g),
44     _b(b),
45     _name(name)
46 {
47 }
49 ColorItem::~ColorItem()
50 {
51 }
53 ColorItem::ColorItem(ColorItem const &other) :
54     Inkscape::UI::Previewable()
55 {
56     if ( this != &other ) {
57         *this = other;
58     }
59 }
61 ColorItem &ColorItem::operator=(ColorItem const &other)
62 {
63     if ( this != &other ) {
64         _r = other._r;
65         _g = other._g;
66         _b = other._b;
67         _name = other._name;
68     }
69     return *this;
70 }
72 typedef enum {
73     XCOLOR_DATA = 0,
74     TEXT_DATA
75 } colorFlavorType;
77 static const GtkTargetEntry color_entries[] = {
78     {"application/x-color", 0, XCOLOR_DATA},
79     {"text/plain", 0, TEXT_DATA},
80 };
82 static void dragGetColorData( GtkWidget *widget,
83                               GdkDragContext *drag_context,
84                               GtkSelectionData *data,
85                               guint info,
86                               guint time,
87                               gpointer user_data)
88 {
89     static GdkAtom typeXColor = gdk_atom_intern("application/x-color", FALSE);
90     static GdkAtom typeText = gdk_atom_intern("text/plain", FALSE);
92     ColorItem* item = reinterpret_cast<ColorItem*>(user_data);
93     if ( info == 1 ) {
94         gchar* tmp = g_strdup_printf("#%02x%02x%02x", item->_r, item->_g, item->_b);
96         gtk_selection_data_set( data,
97                                 typeText,
98                                 8, // format
99                                 (guchar*)tmp,
100                                 strlen((const char*)tmp) + 1);
101         g_free(tmp);
102         tmp = 0;
103     } else {
104         guint16 tmp[4];
105         tmp[0] = (item->_r << 8) | item->_r;
106         tmp[1] = (item->_g << 8) | item->_g;
107         tmp[2] = (item->_b << 8) | item->_b;
108         tmp[3] = 0xffff;
109         gtk_selection_data_set( data,
110                                 typeXColor,
111                                 16, // format
112                                 reinterpret_cast<const guchar*>(tmp),
113                                 (3+1) * 2);
114     }
117 //"drag-drop"
118 gboolean dragDropColorData( GtkWidget *widget,
119                             GdkDragContext *drag_context,
120                             gint x,
121                             gint y,
122                             guint time,
123                             gpointer user_data)
125 // TODO finish
126     return TRUE;
129 static void bouncy( GtkWidget* widget, gpointer callback_data ) {
130     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
131     if ( item ) {
132         item->buttonClicked(false);
133     }
136 static void bouncy2( GtkWidget* widget, gint arg1, gpointer callback_data ) {
137     ColorItem* item = reinterpret_cast<ColorItem*>(callback_data);
138     if ( item ) {
139         item->buttonClicked(true);
140     }
143 Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, Gtk::BuiltinIconSize size)
145     Gtk::Widget* widget = 0;
146     if ( style == PREVIEW_STYLE_BLURB ) {
147         Gtk::Label *lbl = new Gtk::Label(_name);
148         lbl->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER);
149         widget = lbl;
150     } else {
151         Glib::ustring blank("          ");
152         if ( size == Gtk::ICON_SIZE_MENU ) {
153             blank = " ";
154         }
156         GtkWidget* eekWidget = eek_preview_new();
157         EekPreview * preview = EEK_PREVIEW(eekWidget);
158         Gtk::Widget* newBlot = Glib::wrap(eekWidget);
160         eek_preview_set_color( preview, (_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b);
162         eek_preview_set_details( preview, (::PreviewStyle)style, (::ViewType)view, (::GtkIconSize)size );
164         GValue val = {0, {{0}, {0}}};
165         g_value_init( &val, G_TYPE_BOOLEAN );
166         g_value_set_boolean( &val, FALSE );
167         g_object_set_property( G_OBJECT(preview), "focus-on-click", &val );
169 /*
170         Gtk::Button *btn = new Gtk::Button(blank);
171         Gdk::Color color;
172         color.set_rgb((_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b);
173         btn->modify_bg(Gtk::STATE_NORMAL, color);
174         btn->modify_bg(Gtk::STATE_ACTIVE, color);
175         btn->modify_bg(Gtk::STATE_PRELIGHT, color);
176         btn->modify_bg(Gtk::STATE_SELECTED, color);
178         Gtk::Widget* newBlot = btn;
179 */
181         tips.set_tip((*newBlot), _name);
183 /*
184         newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) );
186         sigc::signal<void> type_signal_something;
187 */
188         g_signal_connect( G_OBJECT(newBlot->gobj()),
189                           "clicked",
190                           G_CALLBACK(bouncy),
191                           this);
193         g_signal_connect( G_OBJECT(newBlot->gobj()),
194                           "alt-clicked",
195                           G_CALLBACK(bouncy2),
196                           this);
198         gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()),
199                              GDK_BUTTON1_MASK,
200                              color_entries,
201                              G_N_ELEMENTS(color_entries),
202                              GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
204         g_signal_connect( G_OBJECT(newBlot->gobj()),
205                           "drag-data-get",
206                           G_CALLBACK(dragGetColorData),
207                           this);
209         g_signal_connect( G_OBJECT(newBlot->gobj()),
210                           "drag-drop",
211                           G_CALLBACK(dragDropColorData),
212                           this);
214         widget = newBlot;
215     }
217     return widget;
220 void ColorItem::buttonClicked(bool secondary)
222     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
223     if (desktop) {
224         char const * attrName = secondary ? "stroke" : "fill";
225         guint32 rgba = (_r << 24) | (_g << 16) | (_b << 8) | 0xff;
226         gchar c[64];
227         sp_svg_write_color(c, 64, rgba);
229         SPCSSAttr *css = sp_repr_css_attr_new();
230         sp_repr_css_set_property( css, attrName, c );
231         sp_desktop_set_style(desktop, css);
233         sp_repr_css_attr_unref(css);
234         sp_document_done (SP_DT_DOCUMENT (desktop));
235     }
241 static char* trim( char* str ) {
242     char* ret = str;
243     while ( *str && (*str == ' ' || *str == '\t') ) {
244         str++;
245     }
246     ret = str;
247     while ( *str ) {
248         str++;
249     }
250     str--;
251     while ( str > ret && ( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n' ) {
252         *str-- = 0;
253     }
254     return ret;
257 void skipWhitespace( char*& str ) {
258     while ( *str == ' ' || *str == '\t' ) {
259         str++;
260     }
263 bool parseNum( char*& str, int& val ) {
264     val = 0;
265     while ( '0' <= *str && *str <= '9' ) {
266         val = val * 10 + (*str - '0');
267         str++;
268     }
269     bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n');
270     return retval;
274 class JustForNow
276 public:
277     JustForNow() : _prefWidth(0) {}
279     Glib::ustring _name;
280     int _prefWidth;
281     std::vector<ColorItem*> _colors;
282 };
284 static std::vector<JustForNow*> possible;
286 static void loadPaletteFile( gchar const *filename )
288     char block[1024];
289     FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" );
290     if ( f ) {
291         char* result = fgets( block, sizeof(block), f );
292         if ( result ) {
293             if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) {
294                 bool inHeader = true;
295                 bool hasErr = false;
297                 JustForNow *onceMore = new JustForNow();
299                 do {
300                     result = fgets( block, sizeof(block), f );
301                     block[sizeof(block) - 1] = 0;
302                     if ( result ) {
303                         if ( block[0] == '#' ) {
304                             // ignore comment
305                         } else {
306                             char *ptr = block;
307                             // very simple check for header versus entry
308                             while ( *ptr == ' ' || *ptr == '\t' ) {
309                                 ptr++;
310                             }
311                             if ( *ptr == 0 ) {
312                                 // blank line. skip it.
313                             } else if ( '0' <= *ptr && *ptr <= '9' ) {
314                                 // should be an entry link
315                                 inHeader = false;
316                                 ptr = block;
317                                 Glib::ustring name("");
318                                 int r = 0;
319                                 int g = 0;
320                                 int b = 0;
321                                 skipWhitespace(ptr);
322                                 if ( *ptr ) {
323                                     hasErr = parseNum(ptr, r);
324                                     if ( !hasErr ) {
325                                         skipWhitespace(ptr);
326                                         hasErr = parseNum(ptr, g);
327                                     }
328                                     if ( !hasErr ) {
329                                         skipWhitespace(ptr);
330                                         hasErr = parseNum(ptr, b);
331                                     }
332                                     if ( !hasErr && *ptr ) {
333                                         char* n = trim(ptr);
334                                         if (n != NULL) {
335                                             name = n;
336                                         }
337                                     }
338                                     if ( !hasErr ) {
339                                         // Add the entry now
340                                         Glib::ustring nameStr(name);
341                                         ColorItem* item = new ColorItem( r, g, b, nameStr );
342                                         onceMore->_colors.push_back(item);
343                                     }
344                                 } else {
345                                     hasErr = true;
346                                 }
347                             } else {
348                                 if ( !inHeader ) {
349                                     // Hmmm... probably bad. Not quite the format we want?
350                                     hasErr = true;
351                                 } else {
352                                     char* sep = strchr(result, ':');
353                                     if ( sep ) {
354                                         *sep = 0;
355                                         char* val = trim(sep + 1);
356                                         char* name = trim(result);
357                                         if ( *name ) {
358                                             if ( strcmp( "Name", name ) == 0 )
359                                             {
360                                                 onceMore->_name = val;
361                                             }
362                                             else if ( strcmp( "Columns", name ) == 0 )
363                                             {
364                                                 gchar* endPtr = 0;
365                                                 guint64 numVal = g_ascii_strtoull( val, &endPtr, 10 );
366                                                 if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) {
367                                                     // overflow
368                                                 } else if ( (numVal == 0) && (endPtr == val) ) {
369                                                     // failed conversion
370                                                 } else {
371                                                     onceMore->_prefWidth = numVal;
372                                                 }
373                                             }
374                                         } else {
375                                             // error
376                                             hasErr = true;
377                                         }
378                                     } else {
379                                         // error
380                                         hasErr = true;
381                                     }
382                                 }
383                             }
384                         }
385                     }
386                 } while ( result && !hasErr );
387                 if ( !hasErr ) {
388                     possible.push_back(onceMore);
389                 } else {
390                     delete onceMore;
391                 }
392             }
393         }
395         fclose(f);
396     }
399 static void loadEmUp()
401     static bool beenHere = false;
402     if ( !beenHere ) {
403         beenHere = true;
405         std::list<gchar *> sources;
406         sources.push_back( profile_path("palettes") );
407         sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) );
409         // Use this loop to iterate through a list of possible document locations.
410         while (!sources.empty()) {
411             gchar *dirname = sources.front();
413             if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
414                 GError *err = 0;
415                 GDir *directory = g_dir_open(dirname, 0, &err);
416                 if (!directory) {
417                     gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
418                     g_warning(_("Palettes directory (%s) is unavailable."), safeDir);
419                     g_free(safeDir);
420                 } else {
421                     gchar *filename = 0;
422                     while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) {
423                         gchar* full = g_build_filename(dirname, filename, NULL);
424                         if ( !Inkscape::IO::file_test( full, (GFileTest)(G_FILE_TEST_IS_DIR ) ) ) {
425                             loadPaletteFile(full);
426                         }
427                         g_free(full);
428                     }
429                     g_dir_close(directory);
430                 }
431             }
433             // toss the dirname
434             g_free(dirname);
435             sources.pop_front();
436         }
437     }
448 SwatchesPanel& SwatchesPanel::getInstance()
450     if ( !instance ) {
451         instance = new SwatchesPanel();
452     }
454     return *instance;
459 /**
460  * Constructor
461  */
462 SwatchesPanel::SwatchesPanel() :
463     Inkscape::UI::Widget::Panel ("dialogs.swatches"),
464     _holder(0)
466     _holder = new PreviewHolder();
467     loadEmUp();
469     if ( !possible.empty() ) {
470         JustForNow* first = possible.front();
471         if ( first->_prefWidth > 0 ) {
472             _holder->setColumnPref( first->_prefWidth );
473         }
474         _holder->freezeUpdates();
475         for ( std::vector<ColorItem*>::iterator it = first->_colors.begin(); it != first->_colors.end(); it++ ) {
476             _holder->addPreview(*it);
477         }
478         _holder->thawUpdates();
480         Gtk::RadioMenuItem::Group groupOne;
481         int i = 0;
482         for ( std::vector<JustForNow*>::iterator it = possible.begin(); it != possible.end(); it++ ) {
483             JustForNow* curr = *it;
484             Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name));
485             _regItem( single, 3, i );
486             i++;
487         }
489     }
492     _getContents()->pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET);
493     _setTargetFillable(_holder);
495     show_all_children();
497     restorePanelPrefs();
500 SwatchesPanel::~SwatchesPanel()
504 void SwatchesPanel::setOrientation( Gtk::AnchorType how )
506     // Must call the parent class or bad things might happen
507     Inkscape::UI::Widget::Panel::setOrientation( how );
509     if ( _holder )
510     {
511         _holder->setOrientation( Gtk::ANCHOR_SOUTH );
512     }
515 void SwatchesPanel::_handleAction( int setId, int itemId )
517     switch( setId ) {
518         case 3:
519         {
520             if ( itemId >= 0 && itemId < static_cast<int>(possible.size()) ) {
521                 _holder->clear();
522                 JustForNow* curr = possible[itemId];
523                 if ( curr->_prefWidth > 0 ) {
524                     _holder->setColumnPref( curr->_prefWidth );
525                 }
526                 _holder->freezeUpdates();
527                 for ( std::vector<ColorItem*>::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) {
528                     _holder->addPreview(*it);
529                 }
530                 _holder->thawUpdates();
531             }
532         }
533         break;
534     }
537 } //namespace Dialogs
538 } //namespace UI
539 } //namespace Inkscape
542 /*
543   Local Variables:
544   mode:c++
545   c-file-style:"stroustrup"
546   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
547   indent-tabs-mode:nil
548   fill-column:99
549   End:
550 */
551 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :