Code

Make icon preview seleciton stikcy by default.
[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 namespace Inkscape {
45 namespace UI {
46 namespace Dialog {
49 IconPreviewPanel &IconPreviewPanel::getInstance()
50 {
51     IconPreviewPanel *instance = new IconPreviewPanel();
53     instance->refreshPreview();
55     return *instance;
56 }
58 //#########################################################################
59 //## E V E N T S
60 //#########################################################################
62 void IconPreviewPanel::on_button_clicked(int which)
63 {
64     if ( hot != which ) {
65         buttons[hot]->set_active( false );
67         hot = which;
68         updateMagnify();
69         _getContents()->queue_draw();
70     }
71 }
76 //#########################################################################
77 //## C O N S T R U C T O R    /    D E S T R U C T O R
78 //#########################################################################
79 /**
80  * Constructor
81  */
82 IconPreviewPanel::IconPreviewPanel() :
83     UI::Widget::Panel("", "/dialogs/iconpreview", SP_VERB_VIEW_ICON_PREVIEW),
84     deskTrack(),
85     desktop(0),
86     document(0),
87     timer(0),
88     pending(false),
89     targetId(),
90     hot(1),
91     selectionButton(0),
92     desktopChangeConn(),
93     docReplacedConn(),
94     docModConn(),
95     selChangedConn()
96 {
97     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
98     numEntries = 0;
100     bool pack = prefs->getBool("/iconpreview/pack", true);
102     std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default");
103     std::vector<int> rawSizes;
105     for (std::vector<Glib::ustring>::iterator i = pref_sizes.begin(); i != pref_sizes.end(); ++i) {
106         if (prefs->getBool(*i + "/show", true)) {
107             int sizeVal = prefs->getInt(*i + "/value", -1);
108             if (sizeVal > 0) {
109                 rawSizes.push_back(sizeVal);
110             }
111         }
112     }
114     if ( !rawSizes.empty() ) {
115         numEntries = rawSizes.size();
116         sizes = new int[numEntries];
117         int i = 0;
118         for ( std::vector<int>::iterator it = rawSizes.begin(); it != rawSizes.end(); ++it, ++i ) {
119             sizes[i] = *it;
120         }
121     }
123     if ( numEntries < 1 )
124     {
125         numEntries = 5;
126         sizes = new int[numEntries];
127         sizes[0] = 16;
128         sizes[1] = 24;
129         sizes[2] = 32;
130         sizes[3] = 48;
131         sizes[4] = 128;
132     }
134     pixMem = new guchar*[numEntries];
135     images = new Gtk::Image*[numEntries];
136     labels = new Glib::ustring*[numEntries];
137     buttons = new Gtk::ToggleToolButton*[numEntries];
140     for ( int i = 0; i < numEntries; i++ ) {
141         char *label = g_strdup_printf(_("%d x %d"), sizes[i], sizes[i]);
142         labels[i] = new Glib::ustring(label);
143         g_free(label);
144         pixMem[i] = 0;
145         images[i] = 0;
146     }
149     magLabel.set_label( *labels[hot] );
151     Gtk::VBox* magBox = new Gtk::VBox();
153     Gtk::Frame *magFrame = Gtk::manage(new Gtk::Frame(_("Magnified:")));
154     magFrame->add( magnified );
156     magBox->pack_start( *magFrame, Gtk::PACK_EXPAND_WIDGET );
157     magBox->pack_start( magLabel, Gtk::PACK_SHRINK );
160     Gtk::VBox *verts = new Gtk::VBox();
161     Gtk::HBox *horiz = 0;
162     int previous = 0;
163     int avail = 0;
164     for ( int i = numEntries - 1; i >= 0; --i ) {
165         pixMem[i] = new guchar[4 * sizes[i] * sizes[i]];
166         memset( pixMem[i], 0x00, 4 *  sizes[i] * sizes[i] );
168         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 );
169         GtkImage* img = GTK_IMAGE( gtk_image_new_from_pixbuf( pb ) );
170         images[i] = Glib::wrap(img);
171         Glib::ustring label(*labels[i]);
172         buttons[i] = new Gtk::ToggleToolButton(label);
173         buttons[i]->set_active( i == hot );
174         if ( prefs->getBool("/iconpreview/showFrames", true) ) {
175             Gtk::Frame *frame = new Gtk::Frame();
176             frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
177             frame->add(*images[i]);
178             buttons[i]->set_icon_widget(*Gtk::manage(frame));
179         } else {
180             buttons[i]->set_icon_widget(*images[i]);
181         }
183         tips.set_tip((*buttons[i]), label);
185         buttons[i]->signal_clicked().connect( sigc::bind<int>( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) );
188         Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0, 0));
189         align->add(*buttons[i]);
191         int pad = 12;
192         if ( !pack || ( (avail == 0) && (previous == 0) ) ) {
193             verts->pack_end(*align, Gtk::PACK_SHRINK);
194             previous = sizes[i];
195             avail = sizes[i];
196         } else {
197             if ((avail < pad) || ((sizes[i] > avail) && (sizes[i] < previous))) {
198                 horiz = 0;
199             }
200             if ((horiz == 0) && (sizes[i] <= previous)) {
201                 avail = previous;
202             }
203             if (sizes[i] <= avail) {
204                 if (!horiz) {
205                     horiz = Gtk::manage(new Gtk::HBox());
206                     avail = previous;
207                     verts->pack_end(*horiz, Gtk::PACK_SHRINK);
208                 }
209                 horiz->pack_start(*align, Gtk::PACK_EXPAND_WIDGET);
210                 avail -= sizes[i];
211                 avail -= pad; // a little extra for padding
212             } else {
213                 horiz = 0;
214                 verts->pack_end(*align, Gtk::PACK_SHRINK);
215             }
216         }
217     }
219     iconBox.pack_start(splitter);
220     splitter.pack1( *magBox, true, true );
221     Gtk::Frame *actuals = Gtk::manage(new Gtk::Frame(_("Actual Size:")));
222     actuals->add(*verts);
223     splitter.pack2( *actuals, false, false );
226     selectionButton = new Gtk::CheckButton(_("Selection")); // , GTK_RESPONSE_APPLY
227     magBox->pack_start( *selectionButton, Gtk::PACK_SHRINK );
228     tips.set_tip((*selectionButton), _("Selection only or whole document"));
229     selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) );
231     gint val = prefs->getBool("/iconpreview/selectionOnly");
232     selectionButton->set_active( val != 0 );
235     _getContents()->pack_start(iconBox, Gtk::PACK_SHRINK);
237     show_all_children();
239     // Connect this up last
240     desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &IconPreviewPanel::setDesktop) );
241     deskTrack.connect(GTK_WIDGET(gobj()));
244 IconPreviewPanel::~IconPreviewPanel()
246     setDesktop(0);
247     if (timer) {
248         timer->stop();
249         delete timer;
250         timer = 0;
251     }
253     selChangedConn.disconnect();
254     docModConn.disconnect();
255     docReplacedConn.disconnect();
256     desktopChangeConn.disconnect();
257     deskTrack.disconnect();
260 //#########################################################################
261 //## M E T H O D S
262 //#########################################################################
265 void IconPreviewPanel::setDesktop( SPDesktop* desktop )
267     Panel::setDesktop(desktop);
269     SPDocument *newDoc = (desktop) ? desktop->doc() : 0;
271     if ( desktop != this->desktop ) {
272         docReplacedConn.disconnect();
273         selChangedConn.disconnect();
275         this->desktop = Panel::getDesktop();
276         if ( this->desktop ) {
277             docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument)));
278             if (this->desktop->selection) {
279                 selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
280             }
281         }
282     }
283     setDocument(newDoc);
284     deskTrack.setBase(desktop);
287 void IconPreviewPanel::setDocument( SPDocument *document )
289     if (this->document != document) {
290         docModConn.disconnect();
292         this->document = document;
293         if (this->document) {
294             docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
295             queueRefresh();
296         }
297     }
300 void IconPreviewPanel::refreshPreview()
302     SPDesktop *desktop = getDesktop();
303     if (!timer) {
304         timer = new Glib::Timer();
305     }
306     if (timer->elapsed() < 0.1) {
307         // Do not refresh too quickly
308         queueRefresh();
309     } else if ( desktop ) {
310         bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", true);
311         if ( selectionButton && selectionButton->get_active() )
312         {
313             SPObject *target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0;
314             if ( !target ) {
315                 targetId.clear();
316                 Inkscape::Selection * sel = sp_desktop_selection(desktop);
317                 if ( sel ) {
318                     //g_message("found a selection to play with");
320                     GSList const *items = sel->itemList();
321                     while ( items && !target ) {
322                         SPItem* item = SP_ITEM( items->data );
323                         SPObject * obj = SP_OBJECT(item);
324                         gchar const *id = obj->getId();
325                         if ( id ) {
326                             targetId = id;
327                             target = obj;
328                         }
330                         items = g_slist_next(items);
331                     }
332                 }
333             }
334             if ( target ) {
335                 renderPreview(target);
336             }
337         } else {
338             SPObject *target = desktop->currentRoot();
339             if ( target ) {
340                 renderPreview(target);
341             }
342         }
343         timer->reset();
344     }
347 bool IconPreviewPanel::refreshCB()
349     bool callAgain = true;
350     if (!timer) {
351         timer = new Glib::Timer();
352     }
353     if ( timer->elapsed() > 0.1 ) {
354         callAgain = false;
355         refreshPreview();
356         pending = false;
357     }
358     return callAgain;
361 void IconPreviewPanel::queueRefresh()
363     if (!pending) {
364         pending = true;
365         if (!timer) {
366             timer = new Glib::Timer();
367         }
368         Glib::signal_idle().connect( sigc::mem_fun(this, &IconPreviewPanel::refreshCB), Glib::PRIORITY_DEFAULT_IDLE );
369     }
372 void IconPreviewPanel::modeToggled()
374     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
375     bool selectionOnly = (selectionButton && selectionButton->get_active());
376     prefs->setBool("/iconpreview/selectionOnly", selectionOnly);
377     if ( !selectionOnly ) {
378         targetId.clear();
379     }
381     refreshPreview();
384 void IconPreviewPanel::renderPreview( SPObject* obj )
386     SPDocument * doc = SP_OBJECT_DOCUMENT(obj);
387     gchar const * id = obj->getId();
389 //    g_message(" setting up to render '%s' as the icon", id );
391     NRArenaItem *root = NULL;
393     /* Create new arena */
394     NRArena *arena = NRArena::create();
396     /* Create ArenaItem and set transform */
397     unsigned int visionkey = sp_item_display_key_new(1);
399     root = sp_item_invoke_show ( SP_ITEM( SP_DOCUMENT_ROOT(doc) ),
400                                  arena, visionkey, SP_ITEM_SHOW_DISPLAY );
402     for ( int i = 0; i < numEntries; i++ ) {
403         guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] );
404 //         g_message( " size %d %s", sizes[i], (px ? "worked" : "failed") );
405         if ( px ) {
406             memcpy( pixMem[i], px, sizes[i] * sizes[i] * 4 );
407             g_free( px );
408             px = 0;
409         } else {
410             memset( pixMem[i], 0, sizes[i] * sizes[i] * 4 );
411         }
412         images[i]->queue_draw();
413     }
414     updateMagnify();
416     sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), visionkey);
417     nr_object_unref((NRObject *) arena);
420 void IconPreviewPanel::updateMagnify()
422     Glib::RefPtr<Gdk::Pixbuf> buf = images[hot]->get_pixbuf()->scale_simple( 128, 128, Gdk::INTERP_NEAREST );
423     magLabel.set_label( *labels[hot] );
424     magnified.set( buf );
425     magnified.queue_draw();
426     magnified.get_parent()->queue_draw();
429 } //namespace Dialogs
430 } //namespace UI
431 } //namespace Inkscape
433 /*
434   Local Variables:
435   mode:c++
436   c-file-style:"stroustrup"
437   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
438   indent-tabs-mode:nil
439   fill-column:99
440   End:
441 */
442 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :