Code

Added debug info.
[inkscape.git] / src / ui / dialog / icon-preview.cpp
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  *
9  * Copyright (C) 2004 Bob Jamison
10  * Copyright (C) 2005,2010 Jon A. Cruz
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
18 #include <gtk/gtk.h>
19 #include <glib/gmem.h>
20 #include <glibmm/i18n.h>
21 #include <gtkmm/alignment.h>
22 #include <gtkmm/buttonbox.h>
23 #include <gtkmm/stock.h>
25 #include "desktop.h"
26 #include "desktop-handles.h"
27 #include "display/nr-arena.h"
28 #include "document.h"
29 #include "inkscape.h"
30 #include "preferences.h"
31 #include "selection.h"
32 #include "sp-root.h"
33 #include "xml/repr.h"
35 #include "icon-preview.h"
37 extern "C" {
38 // takes doc, root, icon, and icon name to produce pixels
39 guchar *
40 sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root,
41                   const gchar *name, unsigned int psize );
42 }
44 #define noICON_VERBOSE 1
46 namespace Inkscape {
47 namespace UI {
48 namespace Dialog {
51 IconPreviewPanel &IconPreviewPanel::getInstance()
52 {
53     IconPreviewPanel *instance = new IconPreviewPanel();
55     instance->refreshPreview();
57     return *instance;
58 }
60 //#########################################################################
61 //## E V E N T S
62 //#########################################################################
64 void IconPreviewPanel::on_button_clicked(int which)
65 {
66     if ( hot != which ) {
67         buttons[hot]->set_active( false );
69         hot = which;
70         updateMagnify();
71         _getContents()->queue_draw();
72     }
73 }
78 //#########################################################################
79 //## C O N S T R U C T O R    /    D E S T R U C T O R
80 //#########################################################################
81 /**
82  * Constructor
83  */
84 IconPreviewPanel::IconPreviewPanel() :
85     UI::Widget::Panel("", "/dialogs/iconpreview", SP_VERB_VIEW_ICON_PREVIEW),
86     deskTrack(),
87     desktop(0),
88     document(0),
89     timer(0),
90     renderTimer(0),
91     pending(false),
92     minDelay(0.1),
93     targetId(),
94     hot(1),
95     selectionButton(0),
96     desktopChangeConn(),
97     docReplacedConn(),
98     docModConn(),
99     selChangedConn()
101     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
102     numEntries = 0;
104     bool pack = prefs->getBool("/iconpreview/pack", true);
106     std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default");
107     std::vector<int> rawSizes;
109     for (std::vector<Glib::ustring>::iterator i = pref_sizes.begin(); i != pref_sizes.end(); ++i) {
110         if (prefs->getBool(*i + "/show", true)) {
111             int sizeVal = prefs->getInt(*i + "/value", -1);
112             if (sizeVal > 0) {
113                 rawSizes.push_back(sizeVal);
114             }
115         }
116     }
118     if ( !rawSizes.empty() ) {
119         numEntries = rawSizes.size();
120         sizes = new int[numEntries];
121         int i = 0;
122         for ( std::vector<int>::iterator it = rawSizes.begin(); it != rawSizes.end(); ++it, ++i ) {
123             sizes[i] = *it;
124         }
125     }
127     if ( numEntries < 1 )
128     {
129         numEntries = 5;
130         sizes = new int[numEntries];
131         sizes[0] = 16;
132         sizes[1] = 24;
133         sizes[2] = 32;
134         sizes[3] = 48;
135         sizes[4] = 128;
136     }
138     pixMem = new guchar*[numEntries];
139     images = new Gtk::Image*[numEntries];
140     labels = new Glib::ustring*[numEntries];
141     buttons = new Gtk::ToggleToolButton*[numEntries];
144     for ( int i = 0; i < numEntries; i++ ) {
145         char *label = g_strdup_printf(_("%d x %d"), sizes[i], sizes[i]);
146         labels[i] = new Glib::ustring(label);
147         g_free(label);
148         pixMem[i] = 0;
149         images[i] = 0;
150     }
153     magLabel.set_label( *labels[hot] );
155     Gtk::VBox* magBox = new Gtk::VBox();
157     Gtk::Frame *magFrame = Gtk::manage(new Gtk::Frame(_("Magnified:")));
158     magFrame->add( magnified );
160     magBox->pack_start( *magFrame, Gtk::PACK_EXPAND_WIDGET );
161     magBox->pack_start( magLabel, Gtk::PACK_SHRINK );
164     Gtk::VBox *verts = new Gtk::VBox();
165     Gtk::HBox *horiz = 0;
166     int previous = 0;
167     int avail = 0;
168     for ( int i = numEntries - 1; i >= 0; --i ) {
169         pixMem[i] = new guchar[4 * sizes[i] * sizes[i]];
170         memset( pixMem[i], 0x00, 4 *  sizes[i] * sizes[i] );
172         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 );
173         GtkImage* img = GTK_IMAGE( gtk_image_new_from_pixbuf( pb ) );
174         images[i] = Glib::wrap(img);
175         Glib::ustring label(*labels[i]);
176         buttons[i] = new Gtk::ToggleToolButton(label);
177         buttons[i]->set_active( i == hot );
178         if ( prefs->getBool("/iconpreview/showFrames", true) ) {
179             Gtk::Frame *frame = new Gtk::Frame();
180             frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
181             frame->add(*images[i]);
182             buttons[i]->set_icon_widget(*Gtk::manage(frame));
183         } else {
184             buttons[i]->set_icon_widget(*images[i]);
185         }
187         tips.set_tip((*buttons[i]), label);
189         buttons[i]->signal_clicked().connect( sigc::bind<int>( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) );
192         Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0, 0));
193         align->add(*buttons[i]);
195         int pad = 12;
196         if ( !pack || ( (avail == 0) && (previous == 0) ) ) {
197             verts->pack_end(*align, Gtk::PACK_SHRINK);
198             previous = sizes[i];
199             avail = sizes[i];
200         } else {
201             if ((avail < pad) || ((sizes[i] > avail) && (sizes[i] < previous))) {
202                 horiz = 0;
203             }
204             if ((horiz == 0) && (sizes[i] <= previous)) {
205                 avail = previous;
206             }
207             if (sizes[i] <= avail) {
208                 if (!horiz) {
209                     horiz = Gtk::manage(new Gtk::HBox());
210                     avail = previous;
211                     verts->pack_end(*horiz, Gtk::PACK_SHRINK);
212                 }
213                 horiz->pack_start(*align, Gtk::PACK_EXPAND_WIDGET);
214                 avail -= sizes[i];
215                 avail -= pad; // a little extra for padding
216             } else {
217                 horiz = 0;
218                 verts->pack_end(*align, Gtk::PACK_SHRINK);
219             }
220         }
221     }
223     iconBox.pack_start(splitter);
224     splitter.pack1( *magBox, true, true );
225     Gtk::Frame *actuals = Gtk::manage(new Gtk::Frame(_("Actual Size:")));
226     actuals->add(*verts);
227     splitter.pack2( *actuals, false, false );
230     selectionButton = new Gtk::CheckButton(_("Selection")); // , GTK_RESPONSE_APPLY
231     magBox->pack_start( *selectionButton, Gtk::PACK_SHRINK );
232     tips.set_tip((*selectionButton), _("Selection only or whole document"));
233     selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) );
235     gint val = prefs->getBool("/iconpreview/selectionOnly");
236     selectionButton->set_active( val != 0 );
239     _getContents()->pack_start(iconBox, Gtk::PACK_SHRINK);
241     show_all_children();
243     // Connect this up last
244     desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &IconPreviewPanel::setDesktop) );
245     deskTrack.connect(GTK_WIDGET(gobj()));
248 IconPreviewPanel::~IconPreviewPanel()
250     setDesktop(0);
251     if (timer) {
252         timer->stop();
253         delete timer;
254         timer = 0;
255     }
256     if ( renderTimer ) {
257         renderTimer->stop();
258         delete renderTimer;
259         renderTimer = 0;
260     }
262     selChangedConn.disconnect();
263     docModConn.disconnect();
264     docReplacedConn.disconnect();
265     desktopChangeConn.disconnect();
266     deskTrack.disconnect();
269 //#########################################################################
270 //## M E T H O D S
271 //#########################################################################
274 #if ICON_VERBOSE
275 static Glib::ustring getTimestr()
277     Glib::ustring str;
278     GTimeVal now = {0, 0};
279     g_get_current_time(&now);
280     glong secs = now.tv_sec % 60;
281     glong mins = (now.tv_sec / 60) % 60;
282     gchar *ptr = g_strdup_printf(":%02ld:%02ld.%06ld", mins, secs, now.tv_usec);
283     str = ptr;
284     g_free(ptr);
285     ptr = 0;
286     return str;
288 #endif // ICON_VERBOSE
290 void IconPreviewPanel::setDesktop( SPDesktop* desktop )
292     Panel::setDesktop(desktop);
294     SPDocument *newDoc = (desktop) ? desktop->doc() : 0;
296     if ( desktop != this->desktop ) {
297         docReplacedConn.disconnect();
298         selChangedConn.disconnect();
300         this->desktop = Panel::getDesktop();
301         if ( this->desktop ) {
302             docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument)));
303             if (this->desktop->selection) {
304                 selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
305             }
306         }
307     }
308     setDocument(newDoc);
309     deskTrack.setBase(desktop);
312 void IconPreviewPanel::setDocument( SPDocument *document )
314     if (this->document != document) {
315         docModConn.disconnect();
317         this->document = document;
318         if (this->document) {
319             docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
320             queueRefresh();
321         }
322     }
325 void IconPreviewPanel::refreshPreview()
327     SPDesktop *desktop = getDesktop();
328     if (!timer) {
329         timer = new Glib::Timer();
330     }
331     if (timer->elapsed() < minDelay) {
332 #if ICON_VERBOSE
333         g_message( "%s Deferring refresh as too soon. calling queueRefresh()", getTimestr().c_str() );
334 #endif //ICON_VERBOSE
335         // Do not refresh too quickly
336         queueRefresh();
337     } else if ( desktop ) {
338 #if ICON_VERBOSE
339         g_message( "%s Refreshing preview.", getTimestr().c_str() );
340 #endif // ICON_VERBOSE
341         bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", true);
342         SPObject *target = 0;
343         if ( selectionButton && selectionButton->get_active() )
344         {
345             target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0;
346             if ( !target ) {
347                 targetId.clear();
348                 Inkscape::Selection * sel = sp_desktop_selection(desktop);
349                 if ( sel ) {
350                     //g_message("found a selection to play with");
352                     GSList const *items = sel->itemList();
353                     while ( items && !target ) {
354                         SPItem* item = SP_ITEM( items->data );
355                         SPObject * obj = SP_OBJECT(item);
356                         gchar const *id = obj->getId();
357                         if ( id ) {
358                             targetId = id;
359                             target = obj;
360                         }
362                         items = g_slist_next(items);
363                     }
364                 }
365             }
366         } else {
367             target = desktop->currentRoot();
368         }
369         if ( target ) {
370             renderPreview(target);
371         }
372 #if ICON_VERBOSE
373         g_message( "%s  resetting timer", getTimestr().c_str() );
374 #endif // ICON_VERBOSE
375         timer->reset();
376     }
379 bool IconPreviewPanel::refreshCB()
381     bool callAgain = true;
382     if (!timer) {
383         timer = new Glib::Timer();
384     }
385     if ( timer->elapsed() > minDelay ) {
386 #if ICON_VERBOSE
387         g_message( "%s refreshCB() timer has progressed", getTimestr().c_str() );
388 #endif // ICON_VERBOSE
389         callAgain = false;
390         refreshPreview();
391 #if ICON_VERBOSE
392         g_message( "%s refreshCB() setting pending false", getTimestr().c_str() );
393 #endif // ICON_VERBOSE
394         pending = false;
395     }
396     return callAgain;
399 void IconPreviewPanel::queueRefresh()
401     if (!pending) {
402         pending = true;
403 #if ICON_VERBOSE
404         g_message( "%s queueRefresh() Setting pending true", getTimestr().c_str() );
405 #endif // ICON_VERBOSE
406         if (!timer) {
407             timer = new Glib::Timer();
408         }
409         Glib::signal_idle().connect( sigc::mem_fun(this, &IconPreviewPanel::refreshCB), Glib::PRIORITY_DEFAULT_IDLE );
410     }
413 void IconPreviewPanel::modeToggled()
415     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
416     bool selectionOnly = (selectionButton && selectionButton->get_active());
417     prefs->setBool("/iconpreview/selectionOnly", selectionOnly);
418     if ( !selectionOnly ) {
419         targetId.clear();
420     }
422     refreshPreview();
425 void IconPreviewPanel::renderPreview( SPObject* obj )
427     SPDocument * doc = SP_OBJECT_DOCUMENT(obj);
428     gchar const * id = obj->getId();
429     if ( !renderTimer ) {
430         renderTimer = new Glib::Timer();
431     }
432     renderTimer->reset();
434 #if ICON_VERBOSE
435     g_message("%s setting up to render '%s' as the icon", getTimestr().c_str(), id );
436 #endif // ICON_VERBOSE
438     NRArenaItem *root = NULL;
440     /* Create new arena */
441     NRArena *arena = NRArena::create();
443     /* Create ArenaItem and set transform */
444     unsigned int visionkey = sp_item_display_key_new(1);
446     root = sp_item_invoke_show ( SP_ITEM( SP_DOCUMENT_ROOT(doc) ),
447                                  arena, visionkey, SP_ITEM_SHOW_DISPLAY );
449     for ( int i = 0; i < numEntries; i++ ) {
450         guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] );
451 //         g_message( " size %d %s", sizes[i], (px ? "worked" : "failed") );
452         if ( px ) {
453             memcpy( pixMem[i], px, sizes[i] * sizes[i] * 4 );
454             g_free( px );
455             px = 0;
456         } else {
457             memset( pixMem[i], 0, sizes[i] * sizes[i] * 4 );
458         }
459         images[i]->queue_draw();
460     }
461     updateMagnify();
463     sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), visionkey);
464     nr_object_unref((NRObject *) arena);
465     renderTimer->stop();
466     minDelay = std::max( 0.1, renderTimer->elapsed() * 3.0 );
467 #if ICON_VERBOSE
468     g_message("  render took %f seconds.", renderTimer->elapsed());
469 #endif // ICON_VERBOSE
472 void IconPreviewPanel::updateMagnify()
474     Glib::RefPtr<Gdk::Pixbuf> buf = images[hot]->get_pixbuf()->scale_simple( 128, 128, Gdk::INTERP_NEAREST );
475     magLabel.set_label( *labels[hot] );
476     magnified.set( buf );
477     magnified.queue_draw();
478     magnified.get_parent()->queue_draw();
481 } //namespace Dialogs
482 } //namespace UI
483 } //namespace Inkscape
485 /*
486   Local Variables:
487   mode:c++
488   c-file-style:"stroustrup"
489   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
490   indent-tabs-mode:nil
491   fill-column:99
492   End:
493 */
494 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :