6
* Copyright (C) 2012 Tavmjong Bah
8
* Released under GNU GPL, read the file 'COPYING' for more information
18
#include <gtkmm/buttonbox.h>
19
#include <gtkmm/label.h>
20
#include <gtkmm/table.h>
21
#include <gtkmm/scrolledwindow.h>
22
#include <gtkmm/comboboxtext.h>
23
#include <gtkmm/iconview.h>
24
#include <gtkmm/liststore.h>
25
#include <gtkmm/treemodelcolumn.h>
26
#include <gtkmm/clipboard.h>
28
#include "path-prefix.h"
31
#include "ui/cache/svg_preview_cache.h"
32
#include "ui/clipboard.h"
37
#include "desktop-handles.h"
42
#include "sp-symbol.h"
50
static Cache::SvgPreview svg_preview_cache;
54
// See: http://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumnRecord.html
55
class SymbolColumns : public Gtk::TreeModel::ColumnRecord
59
Gtk::TreeModelColumn<Glib::ustring> symbol_id;
60
Gtk::TreeModelColumn<Glib::ustring> symbol_title;
61
Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > symbol_image;
70
SymbolColumns* SymbolsDialog::getColumns()
72
SymbolColumns* columns = new SymbolColumns();
79
SymbolsDialog::SymbolsDialog( gchar const* prefsPath ) :
80
UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SYMBOLS),
81
store(Gtk::ListStore::create(*getColumns())),
92
/******************** Table *************************/
93
// Replace by Grid for GTK 3.0
94
Gtk::Table *table = new Gtk::Table(2, 4, false);
95
// panel is a cloked Gtk::VBox
96
_getContents()->pack_start(*Gtk::manage(table), Gtk::PACK_EXPAND_WIDGET);
99
/******************** Symbol Sets *************************/
100
Gtk::Label* labelSet = new Gtk::Label("Symbol set: ");
101
table->attach(*Gtk::manage(labelSet),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK);
103
symbolSet = new Gtk::ComboBoxText(); // Fill in later
104
symbolSet->append("Current Document");
105
symbolSet->set_active_text("Current Document");
106
table->attach(*Gtk::manage(symbolSet),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK);
108
sigc::connection connSet =
109
symbolSet->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild));
110
instanceConns.push_back(connSet);
114
/********************* Icon View **************************/
115
SymbolColumns* columns = getColumns();
117
iconView = new Gtk::IconView(static_cast<Glib::RefPtr<Gtk::TreeModel> >(store));
118
//iconView->set_text_column( columns->symbol_id );
119
iconView->set_tooltip_column( 1 );
120
iconView->set_pixbuf_column( columns->symbol_image );
122
sigc::connection connIconChanged;
124
iconView->signal_selection_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::iconChanged));
125
instanceConns.push_back(connIconChanged);
127
Gtk::ScrolledWindow *scroller = new Gtk::ScrolledWindow();
128
scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
129
scroller->add(*Gtk::manage(iconView));
130
table->attach(*Gtk::manage(scroller),0,2,row,row+1,Gtk::EXPAND|Gtk::FILL,Gtk::EXPAND|Gtk::FILL);
134
/******************** Preview Scale ***********************/
135
Gtk::Label* labelScale = new Gtk::Label("Preview scale: ");
136
table->attach(*Gtk::manage(labelScale),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK);
138
previewScale = new Gtk::ComboBoxText();
139
const gchar *scales[] =
140
{"Fit", "Fit to width", "Fit to height", "0.1", "0.2", "0.5", "1.0", "2.0", "5.0", NULL};
141
for( int i = 0; scales[i]; ++i ) {
142
previewScale->append(scales[i]);
144
previewScale->set_active_text(scales[0]);
145
table->attach(*Gtk::manage(previewScale),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK);
147
sigc::connection connScale =
148
previewScale->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild));
149
instanceConns.push_back(connScale);
153
/******************** Preview Size ************************/
154
Gtk::Label* labelSize = new Gtk::Label("Preview size: ");
155
table->attach(*Gtk::manage(labelSize),0,1,row,row+1,Gtk::SHRINK,Gtk::SHRINK);
157
previewSize = new Gtk::ComboBoxText();
158
const gchar *sizes[] = {"16", "24", "32", "48", "64", NULL};
159
for( int i = 0; sizes[i]; ++i ) {
160
previewSize->append(sizes[i]);
162
previewSize->set_active_text(sizes[2]);
163
table->attach(*Gtk::manage(previewSize),1,2,row,row+1,Gtk::FILL|Gtk::EXPAND,Gtk::SHRINK);
165
sigc::connection connSize =
166
previewSize->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild));
167
instanceConns.push_back(connSize);
171
/**********************************************************/
172
currentDesktop = inkscape_active_desktop();
173
currentDocument = sp_desktop_document(currentDesktop);
175
previewDocument = symbols_preview_doc(); /* Template to render symbols in */
176
previewDocument->ensureUpToDate(); /* Necessary? */
178
key = SPItem::display_key_new(1);
179
renderDrawing.setRoot(previewDocument->getRoot()->invoke_show(renderDrawing, key, SP_ITEM_SHOW_DISPLAY ));
182
draw_symbols( currentDocument ); /* Defaults to current document */
185
deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &SymbolsDialog::setTargetDesktop) );
186
instanceConns.push_back( desktopChangeConn );
187
deskTrack.connect(GTK_WIDGET(gobj()));
190
SymbolsDialog::~SymbolsDialog()
192
for (std::vector<sigc::connection>::iterator it = instanceConns.begin(); it != instanceConns.end(); ++it) {
195
instanceConns.clear();
196
deskTrack.disconnect();
199
SymbolsDialog& SymbolsDialog::getInstance()
201
return *new SymbolsDialog();
204
void SymbolsDialog::rebuild() {
207
Glib::ustring symbolSetString = symbolSet->get_active_text();
209
SPDocument* symbolDocument = symbolSets[symbolSetString];
210
if( !symbolDocument ) {
211
// Symbol must be from Current Document (this method of
212
// checking should be language independent).
213
symbolDocument = currentDocument;
215
draw_symbols( symbolDocument );
218
void SymbolsDialog::iconChanged() {
220
std::vector<Gtk::TreePath> iconArray = iconView->get_selected_items();
222
Gtk::IconView::ArrayHandle_TreePaths iconArray = iconView->get_selected_items();
225
if( iconArray.empty() ) {
226
//std::cout << " iconArray empty: huh? " << std::endl;
228
Gtk::TreeModel::Path const & path = *iconArray.begin();
229
Gtk::ListStore::iterator row = store->get_iter(path);
230
Glib::ustring symbol_id = (*row)[getColumns()->symbol_id];
232
/* OK, we know symbol name... now we need to copy it to clipboard, bon chance! */
233
Glib::ustring symbolSetString = symbolSet->get_active_text();
235
SPDocument* symbolDocument = symbolSets[symbolSetString];
236
if( !symbolDocument ) {
237
// Symbol must be from Current Document (this method of
238
// checking should be language independent).
239
symbolDocument = currentDocument;
242
SPObject* symbol = symbolDocument->getObjectById(symbol_id);
245
// Find style for use in <use>
246
// First look for default style stored in <symbol>
247
gchar const* style = symbol->getAttribute("inkscape:symbol-style");
249
// If no default style in <symbol>, look in documents.
250
if( symbolDocument == currentDocument ) {
251
style = style_from_use( symbol_id.c_str(), currentDocument );
253
style = symbolDocument->getReprRoot()->attribute("style");
257
ClipboardManager *cm = ClipboardManager::get();
258
cm->copySymbol(symbol->getRepr(), style);
263
/* Hunts preference directories for symbol files */
264
void SymbolsDialog::get_symbols() {
266
std::list<Glib::ustring> directories;
268
if( Inkscape::IO::file_test( INKSCAPE_SYMBOLSDIR, G_FILE_TEST_EXISTS ) &&
269
Inkscape::IO::file_test( INKSCAPE_SYMBOLSDIR, G_FILE_TEST_IS_DIR ) ) {
270
directories.push_back( INKSCAPE_SYMBOLSDIR );
272
if( Inkscape::IO::file_test( profile_path("symbols"), G_FILE_TEST_EXISTS ) &&
273
Inkscape::IO::file_test( profile_path("symbols"), G_FILE_TEST_IS_DIR ) ) {
274
directories.push_back( profile_path("symbols") );
277
std::list<Glib::ustring>::iterator it;
278
for( it = directories.begin(); it != directories.end(); ++it ) {
281
GDir *dir = g_dir_open( (*it).c_str(), 0, &err );
285
while( (filename = (gchar *)g_dir_read_name( dir ) ) != NULL) {
287
gchar *fullname = g_build_filename((*it).c_str(), filename, NULL);
289
if ( !Inkscape::IO::file_test( fullname, G_FILE_TEST_IS_DIR ) ) {
291
SPDocument* symbol_doc = SPDocument::createNewDoc( fullname, FALSE );
293
symbolSets[Glib::ustring(filename)]= symbol_doc;
294
symbolSet->append(filename);
304
GSList* SymbolsDialog::symbols_in_doc_recursive (SPObject *r, GSList *l)
307
// Stop multiple counting of same symbol
312
if( SP_IS_SYMBOL(r) ) {
313
l = g_slist_prepend (l, r);
316
for (SPObject *child = r->firstChild(); child; child = child->getNext()) {
317
l = symbols_in_doc_recursive( child, l );
323
GSList* SymbolsDialog::symbols_in_doc( SPDocument* symbolDocument ) {
326
l = symbols_in_doc_recursive (symbolDocument->getRoot(), l );
330
GSList* SymbolsDialog::use_in_doc_recursive (SPObject *r, GSList *l)
334
l = g_slist_prepend (l, r);
337
for (SPObject *child = r->firstChild(); child; child = child->getNext()) {
338
l = use_in_doc_recursive( child, l );
344
GSList* SymbolsDialog::use_in_doc( SPDocument* useDocument ) {
347
l = use_in_doc_recursive (useDocument->getRoot(), l );
351
// Returns style from first <use> element found that references id.
352
// This is a last ditch effort to find a style.
353
gchar const* SymbolsDialog::style_from_use( gchar const* id, SPDocument* document) {
355
gchar const* style = 0;
356
GSList* l = use_in_doc( document );
357
for( ; l != NULL; l = l->next ) {
358
SPObject* use = SP_OBJECT(l->data);
359
if( SP_IS_USE( use ) ) {
360
gchar const *href = use->getRepr()->attribute("xlink:href");
362
Glib::ustring href2(href);
363
Glib::ustring id2(id);
365
if( !href2.compare(id2) ) {
366
style = use->getRepr()->attribute("style");
375
void SymbolsDialog::draw_symbols( SPDocument* symbolDocument ) {
378
SymbolColumns* columns = getColumns();
380
GSList* l = symbols_in_doc( symbolDocument );
381
for( ; l != NULL; l = l->next ) {
383
SPObject* symbol = SP_OBJECT(l->data);
384
if (!SP_IS_SYMBOL(symbol)) {
385
//std::cout << " Error: not symbol" << std::endl;
389
gchar const *id = symbol->getRepr()->attribute("id");
390
gchar const *title = symbol->title(); // From title element
395
Glib::RefPtr<Gdk::Pixbuf> pixbuf = create_symbol_image(id, symbolDocument, &renderDrawing, key );
398
Gtk::ListStore::iterator row = store->append();
399
(*row)[columns->symbol_id] = Glib::ustring( id );
400
(*row)[columns->symbol_title] = Glib::ustring( title );
401
(*row)[columns->symbol_image] = pixbuf;
407
* Returns image of symbol.
409
* Symbols normally are not visible. They must be referenced by a
410
* <use> element. A temporary document is created with a dummy
411
* <symbol> element and a <use> element that references the symbol
412
* element. Each real symbol is swapped in for the dummy symbol and
413
* the temporary document is rendered.
415
Glib::RefPtr<Gdk::Pixbuf>
416
SymbolsDialog::create_symbol_image(gchar const *symbol_id,
418
Inkscape::Drawing* drawing,
419
unsigned /*visionkey*/)
422
// Retrieve the symbol named 'symbol_id' from the source SVG document
423
SPObject const* symbol = source->getObjectById(symbol_id);
424
if (symbol == NULL) {
425
//std::cout << " Failed to find symbol: " << symbol_id << std::endl;
429
// Create a copy repr of the symbol with id="the_symbol"
430
Inkscape::XML::Document *xml_doc = previewDocument->getReprDoc();
431
Inkscape::XML::Node *repr = symbol->getRepr()->duplicate(xml_doc);
432
repr->setAttribute("id", "the_symbol");
434
// Replace old "the_symbol" in previewDocument by new.
435
Inkscape::XML::Node *root = previewDocument->getReprRoot();
436
SPObject *symbol_old = previewDocument->getObjectById("the_symbol");
438
symbol_old->deleteObject(false);
441
// First look for default style stored in <symbol>
442
gchar const* style = repr->attribute("inkscape:symbol-style");
444
// If no default style in <symbol>, look in documents.
445
if( source == currentDocument ) {
446
style = style_from_use( symbol_id, source );
448
style = source->getReprRoot()->attribute("style");
452
// This is for display in Symbols dialog only
454
repr->setAttribute( "style", style );
457
// BUG: Symbols don't work if defined outside of <defs>. Causes Inkscape
458
// crash when trying to read in such a file.
459
root->appendChild(repr);
460
//defsrepr->appendChild(repr);
461
Inkscape::GC::release(repr);
463
// Uncomment this to get the previewDocument documents saved (useful for debugging)
464
// FILE *fp = fopen (g_strconcat(symbol_id, ".svg", NULL), "w");
465
// sp_repr_save_stream(previewDocument->getReprDoc(), fp);
468
// Make sure previewDocument is up-to-date.
469
previewDocument->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
470
previewDocument->ensureUpToDate();
472
// Make sure we have symbol in previewDocument
473
SPObject *object_temp = previewDocument->getObjectById( "the_use" );
474
previewDocument->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
475
previewDocument->ensureUpToDate();
477
// if( object_temp == NULL || !SP_IS_ITEM(object_temp) ) {
478
// //std::cout << " previewDocument broken?" << std::endl;
482
SPItem *item = SP_ITEM(object_temp);
484
// Find object's bbox in document.
485
// Note symbols can have own viewport... ignore for now.
486
//Geom::OptRect dbox = item->geometricBounds();
487
Geom::OptRect dbox = item->documentVisualBounds();
489
// //std::cout << " No dbox" << std::endl;
493
Glib::ustring previewSizeString = previewSize->get_active_text();
494
unsigned psize = atol( previewSizeString.c_str() );
496
Glib::ustring previewScaleString = previewScale->get_active_text();
497
int previewScaleRow = previewScale->get_active_row_number();
499
/* Update to renderable state */
500
Glib::ustring key = svg_preview_cache.cache_key(previewDocument->getURI(), symbol_id, psize);
501
//std::cout << " Key: " << key << std::endl;
503
//Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(svg_preview_cache.get_preview_from_cache(key));
504
Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::RefPtr<Gdk::Pixbuf>(0);
508
/* Scale symbols to fit */
510
switch (previewScaleRow) {
513
scale = psize/std::max(dbox->width(),dbox->height());
517
scale = psize/dbox->width();
521
scale = psize/dbox->height();
524
scale = atof( previewScaleString.c_str() );
527
pixbuf = Glib::wrap(render_pixbuf(*drawing, scale, *dbox, psize));
528
svg_preview_cache.set_preview_in_cache(key, pixbuf->gobj());
535
* Return empty doc to render symbols in.
536
* Symbols are by default not rendered so a <use> element is
539
SPDocument* SymbolsDialog::symbols_preview_doc()
541
// BUG: <symbol> must be inside <defs>
542
gchar const *buffer =
543
"<svg xmlns=\"http://www.w3.org/2000/svg\""
544
" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
545
" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\""
546
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""
547
" style=\"fill:none;stroke:black;stroke-width:2\">"
548
" <defs id=\"defs\">"
549
" <symbol id=\"the_symbol\"/>"
551
" <use id=\"the_use\" xlink:href=\"#the_symbol\"/>"
554
return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE );
557
void SymbolsDialog::setTargetDesktop(SPDesktop *desktop)
559
if (this->currentDesktop != desktop) {
560
this->currentDesktop = desktop;
561
if( !symbolSets[symbolSet->get_active_text()] ) {
562
// Symbol set is from Current document, update
569
} //namespace Dialogs
571
} //namespace Inkscape