Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / ui / dialog / icon-preview.cpp
index 088f630314fb1f5dcca0b2b8cb6d92c76168bd84..b507d9f9af4f05911483935289211c44d8764a9b 100644 (file)
@@ -5,9 +5,10 @@
  *   Jon A. Cruz
  *   Bob Jamison
  *   Other dudes from The Inkscape Organization
+ *   Abhishek Sharma
  *
  * Copyright (C) 2004 Bob Jamison
- * Copyright (C) 2005 Jon A. Cruz
+ * Copyright (C) 2005,2010 Jon A. Cruz
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
@@ -18,6 +19,7 @@
 #include <gtk/gtk.h>
 #include <glib/gmem.h>
 #include <glibmm/i18n.h>
+#include <gtkmm/alignment.h>
 #include <gtkmm/buttonbox.h>
 #include <gtkmm/stock.h>
 
@@ -40,19 +42,20 @@ sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root,
                   const gchar *name, unsigned int psize );
 }
 
+#define noICON_VERBOSE 1
+
 namespace Inkscape {
 namespace UI {
-namespace Dialogs {
+namespace Dialog {
 
 
-IconPreviewPanel&
-IconPreviewPanel::getInstance()
+IconPreviewPanel &IconPreviewPanel::getInstance()
 {
-    static IconPreviewPanel &instance = *new IconPreviewPanel();
+    IconPreviewPanel *instance = new IconPreviewPanel();
 
-    instance.refreshPreview();
+    instance->refreshPreview();
 
-    return instance;
+    return *instance;
 }
 
 //#########################################################################
@@ -81,13 +84,26 @@ void IconPreviewPanel::on_button_clicked(int which)
  */
 IconPreviewPanel::IconPreviewPanel() :
     UI::Widget::Panel("", "/dialogs/iconpreview", SP_VERB_VIEW_ICON_PREVIEW),
+    deskTrack(),
+    desktop(0),
+    document(0),
+    timer(0),
+    renderTimer(0),
+    pending(false),
+    minDelay(0.1),
+    targetId(),
     hot(1),
-    refreshButton(0),
-    selectionButton(0)
+    selectionButton(0),
+    desktopChangeConn(),
+    docReplacedConn(),
+    docModConn(),
+    selChangedConn()
 {
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     numEntries = 0;
 
+    bool pack = prefs->getBool("/iconpreview/pack", true);
+
     std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default");
     std::vector<int> rawSizes;
 
@@ -139,12 +155,18 @@ IconPreviewPanel::IconPreviewPanel() :
 
     Gtk::VBox* magBox = new Gtk::VBox();
 
-    magBox->pack_start( magnified );
+    Gtk::Frame *magFrame = Gtk::manage(new Gtk::Frame(_("Magnified:")));
+    magFrame->add( magnified );
+
+    magBox->pack_start( *magFrame, Gtk::PACK_EXPAND_WIDGET );
     magBox->pack_start( magLabel, Gtk::PACK_SHRINK );
 
 
-    Gtk::VBox * verts = new Gtk::VBox();
-    for ( int i = 0; i < numEntries; i++ ) {
+    Gtk::VBox *verts = new Gtk::VBox();
+    Gtk::HBox *horiz = 0;
+    int previous = 0;
+    int avail = 0;
+    for ( int i = numEntries - 1; i >= 0; --i ) {
         pixMem[i] = new guchar[4 * sizes[i] * sizes[i]];
         memset( pixMem[i], 0x00, 4 *  sizes[i] * sizes[i] );
 
@@ -154,44 +176,95 @@ IconPreviewPanel::IconPreviewPanel() :
         Glib::ustring label(*labels[i]);
         buttons[i] = new Gtk::ToggleToolButton(label);
         buttons[i]->set_active( i == hot );
-        buttons[i]->set_icon_widget(*images[i]);
+        if ( prefs->getBool("/iconpreview/showFrames", true) ) {
+            Gtk::Frame *frame = new Gtk::Frame();
+            frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
+            frame->add(*images[i]);
+            buttons[i]->set_icon_widget(*Gtk::manage(frame));
+        } else {
+            buttons[i]->set_icon_widget(*images[i]);
+        }
 
         tips.set_tip((*buttons[i]), label);
 
         buttons[i]->signal_clicked().connect( sigc::bind<int>( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) );
 
 
-        verts->add(*buttons[i]);
+        Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0, 0));
+        align->add(*buttons[i]);
+
+        int pad = 12;
+        if ( !pack || ( (avail == 0) && (previous == 0) ) ) {
+            verts->pack_end(*align, Gtk::PACK_SHRINK);
+            previous = sizes[i];
+            avail = sizes[i];
+        } else {
+            if ((avail < pad) || ((sizes[i] > avail) && (sizes[i] < previous))) {
+                horiz = 0;
+            }
+            if ((horiz == 0) && (sizes[i] <= previous)) {
+                avail = previous;
+            }
+            if (sizes[i] <= avail) {
+                if (!horiz) {
+                    horiz = Gtk::manage(new Gtk::HBox());
+                    avail = previous;
+                    verts->pack_end(*horiz, Gtk::PACK_SHRINK);
+                }
+                horiz->pack_start(*align, Gtk::PACK_EXPAND_WIDGET);
+                avail -= sizes[i];
+                avail -= pad; // a little extra for padding
+            } else {
+                horiz = 0;
+                verts->pack_end(*align, Gtk::PACK_SHRINK);
+            }
+        }
     }
 
     iconBox.pack_start(splitter);
     splitter.pack1( *magBox, true, true );
-    splitter.pack2( *verts, false, false );
-
-
-    //## The Refresh button
-
+    Gtk::Frame *actuals = Gtk::manage(new Gtk::Frame(_("Actual Size:")));
+    actuals->add(*verts);
+    splitter.pack2( *actuals, false, false );
 
-    Gtk::HButtonBox* holder = new Gtk::HButtonBox( Gtk::BUTTONBOX_END );
-    _getContents()->pack_end(*holder, false, false);
 
-    selectionButton = new Gtk::ToggleButton(_("Selection")); // , GTK_RESPONSE_APPLY
-    holder->pack_start( *selectionButton, false, false );
+    selectionButton = new Gtk::CheckButton(C_("Icon preview window", "Sele_ction"), true);//selectionButton = (Gtk::ToggleButton*) gtk_check_button_new_with_mnemonic(_("_Selection")); // , GTK_RESPONSE_APPLY
+    magBox->pack_start( *selectionButton, Gtk::PACK_SHRINK );
     tips.set_tip((*selectionButton), _("Selection only or whole document"));
     selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) );
 
     gint val = prefs->getBool("/iconpreview/selectionOnly");
     selectionButton->set_active( val != 0 );
 
-    refreshButton = new Gtk::Button(Gtk::Stock::REFRESH); // , GTK_RESPONSE_APPLY
-    holder->pack_end( *refreshButton, false, false );
-    tips.set_tip((*refreshButton), _("Refresh the icons"));
-    refreshButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::refreshPreview) );
 
-
-    _getContents()->pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET);
+    _getContents()->pack_start(iconBox, Gtk::PACK_SHRINK);
 
     show_all_children();
+
+    // Connect this up last
+    desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &IconPreviewPanel::setDesktop) );
+    deskTrack.connect(GTK_WIDGET(gobj()));
+}
+
+IconPreviewPanel::~IconPreviewPanel()
+{
+    setDesktop(0);
+    if (timer) {
+        timer->stop();
+        delete timer;
+        timer = 0;
+    }
+    if ( renderTimer ) {
+        renderTimer->stop();
+        delete renderTimer;
+        renderTimer = 0;
+    }
+
+    selChangedConn.disconnect();
+    docModConn.disconnect();
+    docReplacedConn.disconnect();
+    desktopChangeConn.disconnect();
+    deskTrack.disconnect();
 }
 
 //#########################################################################
@@ -199,48 +272,155 @@ IconPreviewPanel::IconPreviewPanel() :
 //#########################################################################
 
 
+#if ICON_VERBOSE
+static Glib::ustring getTimestr()
+{
+    Glib::ustring str;
+    GTimeVal now = {0, 0};
+    g_get_current_time(&now);
+    glong secs = now.tv_sec % 60;
+    glong mins = (now.tv_sec / 60) % 60;
+    gchar *ptr = g_strdup_printf(":%02ld:%02ld.%06ld", mins, secs, now.tv_usec);
+    str = ptr;
+    g_free(ptr);
+    ptr = 0;
+    return str;
+}
+#endif // ICON_VERBOSE
+
+void IconPreviewPanel::setDesktop( SPDesktop* desktop )
+{
+    Panel::setDesktop(desktop);
+
+    SPDocument *newDoc = (desktop) ? desktop->doc() : 0;
+
+    if ( desktop != this->desktop ) {
+        docReplacedConn.disconnect();
+        selChangedConn.disconnect();
+
+        this->desktop = Panel::getDesktop();
+        if ( this->desktop ) {
+            docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument)));
+            if ( this->desktop->selection && Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) {
+                selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
+            }
+        }
+    }
+    setDocument(newDoc);
+    deskTrack.setBase(desktop);
+}
+
+void IconPreviewPanel::setDocument( SPDocument *document )
+{
+    if (this->document != document) {
+        docModConn.disconnect();
+
+        this->document = document;
+        if (this->document) {
+            if ( Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) {
+                docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh)));
+            }
+            queueRefresh();
+        }
+    }
+}
+
 void IconPreviewPanel::refreshPreview()
 {
     SPDesktop *desktop = getDesktop();
-    if ( desktop ) {
-
+    if (!timer) {
+        timer = new Glib::Timer();
+    }
+    if (timer->elapsed() < minDelay) {
+#if ICON_VERBOSE
+        g_message( "%s Deferring refresh as too soon. calling queueRefresh()", getTimestr().c_str() );
+#endif //ICON_VERBOSE
+        // Do not refresh too quickly
+        queueRefresh();
+    } else if ( desktop ) {
+#if ICON_VERBOSE
+        g_message( "%s Refreshing preview.", getTimestr().c_str() );
+#endif // ICON_VERBOSE
+        bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", true);
+        SPObject *target = 0;
         if ( selectionButton && selectionButton->get_active() )
         {
-            Inkscape::Selection * sel = sp_desktop_selection(desktop);
-            if ( sel ) {
-                //g_message("found a selection to play with");
-
-                GSList const *items = sel->itemList();
-                SPObject *target = 0;
-                while ( items && !target ) {
-                    SPItem* item = SP_ITEM( items->data );
-                    SPObject * obj = SP_OBJECT(item);
-                    gchar const *id = obj->getId();
-                    if ( id ) {
-                        target = obj;
+            target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0;
+            if ( !target ) {
+                targetId.clear();
+                Inkscape::Selection * sel = sp_desktop_selection(desktop);
+                if ( sel ) {
+                    //g_message("found a selection to play with");
+
+                    GSList const *items = sel->itemList();
+                    while ( items && !target ) {
+                        SPItem* item = SP_ITEM( items->data );
+                        SPObject * obj = SP_OBJECT(item);
+                        gchar const *id = obj->getId();
+                        if ( id ) {
+                            targetId = id;
+                            target = obj;
+                        }
+
+                        items = g_slist_next(items);
                     }
-
-                    items = g_slist_next(items);
-                }
-                if ( target ) {
-                    renderPreview(target);
                 }
             }
+        } else {
+            target = desktop->currentRoot();
         }
-        else
-        {
-            SPObject *target = desktop->currentRoot();
-            if ( target ) {
-                renderPreview(target);
-            }
+        if ( target ) {
+            renderPreview(target);
         }
+#if ICON_VERBOSE
+        g_message( "%s  resetting timer", getTimestr().c_str() );
+#endif // ICON_VERBOSE
+        timer->reset();
+    }
+}
+
+bool IconPreviewPanel::refreshCB()
+{
+    bool callAgain = true;
+    if (!timer) {
+        timer = new Glib::Timer();
+    }
+    if ( timer->elapsed() > minDelay ) {
+#if ICON_VERBOSE
+        g_message( "%s refreshCB() timer has progressed", getTimestr().c_str() );
+#endif // ICON_VERBOSE
+        callAgain = false;
+        refreshPreview();
+#if ICON_VERBOSE
+        g_message( "%s refreshCB() setting pending false", getTimestr().c_str() );
+#endif // ICON_VERBOSE
+        pending = false;
+    }
+    return callAgain;
+}
+
+void IconPreviewPanel::queueRefresh()
+{
+    if (!pending) {
+        pending = true;
+#if ICON_VERBOSE
+        g_message( "%s queueRefresh() Setting pending true", getTimestr().c_str() );
+#endif // ICON_VERBOSE
+        if (!timer) {
+            timer = new Glib::Timer();
+        }
+        Glib::signal_idle().connect( sigc::mem_fun(this, &IconPreviewPanel::refreshCB), Glib::PRIORITY_DEFAULT_IDLE );
     }
 }
 
 void IconPreviewPanel::modeToggled()
 {
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-    prefs->setBool("/iconpreview/selectionOnly", (selectionButton && selectionButton->get_active()));
+    bool selectionOnly = (selectionButton && selectionButton->get_active());
+    prefs->setBool("/iconpreview/selectionOnly", selectionOnly);
+    if ( !selectionOnly ) {
+        targetId.clear();
+    }
 
     refreshPreview();
 }
@@ -249,8 +429,14 @@ void IconPreviewPanel::renderPreview( SPObject* obj )
 {
     SPDocument * doc = SP_OBJECT_DOCUMENT(obj);
     gchar const * id = obj->getId();
+    if ( !renderTimer ) {
+        renderTimer = new Glib::Timer();
+    }
+    renderTimer->reset();
 
-//    g_message(" setting up to render '%s' as the icon", id );
+#if ICON_VERBOSE
+    g_message("%s setting up to render '%s' as the icon", getTimestr().c_str(), id );
+#endif // ICON_VERBOSE
 
     NRArenaItem *root = NULL;
 
@@ -258,10 +444,9 @@ void IconPreviewPanel::renderPreview( SPObject* obj )
     NRArena *arena = NRArena::create();
 
     /* Create ArenaItem and set transform */
-    unsigned int visionkey = sp_item_display_key_new(1);
+    unsigned int visionkey = SPItem::display_key_new(1);
 
-    root = sp_item_invoke_show ( SP_ITEM( SP_DOCUMENT_ROOT(doc) ),
-                                 arena, visionkey, SP_ITEM_SHOW_DISPLAY );
+    root = SP_ITEM( doc->getRoot() )->invoke_show( arena, visionkey, SP_ITEM_SHOW_DISPLAY );
 
     for ( int i = 0; i < numEntries; i++ ) {
         guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] );
@@ -277,8 +462,13 @@ void IconPreviewPanel::renderPreview( SPObject* obj )
     }
     updateMagnify();
 
-    sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), visionkey);
+    SP_ITEM(doc->getRoot())->invoke_hide(visionkey);
     nr_object_unref((NRObject *) arena);
+    renderTimer->stop();
+    minDelay = std::max( 0.1, renderTimer->elapsed() * 3.0 );
+#if ICON_VERBOSE
+    g_message("  render took %f seconds.", renderTimer->elapsed());
+#endif // ICON_VERBOSE
 }
 
 void IconPreviewPanel::updateMagnify()
@@ -290,7 +480,6 @@ void IconPreviewPanel::updateMagnify()
     magnified.get_parent()->queue_draw();
 }
 
-
 } //namespace Dialogs
 } //namespace UI
 } //namespace Inkscape
@@ -304,4 +493,4 @@ void IconPreviewPanel::updateMagnify()
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :