Code

Merge and cleanup of GSoC C++-ification project.
[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  *   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()
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()));
249 IconPreviewPanel::~IconPreviewPanel()
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();
270 //#########################################################################
271 //## M E T H O D S
272 //#########################################################################
275 #if ICON_VERBOSE
276 static Glib::ustring getTimestr()
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;
289 #endif // ICON_VERBOSE
291 void IconPreviewPanel::setDesktop( SPDesktop* desktop )
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);
313 void IconPreviewPanel::setDocument( SPDocument *document )
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     }
328 void IconPreviewPanel::refreshPreview()
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     }
382 bool IconPreviewPanel::refreshCB()
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;
402 void IconPreviewPanel::queueRefresh()
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     }
416 void IconPreviewPanel::modeToggled()
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();
428 void IconPreviewPanel::renderPreview( SPObject* obj )
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
474 void IconPreviewPanel::updateMagnify()
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();
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 :