1 /** @file
2 * @brief A simple dialog for previewing icon representation.
3 */
4 /* Authors:
5 * Jon A. Cruz
6 * Bob Jamison
7 * Other dudes from The Inkscape Organization
8 * Abhishek Sharma
9 *
10 * Copyright (C) 2004 Bob Jamison
11 * Copyright (C) 2005,2010 Jon A. Cruz
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
15 #ifdef HAVE_CONFIG_H
16 # include <config.h>
17 #endif
19 #include <gtk/gtk.h>
20 #include <glib/gmem.h>
21 #include <glibmm/i18n.h>
22 #include <gtkmm/alignment.h>
23 #include <gtkmm/buttonbox.h>
24 #include <gtkmm/stock.h>
26 #include "desktop.h"
27 #include "desktop-handles.h"
28 #include "display/nr-arena.h"
29 #include "document.h"
30 #include "inkscape.h"
31 #include "preferences.h"
32 #include "selection.h"
33 #include "sp-root.h"
34 #include "xml/repr.h"
36 #include "icon-preview.h"
38 extern "C" {
39 // takes doc, root, icon, and icon name to produce pixels
40 guchar *
41 sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root,
42 const gchar *name, unsigned int psize );
43 }
45 #define noICON_VERBOSE 1
47 namespace Inkscape {
48 namespace UI {
49 namespace Dialog {
52 IconPreviewPanel &IconPreviewPanel::getInstance()
53 {
54 IconPreviewPanel *instance = new IconPreviewPanel();
56 instance->refreshPreview();
58 return *instance;
59 }
61 //#########################################################################
62 //## E V E N T S
63 //#########################################################################
65 void IconPreviewPanel::on_button_clicked(int which)
66 {
67 if ( hot != which ) {
68 buttons[hot]->set_active( false );
70 hot = which;
71 updateMagnify();
72 _getContents()->queue_draw();
73 }
74 }
79 //#########################################################################
80 //## C O N S T R U C T O R / D E S T R U C T O R
81 //#########################################################################
82 /**
83 * Constructor
84 */
85 IconPreviewPanel::IconPreviewPanel() :
86 UI::Widget::Panel("", "/dialogs/iconpreview", SP_VERB_VIEW_ICON_PREVIEW),
87 deskTrack(),
88 desktop(0),
89 document(0),
90 timer(0),
91 renderTimer(0),
92 pending(false),
93 minDelay(0.1),
94 targetId(),
95 hot(1),
96 selectionButton(0),
97 desktopChangeConn(),
98 docReplacedConn(),
99 docModConn(),
100 selChangedConn()
101 {
102 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
103 numEntries = 0;
105 bool pack = prefs->getBool("/iconpreview/pack", true);
107 std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default");
108 std::vector<int> rawSizes;
110 for (std::vector<Glib::ustring>::iterator i = pref_sizes.begin(); i != pref_sizes.end(); ++i) {
111 if (prefs->getBool(*i + "/show", true)) {
112 int sizeVal = prefs->getInt(*i + "/value", -1);
113 if (sizeVal > 0) {
114 rawSizes.push_back(sizeVal);
115 }
116 }
117 }
119 if ( !rawSizes.empty() ) {
120 numEntries = rawSizes.size();
121 sizes = new int[numEntries];
122 int i = 0;
123 for ( std::vector<int>::iterator it = rawSizes.begin(); it != rawSizes.end(); ++it, ++i ) {
124 sizes[i] = *it;
125 }
126 }
128 if ( numEntries < 1 )
129 {
130 numEntries = 5;
131 sizes = new int[numEntries];
132 sizes[0] = 16;
133 sizes[1] = 24;
134 sizes[2] = 32;
135 sizes[3] = 48;
136 sizes[4] = 128;
137 }
139 pixMem = new guchar*[numEntries];
140 images = new Gtk::Image*[numEntries];
141 labels = new Glib::ustring*[numEntries];
142 buttons = new Gtk::ToggleToolButton*[numEntries];
145 for ( int i = 0; i < numEntries; i++ ) {
146 char *label = g_strdup_printf(_("%d x %d"), sizes[i], sizes[i]);
147 labels[i] = new Glib::ustring(label);
148 g_free(label);
149 pixMem[i] = 0;
150 images[i] = 0;
151 }
154 magLabel.set_label( *labels[hot] );
156 Gtk::VBox* magBox = new Gtk::VBox();
158 Gtk::Frame *magFrame = Gtk::manage(new Gtk::Frame(_("Magnified:")));
159 magFrame->add( magnified );
161 magBox->pack_start( *magFrame, Gtk::PACK_EXPAND_WIDGET );
162 magBox->pack_start( magLabel, Gtk::PACK_SHRINK );
165 Gtk::VBox *verts = new Gtk::VBox();
166 Gtk::HBox *horiz = 0;
167 int previous = 0;
168 int avail = 0;
169 for ( int i = numEntries - 1; i >= 0; --i ) {
170 pixMem[i] = new guchar[4 * sizes[i] * sizes[i]];
171 memset( pixMem[i], 0x00, 4 * sizes[i] * sizes[i] );
173 GdkPixbuf *pb = gdk_pixbuf_new_from_data( pixMem[i], GDK_COLORSPACE_RGB, TRUE, 8, sizes[i], sizes[i], sizes[i] * 4, /*(GdkPixbufDestroyNotify)g_free*/NULL, NULL );
174 GtkImage* img = GTK_IMAGE( gtk_image_new_from_pixbuf( pb ) );
175 images[i] = Glib::wrap(img);
176 Glib::ustring label(*labels[i]);
177 buttons[i] = new Gtk::ToggleToolButton(label);
178 buttons[i]->set_active( i == hot );
179 if ( prefs->getBool("/iconpreview/showFrames", true) ) {
180 Gtk::Frame *frame = new Gtk::Frame();
181 frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
182 frame->add(*images[i]);
183 buttons[i]->set_icon_widget(*Gtk::manage(frame));
184 } else {
185 buttons[i]->set_icon_widget(*images[i]);
186 }
188 tips.set_tip((*buttons[i]), label);
190 buttons[i]->signal_clicked().connect( sigc::bind<int>( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) );
193 Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0, 0));
194 align->add(*buttons[i]);
196 int pad = 12;
197 if ( !pack || ( (avail == 0) && (previous == 0) ) ) {
198 verts->pack_end(*align, Gtk::PACK_SHRINK);
199 previous = sizes[i];
200 avail = sizes[i];
201 } else {
202 if ((avail < pad) || ((sizes[i] > avail) && (sizes[i] < previous))) {
203 horiz = 0;
204 }
205 if ((horiz == 0) && (sizes[i] <= previous)) {
206 avail = previous;
207 }
208 if (sizes[i] <= avail) {
209 if (!horiz) {
210 horiz = Gtk::manage(new Gtk::HBox());
211 avail = previous;
212 verts->pack_end(*horiz, Gtk::PACK_SHRINK);
213 }
214 horiz->pack_start(*align, Gtk::PACK_EXPAND_WIDGET);
215 avail -= sizes[i];
216 avail -= pad; // a little extra for padding
217 } else {
218 horiz = 0;
219 verts->pack_end(*align, Gtk::PACK_SHRINK);
220 }
221 }
222 }
224 iconBox.pack_start(splitter);
225 splitter.pack1( *magBox, true, true );
226 Gtk::Frame *actuals = Gtk::manage(new Gtk::Frame(_("Actual Size:")));
227 actuals->add(*verts);
228 splitter.pack2( *actuals, false, false );
231 selectionButton = new Gtk::CheckButton(C_("Icon preview window", "Sele_ction"), true);//selectionButton = (Gtk::ToggleButton*) gtk_check_button_new_with_mnemonic(_("_Selection")); // , GTK_RESPONSE_APPLY
232 magBox->pack_start( *selectionButton, Gtk::PACK_SHRINK );
233 tips.set_tip((*selectionButton), _("Selection only or whole document"));
234 selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) );
236 gint val = prefs->getBool("/iconpreview/selectionOnly");
237 selectionButton->set_active( val != 0 );
240 _getContents()->pack_start(iconBox, Gtk::PACK_SHRINK);
242 show_all_children();
244 // Connect this up last
245 desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &IconPreviewPanel::setDesktop) );
246 deskTrack.connect(GTK_WIDGET(gobj()));
247 }
249 IconPreviewPanel::~IconPreviewPanel()
250 {
251 setDesktop(0);
252 if (timer) {
253 timer->stop();
254 delete timer;
255 timer = 0;
256 }
257 if ( renderTimer ) {
258 renderTimer->stop();
259 delete renderTimer;
260 renderTimer = 0;
261 }
263 selChangedConn.disconnect();
264 docModConn.disconnect();
265 docReplacedConn.disconnect();
266 desktopChangeConn.disconnect();
267 deskTrack.disconnect();
268 }
270 //#########################################################################
271 //## M E T H O D S
272 //#########################################################################
275 #if ICON_VERBOSE
276 static Glib::ustring getTimestr()
277 {
278 Glib::ustring str;
279 GTimeVal now = {0, 0};
280 g_get_current_time(&now);
281 glong secs = now.tv_sec % 60;
282 glong mins = (now.tv_sec / 60) % 60;
283 gchar *ptr = g_strdup_printf(":%02ld:%02ld.%06ld", mins, secs, now.tv_usec);
284 str = ptr;
285 g_free(ptr);
286 ptr = 0;
287 return str;
288 }
289 #endif // ICON_VERBOSE
291 void IconPreviewPanel::setDesktop( SPDesktop* desktop )
292 {
293 Panel::setDesktop(desktop);
295 SPDocument *newDoc = (desktop) ? desktop->doc() : 0;
297 if ( desktop != this->desktop ) {
298 docReplacedConn.disconnect();
299 selChangedConn.disconnect();
301 this->desktop = Panel::getDesktop();
302 if ( this->desktop ) {
303 docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument)));
304 if ( this->desktop->selection && Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) {
305 selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
306 }
307 }
308 }
309 setDocument(newDoc);
310 deskTrack.setBase(desktop);
311 }
313 void IconPreviewPanel::setDocument( SPDocument *document )
314 {
315 if (this->document != document) {
316 docModConn.disconnect();
318 this->document = document;
319 if (this->document) {
320 if ( Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) {
321 docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
322 }
323 queueRefresh();
324 }
325 }
326 }
328 void IconPreviewPanel::refreshPreview()
329 {
330 SPDesktop *desktop = getDesktop();
331 if (!timer) {
332 timer = new Glib::Timer();
333 }
334 if (timer->elapsed() < minDelay) {
335 #if ICON_VERBOSE
336 g_message( "%s Deferring refresh as too soon. calling queueRefresh()", getTimestr().c_str() );
337 #endif //ICON_VERBOSE
338 // Do not refresh too quickly
339 queueRefresh();
340 } else if ( desktop ) {
341 #if ICON_VERBOSE
342 g_message( "%s Refreshing preview.", getTimestr().c_str() );
343 #endif // ICON_VERBOSE
344 bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", true);
345 SPObject *target = 0;
346 if ( selectionButton && selectionButton->get_active() )
347 {
348 target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0;
349 if ( !target ) {
350 targetId.clear();
351 Inkscape::Selection * sel = sp_desktop_selection(desktop);
352 if ( sel ) {
353 //g_message("found a selection to play with");
355 GSList const *items = sel->itemList();
356 while ( items && !target ) {
357 SPItem* item = SP_ITEM( items->data );
358 SPObject * obj = SP_OBJECT(item);
359 gchar const *id = obj->getId();
360 if ( id ) {
361 targetId = id;
362 target = obj;
363 }
365 items = g_slist_next(items);
366 }
367 }
368 }
369 } else {
370 target = desktop->currentRoot();
371 }
372 if ( target ) {
373 renderPreview(target);
374 }
375 #if ICON_VERBOSE
376 g_message( "%s resetting timer", getTimestr().c_str() );
377 #endif // ICON_VERBOSE
378 timer->reset();
379 }
380 }
382 bool IconPreviewPanel::refreshCB()
383 {
384 bool callAgain = true;
385 if (!timer) {
386 timer = new Glib::Timer();
387 }
388 if ( timer->elapsed() > minDelay ) {
389 #if ICON_VERBOSE
390 g_message( "%s refreshCB() timer has progressed", getTimestr().c_str() );
391 #endif // ICON_VERBOSE
392 callAgain = false;
393 refreshPreview();
394 #if ICON_VERBOSE
395 g_message( "%s refreshCB() setting pending false", getTimestr().c_str() );
396 #endif // ICON_VERBOSE
397 pending = false;
398 }
399 return callAgain;
400 }
402 void IconPreviewPanel::queueRefresh()
403 {
404 if (!pending) {
405 pending = true;
406 #if ICON_VERBOSE
407 g_message( "%s queueRefresh() Setting pending true", getTimestr().c_str() );
408 #endif // ICON_VERBOSE
409 if (!timer) {
410 timer = new Glib::Timer();
411 }
412 Glib::signal_idle().connect( sigc::mem_fun(this, &IconPreviewPanel::refreshCB), Glib::PRIORITY_DEFAULT_IDLE );
413 }
414 }
416 void IconPreviewPanel::modeToggled()
417 {
418 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
419 bool selectionOnly = (selectionButton && selectionButton->get_active());
420 prefs->setBool("/iconpreview/selectionOnly", selectionOnly);
421 if ( !selectionOnly ) {
422 targetId.clear();
423 }
425 refreshPreview();
426 }
428 void IconPreviewPanel::renderPreview( SPObject* obj )
429 {
430 SPDocument * doc = SP_OBJECT_DOCUMENT(obj);
431 gchar const * id = obj->getId();
432 if ( !renderTimer ) {
433 renderTimer = new Glib::Timer();
434 }
435 renderTimer->reset();
437 #if ICON_VERBOSE
438 g_message("%s setting up to render '%s' as the icon", getTimestr().c_str(), id );
439 #endif // ICON_VERBOSE
441 NRArenaItem *root = NULL;
443 /* Create new arena */
444 NRArena *arena = NRArena::create();
446 /* Create ArenaItem and set transform */
447 unsigned int visionkey = SPItem::display_key_new(1);
449 root = SP_ITEM( doc->getRoot() )->invoke_show( arena, visionkey, SP_ITEM_SHOW_DISPLAY );
451 for ( int i = 0; i < numEntries; i++ ) {
452 guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] );
453 // g_message( " size %d %s", sizes[i], (px ? "worked" : "failed") );
454 if ( px ) {
455 memcpy( pixMem[i], px, sizes[i] * sizes[i] * 4 );
456 g_free( px );
457 px = 0;
458 } else {
459 memset( pixMem[i], 0, sizes[i] * sizes[i] * 4 );
460 }
461 images[i]->queue_draw();
462 }
463 updateMagnify();
465 SP_ITEM(doc->getRoot())->invoke_hide(visionkey);
466 nr_object_unref((NRObject *) arena);
467 renderTimer->stop();
468 minDelay = std::max( 0.1, renderTimer->elapsed() * 3.0 );
469 #if ICON_VERBOSE
470 g_message(" render took %f seconds.", renderTimer->elapsed());
471 #endif // ICON_VERBOSE
472 }
474 void IconPreviewPanel::updateMagnify()
475 {
476 Glib::RefPtr<Gdk::Pixbuf> buf = images[hot]->get_pixbuf()->scale_simple( 128, 128, Gdk::INTERP_NEAREST );
477 magLabel.set_label( *labels[hot] );
478 magnified.set( buf );
479 magnified.queue_draw();
480 magnified.get_parent()->queue_draw();
481 }
483 } //namespace Dialogs
484 } //namespace UI
485 } //namespace Inkscape
487 /*
488 Local Variables:
489 mode:c++
490 c-file-style:"stroustrup"
491 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
492 indent-tabs-mode:nil
493 fill-column:99
494 End:
495 */
496 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :