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 Gtk::Frame *frame = new Gtk::Frame();
175 frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
176 frame->add(*images[i]);
177 buttons[i]->set_icon_widget(*Gtk::manage(frame));
179 tips.set_tip((*buttons[i]), label);
181 buttons[i]->signal_clicked().connect( sigc::bind<int>( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) );
184 Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0, 0));
185 align->add(*buttons[i]);
187 int pad = 12;
188 if ( !pack || ( (avail == 0) && (previous == 0) ) ) {
189 verts->pack_end(*align, Gtk::PACK_SHRINK);
190 previous = sizes[i];
191 avail = sizes[i];
192 } else {
193 if ((avail < pad) || ((sizes[i] > avail) && (sizes[i] < previous))) {
194 horiz = 0;
195 }
196 if ((horiz == 0) && (sizes[i] <= previous)) {
197 avail = previous;
198 }
199 if (sizes[i] <= avail) {
200 if (!horiz) {
201 horiz = Gtk::manage(new Gtk::HBox());
202 avail = previous;
203 verts->pack_end(*horiz, Gtk::PACK_SHRINK);
204 }
205 horiz->pack_start(*align, Gtk::PACK_EXPAND_WIDGET);
206 avail -= sizes[i];
207 avail -= pad; // a little extra for padding
208 } else {
209 horiz = 0;
210 verts->pack_end(*align, Gtk::PACK_SHRINK);
211 }
212 }
213 }
215 iconBox.pack_start(splitter);
216 splitter.pack1( *magBox, true, true );
217 Gtk::Frame *actuals = Gtk::manage(new Gtk::Frame(_("Actual Size:")));
218 actuals->add(*verts);
219 splitter.pack2( *actuals, false, false );
222 selectionButton = new Gtk::CheckButton(_("Selection")); // , GTK_RESPONSE_APPLY
223 magBox->pack_start( *selectionButton, Gtk::PACK_SHRINK );
224 tips.set_tip((*selectionButton), _("Selection only or whole document"));
225 selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) );
227 gint val = prefs->getBool("/iconpreview/selectionOnly");
228 selectionButton->set_active( val != 0 );
231 _getContents()->pack_start(iconBox, Gtk::PACK_SHRINK);
233 show_all_children();
235 // Connect this up last
236 desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &IconPreviewPanel::setDesktop) );
237 deskTrack.connect(GTK_WIDGET(gobj()));
238 }
240 IconPreviewPanel::~IconPreviewPanel()
241 {
242 setDesktop(0);
243 if (timer) {
244 timer->stop();
245 delete timer;
246 timer = 0;
247 }
249 selChangedConn.disconnect();
250 docModConn.disconnect();
251 docReplacedConn.disconnect();
252 desktopChangeConn.disconnect();
253 deskTrack.disconnect();
254 }
256 //#########################################################################
257 //## M E T H O D S
258 //#########################################################################
261 void IconPreviewPanel::setDesktop( SPDesktop* desktop )
262 {
263 Panel::setDesktop(desktop);
265 SPDocument *newDoc = (desktop) ? desktop->doc() : 0;
267 if ( desktop != this->desktop ) {
268 docReplacedConn.disconnect();
269 selChangedConn.disconnect();
271 this->desktop = Panel::getDesktop();
272 if ( this->desktop ) {
273 docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument)));
274 if (this->desktop->selection) {
275 selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
276 }
277 }
278 }
279 setDocument(newDoc);
280 deskTrack.setBase(desktop);
281 }
283 void IconPreviewPanel::setDocument( SPDocument *document )
284 {
285 if (this->document != document) {
286 docModConn.disconnect();
288 this->document = document;
289 if (this->document) {
290 docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
291 queueRefresh();
292 }
293 }
294 }
296 void IconPreviewPanel::refreshPreview()
297 {
298 SPDesktop *desktop = getDesktop();
299 if (!timer) {
300 timer = new Glib::Timer();
301 }
302 if (timer->elapsed() < 0.1) {
303 // Do not refresh too quickly
304 queueRefresh();
305 } else if ( desktop ) {
306 bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", false);
307 if ( selectionButton && selectionButton->get_active() )
308 {
309 SPObject *target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0;
310 if ( !target ) {
311 targetId.clear();
312 Inkscape::Selection * sel = sp_desktop_selection(desktop);
313 if ( sel ) {
314 //g_message("found a selection to play with");
316 GSList const *items = sel->itemList();
317 while ( items && !target ) {
318 SPItem* item = SP_ITEM( items->data );
319 SPObject * obj = SP_OBJECT(item);
320 gchar const *id = obj->getId();
321 if ( id ) {
322 targetId = id;
323 target = obj;
324 }
326 items = g_slist_next(items);
327 }
328 }
329 }
330 if ( target ) {
331 renderPreview(target);
332 }
333 } else {
334 SPObject *target = desktop->currentRoot();
335 if ( target ) {
336 renderPreview(target);
337 }
338 }
339 timer->reset();
340 }
341 }
343 bool IconPreviewPanel::refreshCB()
344 {
345 bool callAgain = true;
346 if (!timer) {
347 timer = new Glib::Timer();
348 }
349 if ( timer->elapsed() > 0.1 ) {
350 callAgain = false;
351 refreshPreview();
352 pending = false;
353 }
354 return callAgain;
355 }
357 void IconPreviewPanel::queueRefresh()
358 {
359 if (!pending) {
360 pending = true;
361 if (!timer) {
362 timer = new Glib::Timer();
363 }
364 Glib::signal_idle().connect( sigc::mem_fun(this, &IconPreviewPanel::refreshCB), Glib::PRIORITY_DEFAULT_IDLE );
365 }
366 }
368 void IconPreviewPanel::modeToggled()
369 {
370 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
371 bool selectionOnly = (selectionButton && selectionButton->get_active());
372 prefs->setBool("/iconpreview/selectionOnly", selectionOnly);
373 if ( !selectionOnly ) {
374 targetId.clear();
375 }
377 refreshPreview();
378 }
380 void IconPreviewPanel::renderPreview( SPObject* obj )
381 {
382 SPDocument * doc = SP_OBJECT_DOCUMENT(obj);
383 gchar const * id = obj->getId();
385 // g_message(" setting up to render '%s' as the icon", id );
387 NRArenaItem *root = NULL;
389 /* Create new arena */
390 NRArena *arena = NRArena::create();
392 /* Create ArenaItem and set transform */
393 unsigned int visionkey = sp_item_display_key_new(1);
395 root = sp_item_invoke_show ( SP_ITEM( SP_DOCUMENT_ROOT(doc) ),
396 arena, visionkey, SP_ITEM_SHOW_DISPLAY );
398 for ( int i = 0; i < numEntries; i++ ) {
399 guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] );
400 // g_message( " size %d %s", sizes[i], (px ? "worked" : "failed") );
401 if ( px ) {
402 memcpy( pixMem[i], px, sizes[i] * sizes[i] * 4 );
403 g_free( px );
404 px = 0;
405 } else {
406 memset( pixMem[i], 0, sizes[i] * sizes[i] * 4 );
407 }
408 images[i]->queue_draw();
409 }
410 updateMagnify();
412 sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), visionkey);
413 nr_object_unref((NRObject *) arena);
414 }
416 void IconPreviewPanel::updateMagnify()
417 {
418 Glib::RefPtr<Gdk::Pixbuf> buf = images[hot]->get_pixbuf()->scale_simple( 128, 128, Gdk::INTERP_NEAREST );
419 magLabel.set_label( *labels[hot] );
420 magnified.set( buf );
421 magnified.queue_draw();
422 magnified.get_parent()->queue_draw();
423 }
425 } //namespace Dialogs
426 } //namespace UI
427 } //namespace Inkscape
429 /*
430 Local Variables:
431 mode:c++
432 c-file-style:"stroustrup"
433 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
434 indent-tabs-mode:nil
435 fill-column:99
436 End:
437 */
438 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :