[prev in list] [next in list] [prev in thread] [next in thread] 

List:       koffice-devel
Subject:    kpresenter dbus slideshow control patch
From:       James Hogan <james () albanarts ! com>
Date:       2008-10-05 13:32:04
Message-ID: 200810051432.04856.james () albanarts ! com
[Download RAW message or body]

Hi,

I've attached a patch for kpresenter based on koffice 2.0 beta 1 which 
implements a basic dbus slideshow control interface (see 
http://bugs.kde.org/show_bug.cgi?id=172115).
Its my first patch submission so if i've done anything silly or contrary to 
the coding styles please do let me know.

the interface (KPrViewAdaptor) has the following functionality:
* show "edit custom slideshows" dialog, list custom slideshows, get/set active 
custom slideshow
* start, stop, navigate (prev/next step/slide, first, last) slideshows
* running slideshow information (is one running, current slide/step, num steps 
in slide, number of slides, go to slide)
* signals for the following events:
	* active custom slideshow is changed
	* custom slideshows have been modified
	* slideshow has been started/stopped
	* slideshow slide/step has changed

This has meant adding some functionality to KPrAnimationDirector, KPrDocument, 
KPrView and KPrViewModePresentation.

I'm now going to start work on making slide information available (title, 
outline, notes) and porting the kpresenter 1.6 exportPage function which 
allows a preview of a slide to be saved to a file.

All comments + suggestions are welcome.

Cheers

-- 
James Hogan

["kpresenter_dbus_1.patch" (text/x-diff)]

diff --git a/kpresenter/part/CMakeLists.txt b/kpresenter/part/CMakeLists.txt
index 4e8e384..ee93891 100644
--- a/kpresenter/part/CMakeLists.txt
+++ b/kpresenter/part/CMakeLists.txt
@@ -16,6 +16,7 @@ set( kpresenterprivate_LIB_SRCS
      KPrFactory.cpp
      KPrDocument.cpp
      KPrView.cpp
+     KPrViewAdaptor.cpp
      KPrViewModePresentation.cpp
      KPrViewModeNotes.cpp
      KPrViewModePreviewPageEffect.cpp
diff --git a/kpresenter/part/KPrAnimationDirector.cpp \
b/kpresenter/part/KPrAnimationDirector.cpp index 987c47c..eac396b 100644
--- a/kpresenter/part/KPrAnimationDirector.cpp
+++ b/kpresenter/part/KPrAnimationDirector.cpp
@@ -138,6 +138,26 @@ KoViewConverter * KPrAnimationDirector::viewConverter()
     return &m_zoomHandler;
 }
 
+int KPrAnimationDirector::numPages() const
+{
+    return m_pages.size();
+}
+
+int KPrAnimationDirector::currentPage() const
+{
+    return m_pageIndex;
+}
+
+int KPrAnimationDirector::numStepsInPage() const
+{
+    return m_steps.size();
+}
+
+int KPrAnimationDirector::currentStep() const
+{
+    return m_stepIndex;
+}
+
 bool KPrAnimationDirector::navigate( Navigation navigation )
 {
     bool finished = false;
@@ -207,6 +227,17 @@ void KPrAnimationDirector::navigateToPage( KoPAPageBase *page )
     if ( hasAnimation() ) {
         startTimeLine( m_maxShapeDuration );
     }
+
+    changedPage(m_pageIndex, numStepsInPage());
+    changedStep(m_stepIndex);
+}
+
+void KPrAnimationDirector::navigateToPageIndex( int page )
+{
+    if (page >= 0 && page < m_pages.size())
+    {
+        navigateToPage(m_pages[page]);
+    }
 }
 
 bool KPrAnimationDirector::shapeShown( KoShape * shape )
@@ -307,6 +338,9 @@ bool KPrAnimationDirector::changePage( Navigation navigation )
         startTimeLine( m_maxShapeDuration );
     }
 
+    changedPage(m_pageIndex, numStepsInPage());
+    changedStep(m_stepIndex);
+
     return false;
 }
 
@@ -378,7 +412,9 @@ bool KPrAnimationDirector::nextStep()
                 startTimeLine( m_maxShapeDuration );
             }
         }
+        changedPage(m_pageIndex, numStepsInPage());
     }
+    changedStep(m_stepIndex);
     return false;
 }
 
@@ -402,10 +438,12 @@ void KPrAnimationDirector::previousStep()
             // cancel a running page effect
             delete m_pageEffectRunner;
             m_pageEffectRunner = 0;
+            changedPage(m_pageIndex, numStepsInPage());
         }
     }
     // when going back you allway go to the end of the effect
     finishAnimations();
+    changedStep(m_stepIndex);
 }
 
 void KPrAnimationDirector::updateAnimations()
diff --git a/kpresenter/part/KPrAnimationDirector.h \
b/kpresenter/part/KPrAnimationDirector.h index 6f27dfc..45cdb8a 100644
--- a/kpresenter/part/KPrAnimationDirector.h
+++ b/kpresenter/part/KPrAnimationDirector.h
@@ -64,6 +64,26 @@ public:
     KoViewConverter * viewConverter();
 
     /**
+     * get the number of pages
+     */
+    int numPages() const;
+
+    /**
+     * get the current page number
+     */
+    int currentPage() const;
+
+    /**
+     * get the number of steps in this page
+     */
+    int numStepsInPage() const;
+
+    /**
+     * get the current step
+     */
+    int currentStep() const;
+
+    /**
      * do the next step in the presentation
      *
      * @return true if slideshow is finished and should be exited, false otherwise
@@ -76,6 +96,11 @@ public:
     void navigateToPage( KoPAPageBase * page );
 
     /**
+     * do the navigation to the page index specified
+     */
+    void navigateToPageIndex( int page );
+
+    /**
      * Check if the shape is shown
      *
      * A shape is visible when there are no animations on it or when it
@@ -91,6 +116,23 @@ public:
      */
     QPair<KPrShapeAnimation *, KPrAnimationData *> shapeAnimation( KoShape * shape \
);  
+signals:
+
+    /**
+     * indicates that the page has changed.
+     *
+     * @param page new page index within the current slideshow
+     * @param stepsInPage the number of steps in the new page
+     */
+    void changedPage( int page, int stepsInPage );
+
+    /**
+     * indicates that the step has changed.
+     *
+     * @param step new step index within the page
+     */
+    void changedStep( int step );
+
 protected:
     // set the page to be shon and update the UI
     void updateActivePage( KoPAPageBase * page );
diff --git a/kpresenter/part/KPrDocument.cpp b/kpresenter/part/KPrDocument.cpp
index 676b333..73a6c95 100644
--- a/kpresenter/part/KPrDocument.cpp
+++ b/kpresenter/part/KPrDocument.cpp
@@ -217,6 +217,7 @@ void KPrDocument::setCustomSlideShows( KPrCustomSlideShows* \
replacement )  {
     delete m_customSlideShows;
     m_customSlideShows = replacement;
+    customSlideShowsModified();
 }
 
 int KPrDocument::presentationMonitor()
@@ -256,7 +257,12 @@ QString KPrDocument::activeCustomSlideShow() const
 
 void KPrDocument::setActiveCustomSlideShow( const QString &customSlideShow )
 {
+    bool changed = (customSlideShow != m_activeCustomSlideShow);
     m_activeCustomSlideShow = customSlideShow;
+    if (changed)
+    {
+        activeCustomSlideShowChanged(customSlideShow);
+    }
 }
 
 #include "KPrDocument.moc"
diff --git a/kpresenter/part/KPrDocument.h b/kpresenter/part/KPrDocument.h
index 7f171ef..7e7ed97 100644
--- a/kpresenter/part/KPrDocument.h
+++ b/kpresenter/part/KPrDocument.h
@@ -117,6 +117,21 @@ public:
      */
     void setActiveCustomSlideShow( const QString &customSlideShow );
 
+Q_SIGNALS:
+    /**
+     * Indicates that the active custom slide show has changed.
+     * This is to allow for signalling dbus interfaces.
+     *
+     * @param customSlideShow the new active custom slide show
+     */
+    void activeCustomSlideShowChanged(const QString &customSlideShow);
+
+    /**
+     * Indicates that the custom slide shows have been changed.
+     * This is to allow for signalling dbus interfaces.
+     */
+    void customSlideShowsModified();
+
 protected:
     /// reimplemented
     virtual KoView * createViewInstance( QWidget *parent );
diff --git a/kpresenter/part/KPrView.cpp b/kpresenter/part/KPrView.cpp
index b6c01d4..d01a816 100644
--- a/kpresenter/part/KPrView.cpp
+++ b/kpresenter/part/KPrView.cpp
@@ -34,6 +34,7 @@
 #include "KPrPage.h"
 #include "KPrMasterPage.h"
 #include "KPrPageApplicationData.h"
+#include "KPrViewAdaptor.h"
 #include "KPrViewModePresentation.h"
 #include "KPrViewModeNotes.h"
 #include "commands/KPrAnimationCreateCommand.h"
@@ -58,6 +59,8 @@ KPrView::KPrView( KPrDocument *document, QWidget *parent )
 {
     initGUI();
     initActions();
+
+    m_dbus = new KPrViewAdaptor(this);
 }
 
 KPrView::~KPrView()
@@ -71,6 +74,21 @@ KoViewConverter * KPrView::viewConverter( KoPACanvas * canvas )
     return viewMode()->viewConverter( canvas );
 }
 
+KPrDocument * KPrView::kprDocument()
+{
+    return dynamic_cast<KPrDocument *>( m_doc );
+}
+
+KPrViewAdaptor * KPrView::dbusObject()
+{
+    return m_dbus;
+}
+
+KPrViewModePresentation * KPrView::presentationMode()
+{
+    return m_presentationMode;
+}
+
 void KPrView::updateActivePage(KoPAPageBase *page)
 {
     viewMode()->updateActivePage( page );
@@ -154,6 +172,14 @@ void KPrView::startPresentationFromBeginning()
     startPresentation();
 }
 
+void KPrView::stopPresentation()
+{
+    if (viewMode() == m_presentationMode)
+    {
+        m_presentationMode->activateSavedViewMode();
+    }
+}
+
 void KPrView::createAnimation()
 {
     static int animationcount = 0;
diff --git a/kpresenter/part/KPrView.h b/kpresenter/part/KPrView.h
index f7d435e..e808bd2 100644
--- a/kpresenter/part/KPrView.h
+++ b/kpresenter/part/KPrView.h
@@ -27,6 +27,7 @@
 #include <KoPAView.h>
 
 class KPrDocument;
+class KPrViewAdaptor;
 class KPrViewModeNotes;
 class KPrViewModePresentation;
 class KPrViewModePresenterView;
@@ -42,20 +43,26 @@ public:
 
     virtual KoViewConverter * viewConverter( KoPACanvas * canvas);
 
+    KPrDocument * kprDocument();
+    virtual KPrViewAdaptor * dbusObject();
+
+    KPrViewModePresentation * presentationMode();
+
 public slots:
     void updateActivePage(KoPAPageBase *page);
+    void startPresentation();
+    void startPresentationFromBeginning();
+    void stopPresentation();
+    void dialogCustomSlideShows();
 
 protected:
     void initGUI();
     void initActions();
 
 protected slots:
-    void startPresentation();
-    void startPresentationFromBeginning();
     void createAnimation();
     void showNormal();
     void showNotes();
-    void dialogCustomSlideShows();
     void configureSlideShow();
     void configurePresenterView();
 
@@ -66,6 +73,8 @@ private:
     KAction *m_actionViewModeNotes;
     KAction *m_actionCreateCustomSlideShowsDialog;
 
+    KPrViewAdaptor *m_dbus;
+
     KPrViewModePresentation *m_presentationMode;
     KoPAViewMode *m_normalMode;
     KPrViewModeNotes *m_notesMode;
diff --git a/kpresenter/part/KPrViewAdaptor.cpp b/kpresenter/part/KPrViewAdaptor.cpp
new file mode 100644
index 0000000..20b061c
--- /dev/null
+++ b/kpresenter/part/KPrViewAdaptor.cpp
@@ -0,0 +1,225 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2008 James Hogan <james@albanarts.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301  USA
+*/
+
+#include "KPrViewAdaptor.h"
+#include "KPrView.h"
+#include "KPrViewModePresentation.h"
+#include "KPrAnimationDirector.h"
+#include "KPrDocument.h"
+
+KPrViewAdaptor::KPrViewAdaptor(KPrView* view)
+: KoViewAdaptor(view)
+, m_view(view)
+{
+    setAutoRelaySignals(true);
+
+    connect(m_view->kprDocument(), SIGNAL(activeCustomSlideShowChanged(const QString \
&)), this, SIGNAL(activeCustomSlideShowChanged(const QString &))); +    \
connect(m_view->kprDocument(), SIGNAL(customSlideShowsModified()), this, \
SIGNAL(customSlideShowsModified())); +
+    // We need to know when the presentation is started and stopped
+    connect(m_view->presentationMode(), SIGNAL(activated()), this, \
SLOT(presentationActivated())); +    connect(m_view->presentationMode(), \
SIGNAL(deactivated()), this, SLOT(presentationDeactivated())); +}
+
+KPrViewAdaptor::~KPrViewAdaptor()
+{
+}
+
+// custom slideshows
+
+void KPrViewAdaptor::editCustomSlideShowsDialog()
+{
+    m_view->dialogCustomSlideShows();
+}
+
+QStringList KPrViewAdaptor::customSlideShows() const
+{
+    return m_view->kprDocument()->customSlideShows()->names();
+}
+
+QString KPrViewAdaptor::activeCustomSlideShow() const
+{
+    return m_view->kprDocument()->activeCustomSlideShow();
+}
+
+void KPrViewAdaptor::setActiveCustomSlideShow(QString name)
+{
+    // Check that the custom slideshow exists
+    if (name == "" || customSlideShows().contains(name))
+    {
+        m_view->kprDocument()->setActiveCustomSlideShow(name);
+    }
+}
+
+// screen menu
+
+void KPrViewAdaptor::screenStart()
+{
+    m_view->startPresentation();
+}
+
+void KPrViewAdaptor::screenStartFromFirst()
+{
+    m_view->startPresentationFromBeginning();
+}
+
+void KPrViewAdaptor::screenStop()
+{
+    m_view->stopPresentation();
+}
+
+void KPrViewAdaptor::screenPrev()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::PreviousStep);
 +    }
+}
+
+void KPrViewAdaptor::screenNext()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::NextStep);
 +    }
+}
+
+void KPrViewAdaptor::screenPrevSlide()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::PreviousPage);
 +    }
+}
+
+void KPrViewAdaptor::screenNextSlide()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::NextPage);
 +    }
+}
+
+void KPrViewAdaptor::screenFirst()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::FirstPage);
 +    }
+}
+
+void KPrViewAdaptor::screenLast()
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigate(KPrAnimationDirector::LastPage);
 +    }
+}
+
+// in presentation mode
+
+bool KPrViewAdaptor::isPresRunning() const
+{
+    return (m_view->presentationMode() == m_view->viewMode());
+}
+
+int KPrViewAdaptor::currentPresPage() const
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        return m_view->presentationMode()->animationDirector()->currentPage();
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+int KPrViewAdaptor::currentPresStep() const
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        return m_view->presentationMode()->animationDirector()->currentStep();
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+int KPrViewAdaptor::numStepsInPresPage() const
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        return m_view->presentationMode()->animationDirector()->numStepsInPage();
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+int KPrViewAdaptor::numPresPages() const
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        return m_view->presentationMode()->animationDirector()->numPages();
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+void KPrViewAdaptor::gotoPresPage( int pg )
+{
+    if (0 != m_view->presentationMode()->animationDirector())
+    {
+        m_view->presentationMode()->animationDirector()->navigateToPageIndex(pg);
+    }
+}
+
+/**
+ * Fired when the presentation is activated.
+ */
+void KPrViewAdaptor::presentationActivated()
+{
+    screenStarted(numPresPages());
+    changedPresPage(currentPresPage(), numStepsInPresPage());
+    changedPresStep(currentPresStep());
+
+    // Connect up the animation director signals
+    Q_ASSERT(0 != m_view->presentationMode()->animationDirector());
+    connect(m_view->presentationMode()->animationDirector(), SIGNAL(changedPage(int, \
int)), this, SIGNAL(changedPresPage(int, int))); +    \
connect(m_view->presentationMode()->animationDirector(), SIGNAL(changedStep(int)), \
this, SIGNAL(changedPresStep(int))); +}
+
+/**
+ * Fired when the presentation is about to be deactivated.
+ */
+void KPrViewAdaptor::presentationDeactivated()
+{
+    // Disconnect up the animation director signals
+    Q_ASSERT(0 != m_view->presentationMode()->animationDirector());
+    disconnect(m_view->presentationMode()->animationDirector(), \
SIGNAL(changedPage(int, int)), this, SIGNAL(changedPresPage(int, int))); +    \
disconnect(m_view->presentationMode()->animationDirector(), SIGNAL(changedStep(int)), \
this, SIGNAL(changedPresStep(int))); +
+    screenStopped();
+}
+
diff --git a/kpresenter/part/KPrViewAdaptor.h b/kpresenter/part/KPrViewAdaptor.h
new file mode 100644
index 0000000..009a022
--- /dev/null
+++ b/kpresenter/part/KPrViewAdaptor.h
@@ -0,0 +1,120 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2008 James Hogan <james@albanarts.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301  USA
+*/
+
+#ifndef KPR_VIEW_ADAPTOR_H
+#define KPR_VIEW_ADAPTOR_H
+
+#include <KoViewAdaptor.h>
+
+class KPrView;
+
+class KPrViewAdaptor : public KoViewAdaptor
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "org.kde.koffice.presentation.view")
+
+public:
+    explicit KPrViewAdaptor(KPrView* view);
+    virtual ~KPrViewAdaptor();
+
+public slots:
+
+    // custom slideshows
+    void editCustomSlideShowsDialog();
+    QStringList customSlideShows() const;
+    QString activeCustomSlideShow() const;
+    void setActiveCustomSlideShow(QString name);
+
+    // screen menu
+    void screenStart();
+    void screenStartFromFirst();
+    void screenStop();
+    void screenPrev();
+    void screenNext();
+    void screenPrevSlide();
+    void screenNextSlide();
+    void screenFirst();
+    void screenLast();
+
+    // in presentation mode
+    bool isPresRunning() const;
+    int currentPresPage() const;
+    int currentPresStep() const;
+    int numStepsInPresPage() const;
+    int numPresPages() const;
+    void gotoPresPage( int pg );
+
+signals:
+    /**
+     * Indicates that the active custom slide show has changed.
+     * This is to allow for signalling dbus interfaces.
+     *
+     * @param customSlideShow the new active custom slide show
+     */
+    void activeCustomSlideShowChanged(const QString &customSlideShow);
+
+    /**
+     * Indicates that the custom slide shows have been changed.
+     * This is to allow for signalling dbus interfaces.
+     */
+    void customSlideShowsModified();
+
+    /**
+     * Indicates that the slideshow has started
+     *
+     * @param numSlides Number of slides in the slideshow
+     */
+    void screenStarted(int numSlides);
+
+    /**
+     * Indicates that the slideshow has finished.
+     */
+    void screenStopped();
+
+    /**
+     * indicates that the page has changed
+     *
+     * @param page new page index within the current slideshow
+     * @param stepsInPage the number of steps in the new page
+     */
+    void changedPresPage( int page, int stepsInPage );
+
+    /**
+     * indicates that the step has changed
+     *
+     * @param step new step index within the page
+     */
+    void changedPresStep( int step );
+
+private slots:
+    /**
+     * Fired when the presentation is activated.
+     */
+    void presentationActivated();
+
+    /**
+     * Fired when the presentation is about to be deactivated.
+     */
+    void presentationDeactivated();
+
+private:
+    KPrView* m_view;
+};
+
+#endif
diff --git a/kpresenter/part/KPrViewModePresentation.cpp \
b/kpresenter/part/KPrViewModePresentation.cpp index adc3115..32e9465 100644
--- a/kpresenter/part/KPrViewModePresentation.cpp
+++ b/kpresenter/part/KPrViewModePresentation.cpp
@@ -180,10 +180,14 @@ void KPrViewModePresentation::activate( KoPAViewMode * \
previousViewMode )  m_tool->activate(false);
 
     m_animationDirector = new KPrAnimationDirector( m_view, m_canvas, pages, \
m_view->activePage() ); +
+    activated();
 }
 
 void KPrViewModePresentation::deactivate()
 {
+    deactivated();
+
     KoPAPageBase * page = m_view->activePage();
     if ( m_endOfSlideShowPage ) {
         if ( page == m_endOfSlideShowPage ) {
diff --git a/kpresenter/part/KPrViewModePresentation.h \
b/kpresenter/part/KPrViewModePresentation.h index 13be8c0..b01703e 100644
--- a/kpresenter/part/KPrViewModePresentation.h
+++ b/kpresenter/part/KPrViewModePresentation.h
@@ -32,6 +32,8 @@ class KPrEndOfSlideShowPage;
 
 class KPrViewModePresentation : public KoPAViewMode
 {
+    Q_OBJECT
+
 public:
     KPrViewModePresentation( KoPAView * view, KoPACanvas * m_canvas );
     ~KPrViewModePresentation();
@@ -78,6 +80,18 @@ public:
 
     void navigateToPage( KoPAPageBase * page );
 
+signals:
+
+    /**
+     * Fired when the presentation is activated.
+     */
+    void activated();
+
+    /**
+     * Fired when the presentation is about to be deactivated.
+     */
+    void deactivated();
+
 protected:
     KoPAViewMode * m_savedViewMode;
     QWidget * m_savedParent;



_______________________________________________
koffice-devel mailing list
koffice-devel@kde.org
https://mail.kde.org/mailman/listinfo/koffice-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic