Akarsh Simha | 9 Feb 17:31
Picon
Gravatar

The GSoC question

Hi

So we have two weeks to put up ideas for GSoC up on the KDE community wiki.

I'm willing to mentor one student again. Anyone else interested in mentoring? We might have place for two students again.

Also, we need to come up with projects that neither have to deal with the the hardwired bits of Kstars code, and that at the same time, that do not add complications to KStars' codebase.

Also, I think this is the time when we really need honest feedback from previous students. I thought Harry's feedback about the tediousness of projects was very valuable.

_______________________________________________
Kstars-devel mailing list
Kstars-devel <at> kde.org
https://mail.kde.org/mailman/listinfo/kstars-devel
Jasem Mutlaq | 30 Jan 02:15

[kstars] kstars: Improving KStars FITS viewer to be utilized effectively in Ekos.

Git commit 48084935bbc6f9bcf805e9a454f15411a25f1a24 by Jasem Mutlaq.
Committed on 30/01/2012 at 02:11.
Pushed by mutlaqja into branch 'master'.

Improving KStars FITS viewer to be utilized effectively in Ekos.
The viewer now supports opening multiple FITS simultaneously, each in its own tab with its own histogram, undo/redo stacks, settings...etc. The viewer will be used for focusing and guiding in addition for the regular image capture.

CCMAIL: kstars-devel <at> kde.org

M  +1    -0    kstars/CMakeLists.txt
M  +6    -4    kstars/ekos/ekos.cpp
A  +9    -0    kstars/fitsviewer/fitscommon.h     [License: UNKNOWN]  *
M  +29   -34   kstars/fitsviewer/fitshistogram.cpp
M  +3    -3    kstars/fitsviewer/fitshistogram.h
M  +137  -142  kstars/fitsviewer/fitshistogramui.ui
M  +61   -76   kstars/fitsviewer/fitsimage.cpp
M  +20   -13   kstars/fitsviewer/fitsimage.h
A  +273  -0    kstars/fitsviewer/fitstab.cpp     [License: UNKNOWN]  *
A  +63   -0    kstars/fitsviewer/fitstab.h     [License: UNKNOWN]  *
M  +226  -196  kstars/fitsviewer/fitsviewer.cpp
M  +36   -29   kstars/fitsviewer/fitsviewer.h
M  +3    -4    kstars/indi/indistd.cpp
M  +2    -1    kstars/kstarsactions.cpp

The files marked with a * at the end have a non valid license. Please read: http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


http://commits.kde.org/kstars/48084935bbc6f9bcf805e9a454f15411a25f1a24

diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt
index 80acbf2..fe8354b 100644
--- a/kstars/CMakeLists.txt
+++ b/kstars/CMakeLists.txt
 <at>  <at>  -10,6 +10,7  <at>  <at>  if (CFITSIO_FOUND)
     fitsviewer/fitsimage.cpp
     fitsviewer/fitsviewer.cpp
     fitsviewer/fitshistogramdraw.cpp
+    fitsviewer/fitstab.cpp
 )
   set (fitsui_SRCS
     fitsviewer/fitsheaderdialog.ui
diff --git a/kstars/ekos/ekos.cpp b/kstars/ekos/ekos.cpp
index 4aa2cde..97d45c7 100644
--- a/kstars/ekos/ekos.cpp
+++ b/kstars/ekos/ekos.cpp
 <at>  <at>  -190,6 +190,9  <at>  <at>  void Ekos::connectDevices()
 
     if (filter != NULL)
         filter->Connect();
+
+    connectB->setEnabled(false);
+    disconnectB->setEnabled(true);
 }
 
 void Ekos::disconnectDevices()
 <at>  <at>  -210,6 +213,9  <at>  <at>  void Ekos::disconnectDevices()
     if (filter != NULL)
         filter->Disconnect();
 
+    connectB->setEnabled(true);
+    disconnectB->setEnabled(false);
+
 }
 
 void Ekos::cleanDevices()
 <at>  <at>  -225,10 +231,6  <at>  <at>  void Ekos::cleanDevices()
 
 void Ekos::processNewDevice(INDI_D *dp)
 {
-    // Let's wait until we get a CONNECTION property from the device
-
-    //connect(dp, SIGNAL(newProperty(INDI_P*)), this, SLOT(NewProperty(INDI_P*)));
-
     foreach (IDevice *dv, processed_devices)
     {
         if (dv->name == dp->name)
diff --git a/kstars/fitsviewer/fitscommon.h b/kstars/fitsviewer/fitscommon.h
new file mode 100644
index 0000000..341b8b0
--- /dev/null
+++ b/kstars/fitsviewer/fitscommon.h
 <at>  <at>  -0,0 +1,9  <at>  <at> 
+#ifndef FITSCOMMON_H
+#define FITSCOMMON_H
+
+enum FITSMode { FITS_NORMAL, FITS_FOCUS };
+enum FITSBar  { FITS_POSITION, FITS_VALUE, FITS_RESOLUTION, FITS_ZOOM, FITS_MESSAGE };
+enum FITSScale { FITSAuto = 0 , FITSLinear, FITSLog, FITSSqrt, FITSCustom };
+enum FITSZoom { ZOOM_FIT_WINDOW, ZOOM_KEEP_LEVEL, ZOOM_FULL };
+
+#endif // FITSCOMMON_H
diff --git a/kstars/fitsviewer/fitshistogram.cpp b/kstars/fitsviewer/fitshistogram.cpp
index be2d1dd..9d3e4c4 100644
--- a/kstars/fitsviewer/fitshistogram.cpp
+++ b/kstars/fitsviewer/fitshistogram.cpp
 <at>  <at>  -16,7 +16,7  <at>  <at> 
  ***************************************************************************/
 
 #include "fitshistogram.h"
-#include "fitsviewer.h"
+#include "fitstab.h"
 #include "fitsimage.h"
 
 #include <math.h>
 <at>  <at>  -47,9 +47,10  <at>  <at>  histogramUI::histogramUI(QDialog *parent) : QDialog(parent)
 
 FITSHistogram::FITSHistogram(QWidget *parent) : QDialog(parent)
 {
-    viewer = (FITSViewer *) parent;
     ui = new histogramUI(this);
 
+    tab = (FITSTab *) parent;
+
     //histArray = NULL;
 
     type   = 0;
 <at>  <at>  -86,12 +87,13  <at>  <at>  void FITSHistogram::updateBoxes(int lowerLimit, int upperLimit)
 
 void FITSHistogram::applyScale()
 {
+
     int swap;
 
     int min = ui->histFrame->getLowerLimit();
     int max = ui->histFrame->getUpperLimit();
 
-    viewer->image->getFITSMinMax(&fits_min, &fits_max);
+    tab->getImage()->getFITSMinMax(&fits_min, &fits_max);
 
     FITSHistogramCommand *histC;
 
 <at>  <at>  -120,21 +122,23  <at>  <at>  void FITSHistogram::applyScale()
     else if (ui->sqrtR->isChecked())
         type = 3;
 
-    histC = new FITSHistogramCommand(viewer, this, type, min, max);
+    histC = new FITSHistogramCommand(tab, this, type, min, max);
+
+    tab->getUndoStack()->push(histC);
 
-    viewer->history->push(histC);
 }
 
 void FITSHistogram::constructHistogram(int hist_width, int hist_height)
 {
 
+
     int id;
     double fits_w=0, fits_h=0;
     int binRoundSize=0, binRange=0;
-    float *buffer = viewer->image->getImageBuffer();
+    float *buffer = tab->getImage()->getImageBuffer();
 
-    viewer->image->getFITSSize(&fits_w, &fits_h);
-    viewer->image->getFITSMinMax(&fits_min, &fits_max);
+    tab->getImage()->getFITSSize(&fits_w, &fits_h);
+    tab->getImage()->getFITSMinMax(&fits_min, &fits_max);
 
     int pixel_range = (int) (fits_max - fits_min);
 
 <at>  <at>  -190,6 +194,7  <at>  <at>  void FITSHistogram::constructHistogram(int hist_width, int hist_height)
     updateBoxes(ui->histFrame->getLowerLimit(), ui->histFrame->getUpperLimit());
 
     ui->histFrame->update();
+
 }
 
 
 <at>  <at>  -201,10 +206,11  <at>  <at>  void FITSHistogram::updateIntenFreq(int x)
 
     int index = (int) ceil(x / binSize);
 
-    ui->intensityOUT->setText(QString("%1").arg( index + viewer->image->stats.min));
+    ui->intensityOUT->setText(QString("%1").arg( index + tab->getImage()->stats.min));
 
     ui->frequencyOUT->setText(QString("%1").arg(histArray[x]));
 
+
 }
 
 
 <at>  <at>  -226,12 +232,11  <at>  <at>  void FITSHistogram::updateHistogram()
 FITSHistogramCommand::FITSHistogramCommand(QWidget * parent, FITSHistogram *inHisto, int newType, int lmin, int lmax)
 {
 
-    viewer    = (FITSViewer *) parent;
+
+    tab    = (FITSTab *) parent;
     type      = newType;
     histo     = inHisto;
-    //oldImage  = new QImage();
-    // TODO apply maximum compression against this buffer
-    buffer = (float *) malloc (viewer->image->getWidth() * viewer->image->getHeight() * sizeof(float));
+    buffer = (float *) malloc (tab->getImage()->getWidth() * tab->getImage()->getHeight() * sizeof(float));
 
     min = lmin;
     max = lmax;
 <at>  <at>  -249,27 +254,18  <at>  <at>  void FITSHistogramCommand::redo()
 
     float val, bufferVal;
     double coeff;
-    FITSImage *image = viewer->image;
+    FITSImage *image = tab->getImage();
     float *image_buffer = image->getImageBuffer();
     int width  = image->getWidth();
     int height = image->getHeight();
 
-    /*
-    if (buffer == NULL)
-    {
-     // TODO how to remove this item from redo/undo history?
-     KMessageBox(0, i18n("There is no enough memory to perform this operation."));
-     return;
-    }
-    */
-
     memcpy(buffer, image_buffer, width * height * sizeof(float));
-    //*oldImage = image->displayImage->copy();
+
 
     switch (type)
     {
-    case FITSImage::FITSAuto:
-    case FITSImage::FITSLinear:
+    case FITSAuto:
+    case FITSLinear:
         for (int i=0; i < height; i++)
             for (int j=0; j < width; j++)
             {
 <at>  <at>  -281,7 +277,7  <at>  <at>  void FITSHistogramCommand::redo()
             }
         break;
 
-    case FITSImage::FITSLog:
+    case FITSLog:
         coeff = max / log(1 + max);
 
         for (int i=0; i < height; i++)
 <at>  <at>  -297,7 +293,7  <at>  <at>  void FITSHistogramCommand::redo()
             }
         break;
 
-    case FITSImage::FITSSqrt:
+    case FITSSqrt:
         coeff = max / sqrt(max);
 
         for (int i=0; i < height; i++)
 <at>  <at>  -316,23 +312,22  <at>  <at>  void FITSHistogramCommand::redo()
         break;
     }
 
-    image->rescale(FITSImage::ZOOM_KEEP_LEVEL);
+    tab->getImage()->rescale(ZOOM_KEEP_LEVEL);
 
     if (histo != NULL)
         histo->updateHistogram();
 
-    viewer->fitsChange();
+    //tab->modifyFITSState(false);
+
 }
 
 void FITSHistogramCommand::undo()
 {
-
-    FITSImage *image = viewer->image;
+    FITSImage *image = tab->getImage();
     memcpy( image->getImageBuffer(), buffer, image->getWidth() * image->getHeight() * sizeof(float));
-    image->rescale(FITSImage::ZOOM_KEEP_LEVEL);
+    image->rescale(ZOOM_KEEP_LEVEL);
     image->calculateStats();
 
-
     if (histo != NULL)
         histo->updateHistogram();
 
diff --git a/kstars/fitsviewer/fitshistogram.h b/kstars/fitsviewer/fitshistogram.h
index fa34f1e..35daf05 100644
--- a/kstars/fitsviewer/fitshistogram.h
+++ b/kstars/fitsviewer/fitshistogram.h
 <at>  <at>  -31,7 +31,7  <at>  <at> 
 
 const int INITIAL_MAXIMUM_WIDTH = 1024;
 
-class FITSViewer;
+class FITSTab;
 class QPixmap;
 
 
 <at>  <at>  -61,6 +61,7  <at>  <at>  public:
     int napply;
     double histFactor;
     double fits_min, fits_max;
+    FITSTab *tab;
 
 private:
 
 <at>  <at>  -68,7 +69,6  <at>  <at>  private:
     histogramUI *ui;
     int histogram_height, histogram_width;
     QVarLengthArray<int, INITIAL_MAXIMUM_WIDTH> histArray;
-    FITSViewer * viewer;
 
 public slots:
     void applyScale();
 <at>  <at>  -94,7 +94,7  <at>  <at>  private:
     int type;
     int min, max;
     float *buffer;
-    FITSViewer *viewer;
+    FITSTab *tab;
 };
 
 
diff --git a/kstars/fitsviewer/fitshistogramui.ui b/kstars/fitsviewer/fitshistogramui.ui
index 3d39b79..f4eb25f 100644
--- a/kstars/fitsviewer/fitshistogramui.ui
+++ b/kstars/fitsviewer/fitshistogramui.ui
 <at>  <at>  -1,7 +1,8  <at>  <at> 
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>FITSHistogramUI</class>
- <widget class="QDialog" name="FITSHistogramUI" >
-  <property name="geometry" >
+ <widget class="QDialog" name="FITSHistogramUI">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
 <at>  <at>  -9,192 +10,186  <at>  <at> 
     <height>325</height>
    </rect>
   </property>
-  <property name="sizePolicy" >
-   <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
-  <property name="minimumSize" >
+  <property name="minimumSize">
    <size>
     <width>650</width>
     <height>325</height>
    </size>
   </property>
-  <property name="maximumSize" >
+  <property name="maximumSize">
    <size>
     <width>650</width>
     <height>325</height>
    </size>
   </property>
-  <property name="windowTitle" >
+  <property name="windowTitle">
    <string>Histogram</string>
   </property>
-  <property name="sizeGripEnabled" >
+  <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
-  <layout class="QVBoxLayout" >
-   <property name="spacing" >
+  <layout class="QVBoxLayout">
+   <property name="spacing">
     <number>6</number>
    </property>
-   <property name="margin" >
+   <property name="margin">
     <number>5</number>
    </property>
    <item>
-    <layout class="QHBoxLayout" >
-     <property name="spacing" >
-      <number>8</number>
-     </property>
-     <property name="margin" >
+    <layout class="QGridLayout" name="gridLayout">
+     <property name="margin">
       <number>0</number>
      </property>
-     <item>
-      <widget class="histDrawArea" name="histFrame" >
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Preferred" hsizetype="Fixed" >
+     <item row="0" column="0">
+      <widget class="histDrawArea" name="histFrame">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
-       <property name="minimumSize" >
+       <property name="minimumSize">
         <size>
          <width>516</width>
          <height>216</height>
         </size>
        </property>
-       <property name="maximumSize" >
+       <property name="maximumSize">
         <size>
          <width>516</width>
          <height>216</height>
         </size>
        </property>
-       <property name="frameShape" >
+       <property name="frameShape">
         <enum>QFrame::Box</enum>
        </property>
-       <property name="frameShadow" >
+       <property name="frameShadow">
         <enum>QFrame::Plain</enum>
        </property>
-       <property name="lineWidth" >
+       <property name="lineWidth">
         <number>1</number>
        </property>
       </widget>
      </item>
-     <item>
-      <widget class="QGroupBox" name="FITSScaleGroup" >
-       <property name="windowModality" >
-        <enum>Qt::NonModal</enum>
-       </property>
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+     <item row="0" column="1">
+      <widget class="QGroupBox" name="FITSScaleGroup">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
-       <property name="layoutDirection" >
+       <property name="layoutDirection">
         <enum>Qt::LeftToRight</enum>
        </property>
-       <property name="autoFillBackground" >
+       <property name="autoFillBackground">
         <bool>false</bool>
        </property>
-       <property name="title" >
+       <property name="title">
         <string>FITS Scale</string>
        </property>
-       <layout class="QVBoxLayout" >
-        <property name="spacing" >
-         <number>6</number>
+       <widget class="QRadioButton" name="autoR">
+        <property name="geometry">
+         <rect>
+          <x>17</x>
+          <y>25</y>
+          <width>63</width>
+          <height>22</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Auto</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+       <widget class="QRadioButton" name="linearR">
+        <property name="geometry">
+         <rect>
+          <x>17</x>
+          <y>53</y>
+          <width>72</width>
+          <height>22</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Linear</string>
+        </property>
+       </widget>
+       <widget class="QRadioButton" name="logR">
+        <property name="geometry">
+         <rect>
+          <x>17</x>
+          <y>81</y>
+          <width>111</width>
+          <height>22</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Logarithmic</string>
+        </property>
+       </widget>
+       <widget class="QRadioButton" name="sqrtR">
+        <property name="geometry">
+         <rect>
+          <x>17</x>
+          <y>109</y>
+          <width>110</width>
+          <height>22</height>
+         </rect>
         </property>
-        <property name="margin" >
-         <number>9</number>
+        <property name="text">
+         <string>Square root</string>
         </property>
-        <item>
-         <widget class="QRadioButton" name="autoR" >
-          <property name="text" >
-           <string>Auto</string>
-          </property>
-          <property name="checked" >
-           <bool>true</bool>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QRadioButton" name="linearR" >
-          <property name="text" >
-           <string>Linear</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QRadioButton" name="logR" >
-          <property name="text" >
-           <string>Logarithmic</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QRadioButton" name="sqrtR" >
-          <property name="text" >
-           <string>Square root</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <spacer>
-          <property name="orientation" >
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeType" >
-           <enum>QSizePolicy::Preferred</enum>
-          </property>
-          <property name="sizeHint" stdset="0" >
-           <size>
-            <width>20</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-       </layout>
+       </widget>
       </widget>
      </item>
     </layout>
    </item>
    <item>
-    <layout class="QGridLayout" >
-     <property name="margin" >
+    <layout class="QGridLayout">
+     <property name="margin">
       <number>0</number>
      </property>
-     <property name="spacing" >
+     <property name="spacing">
       <number>6</number>
      </property>
-     <item row="1" column="2" >
-      <widget class="QLabel" name="maxLabel" >
-       <property name="text" >
+     <item row="1" column="2">
+      <widget class="QLabel" name="maxLabel">
+       <property name="text">
         <string>Max.</string>
        </property>
       </widget>
      </item>
-     <item row="0" column="1" >
-      <widget class="KLineEdit" native="1" name="intensityOUT" >
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+     <item row="0" column="1">
+      <widget class="KLineEdit" name="intensityOUT" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
-       <property name="readOnly" stdset="0" >
+       <property name="readOnly" stdset="0">
         <bool>true</bool>
        </property>
       </widget>
      </item>
-     <item row="0" column="4" >
+     <item row="0" column="4">
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeType" >
+       <property name="sizeType">
         <enum>QSizePolicy::Expanding</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>130</width>
          <height>20</height>
 <at>  <at>  -202,54 +197,54  <at>  <at> 
        </property>
       </spacer>
      </item>
-     <item row="0" column="3" >
-      <widget class="KLineEdit" native="1" name="minOUT" >
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+     <item row="0" column="3">
+      <widget class="KLineEdit" name="minOUT" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
       </widget>
      </item>
-     <item row="0" column="0" >
-      <widget class="QLabel" name="intensityLabel" >
-       <property name="text" >
+     <item row="0" column="0">
+      <widget class="QLabel" name="intensityLabel">
+       <property name="text">
         <string>Intensity:</string>
        </property>
       </widget>
      </item>
-     <item row="1" column="1" >
-      <widget class="KLineEdit" native="1" name="frequencyOUT" >
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+     <item row="1" column="1">
+      <widget class="KLineEdit" name="frequencyOUT" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
-       <property name="readOnly" stdset="0" >
+       <property name="readOnly" stdset="0">
         <bool>true</bool>
        </property>
       </widget>
      </item>
-     <item row="1" column="0" >
-      <widget class="QLabel" name="frequencyLabel" >
-       <property name="text" >
+     <item row="1" column="0">
+      <widget class="QLabel" name="frequencyLabel">
+       <property name="text">
         <string>Frequency:</string>
        </property>
       </widget>
      </item>
-     <item row="0" column="2" >
-      <widget class="QLabel" name="minLabel" >
-       <property name="text" >
+     <item row="0" column="2">
+      <widget class="QLabel" name="minLabel">
+       <property name="text">
         <string>Min.</string>
        </property>
       </widget>
      </item>
-     <item row="1" column="3" >
-      <widget class="KLineEdit" native="1" name="maxOUT" >
-       <property name="sizePolicy" >
-        <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+     <item row="1" column="3">
+      <widget class="KLineEdit" name="maxOUT" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
 <at>  <at>  -259,22 +254,22  <at>  <at> 
     </layout>
    </item>
    <item>
-    <layout class="QHBoxLayout" >
-     <property name="spacing" >
+    <layout class="QHBoxLayout">
+     <property name="spacing">
       <number>6</number>
      </property>
-     <property name="margin" >
+     <property name="margin">
       <number>0</number>
      </property>
      <item>
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeType" >
+       <property name="sizeType">
         <enum>QSizePolicy::Expanding</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>270</width>
          <height>20</height>
 <at>  <at>  -283,24 +278,24  <at>  <at> 
       </spacer>
      </item>
      <item>
-      <widget class="QPushButton" name="applyB" >
-       <property name="text" >
+      <widget class="QPushButton" name="applyB">
+       <property name="text">
         <string>&amp;Apply</string>
        </property>
-       <property name="autoDefault" >
+       <property name="autoDefault">
         <bool>true</bool>
        </property>
-       <property name="default" >
+       <property name="default">
         <bool>true</bool>
        </property>
       </widget>
      </item>
      <item>
-      <widget class="QPushButton" name="cancelB" >
-       <property name="text" >
+      <widget class="QPushButton" name="cancelB">
+       <property name="text">
         <string>&amp;Close</string>
        </property>
-       <property name="autoDefault" >
+       <property name="autoDefault">
         <bool>true</bool>
        </property>
       </widget>
 <at>  <at>  -309,7 +304,7  <at>  <at> 
    </item>
   </layout>
  </widget>
-  <customwidgets>
+ <customwidgets>
   <customwidget>
    <class>histDrawArea</class>
    <extends>QFrame</extends>
 <at>  <at>  -330,11 +325,11  <at>  <at> 
    <receiver>FITSHistogramUI</receiver>
    <slot>reject()</slot>
    <hints>
-    <hint type="sourcelabel" >
+    <hint type="sourcelabel">
      <x>592</x>
      <y>303</y>
     </hint>
-    <hint type="destinationlabel" >
+    <hint type="destinationlabel">
      <x>591</x>
      <y>316</y>
     </hint>
diff --git a/kstars/fitsviewer/fitsimage.cpp b/kstars/fitsviewer/fitsimage.cpp
index 840f7fd..7d2d7b6 100644
--- a/kstars/fitsviewer/fitsimage.cpp
+++ b/kstars/fitsviewer/fitsimage.cpp
 <at>  <at>  -39,7 +39,6  <at>  <at> 
 #include <KMessageBox>
 #include <KFileDialog>
 
-#include "fitsviewer.h"
 #include "ksutils.h"
 
 #define ZOOM_DEFAULT	100.0
 <at>  <at>  -71,13 +70,14  <at>  <at>  void FITSLabel::mouseMoveEvent(QMouseEvent *e)
     x = KSUtils::clamp(x, 1.0, width);
     y = KSUtils::clamp(y, 1.0, height);
 
-    image->getViewer()->statusBar()->changeItem(QString("%1 , %2").arg( (int)x ).arg( (int)y ), 0);
+    emit newStatus(QString("%1 , %2").arg( (int)x ).arg( (int)y ), FITS_POSITION);
 
     // Range is 0 to dim-1 when accessing array
     x -= 1;
     y -= 1;
 
-    image->getViewer()->statusBar()->changeItem( KGlobal::locale()->formatNumber( buffer[(int) (y * width + x)], 3 ), 1 );
+    emit newStatus(KGlobal::locale()->formatNumber( buffer[(int) (y * width + x)]), FITS_VALUE);
+
     setCursor(Qt::CrossCursor);
 
     e->accept();
 <at>  <at>  -85,8 +85,6  <at>  <at>  void FITSLabel::mouseMoveEvent(QMouseEvent *e)
 
 FITSImage::FITSImage(QWidget * parent) : QScrollArea(parent) , zoomFactor(1.2)
 {
-    viewer = (FITSViewer *) parent;
-
     image_frame = new FITSLabel(this);
     image_buffer = NULL;
     displayImage = NULL;
 <at>  <at>  -94,6 +92,8  <at>  <at>  FITSImage::FITSImage(QWidget * parent) : QScrollArea(parent) , zoomFactor(1.2)
 
     currentZoom = 0.0;
 
+    connect(image_frame, SIGNAL(newStatus(QString,FITSBar)), this, SIGNAL(newStatus(QString,FITSBar)));
+
     image_frame->setMouseTracking(true);
 
     // Default size
 <at>  <at>  -104,14 +104,13  <at>  <at>  FITSImage::~FITSImage()
 {
     int status;
 
-    if (fits_close_file(fptr, &status))
-        fits_report_error(stderr, status);
+    fits_close_file(fptr, &status);
 
     delete(image_buffer);
     delete(displayImage);
 }
 
-int FITSImage::loadFits ( const QString &filename )
+bool FITSImage::loadFITS ( const QString &filename )
 {
 
     int status=0, nulval=0, anynull=0;
 <at>  <at>  -123,18 +122,18  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
     //fitsProg.setWindowModality(Qt::WindowModal);
 
      fitsProg.setValue(30);
-     //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 
     if (fits_open_image(&fptr, filename.toAscii(), READONLY, &status))
     {
         fits_report_error(stderr, status);
         fits_get_errstatus(status, error_status);
-        KMessageBox::error(0, i18n("FITS file open error: %1", QString::fromUtf8(error_status)), i18n("FITS Open"));
-        return -1;
+        KMessageBox::error(0, i18n("Could not open file %1 (fits_get_img_param). Error %2", filename, QString::fromUtf8(error_status)), i18n("FITS Open"));
+        return false;
     }
 
+
     if (fitsProg.wasCanceled())
-	return -1;
+        return false;
 
     fitsProg.setValue(50);
     //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 <at>  <at>  -143,28 +142,26  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
     {
         fits_report_error(stderr, status);
         fits_get_errstatus(status, error_status);
-        KMessageBox::error(0, i18n("FITS file open error: %1", QString::fromUtf8(error_status)), i18n("FITS Open"));
-        return -1;
+        KMessageBox::error(0, i18n("FITS file open error (fits_get_img_param): %1", QString::fromUtf8(error_status)), i18n("FITS Open"));
+        return false;
     }
 
     if (fits_get_img_type(fptr, &data_type, &status))
     {
         fits_report_error(stderr, status);
         fits_get_errstatus(status, error_status);
-        KMessageBox::error(0, i18n("FITS file open error: %1", QString::fromUtf8(error_status)), i18n("FITS Open"));
-        return -1;
+        KMessageBox::error(0, i18n("FITS file open error (fits_get_img_type): %1", QString::fromUtf8(error_status)), i18n("FITS Open"));
+        return false;
     }
 
     if (fitsProg.wasCanceled())
-	return -1;
+        return false;
 
     fitsProg.setValue(60);
-    //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 
     stats.dim[0] = naxes[0];
     stats.dim[1] = naxes[1];
 
-    //kDebug() << "bitpix: " << stats.bitpix << " dim[0]: " << stats.dim[0] << " dim[1]: " << stats.dim[1] << " ndim: " << stats.ndim << " Image Type: " << data_type;
 
     delete (image_buffer);
     delete (displayImage);
 <at>  <at>  -177,7 +174,7  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
     {
 	// Display error message here after freeze
         kDebug() << "Not enough memory for image_buffer";
-	return -1;
+        return false;
     }
 
     displayImage = new QImage(stats.dim[0], stats.dim[1], QImage::Format_Indexed8);
 <at>  <at>  -186,18 +183,17  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
     {
 	// Display error message here after freeze
         kDebug() << "Not enough memory for display_image";
-	return -1;
+        return false;
     }
 
     if (fitsProg.wasCanceled())
     {
       delete (image_buffer);
       delete (displayImage);
-      return -1;
+      return false;
     }
 
     fitsProg.setValue(70);
-    //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 
     displayImage->setNumColors(256);
 
 <at>  <at>  -210,19 +206,19  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
 
     if (fits_read_pix(fptr, TFLOAT, fpixel, nelements, &nulval, image_buffer, &anynull, &status))
     {
+        fprintf(stderr, "fits_read_pix error\n");
         fits_report_error(stderr, status);
-        return -1;
+        return false;
     }
 
     if (fitsProg.wasCanceled())
     {
       delete (image_buffer);
       delete (displayImage);
-      return -1;
+      return false;
     }
 
     fitsProg.setValue(80);
-    //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 
    
     currentZoom   = 100;
 <at>  <at>  -231,32 +227,33  <at>  <at>  int FITSImage::loadFits ( const QString &filename )
 
     // Rescale to fits window
     if (rescale(ZOOM_FIT_WINDOW))
-        return -1;
+        return false;
 
     if (fitsProg.wasCanceled())
     {
       delete (image_buffer);
       delete (displayImage);
-      return -1;
+      return false;
     }
 
     fitsProg.setValue(90);
-    //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
     calculateStats();
 
+
     if (fitsProg.wasCanceled())
     {
       delete (image_buffer);
       delete (displayImage);
-      return -1;
+      return false;
     }
 
     fitsProg.setValue(100);
-    //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers);
 
     setAlignment(Qt::AlignCenter);
 
-    return 0;
+    emit newStatus(QString("%1%x%2").arg(currentWidth).arg(currentHeight), FITS_RESOLUTION);
+
+    return true;
 
 }
 
 <at>  <at>  -275,21 +272,21  <at>  <at>  int FITSImage::saveFITS( const QString &filename )
     if (fits_create_file(&new_fptr, filename.toAscii(), &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     /* Copy ALL contents */
     if (fits_copy_file(fptr, new_fptr, 1, 1, 1, &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     /* close current file */
     if (fits_close_file(fptr, &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     fptr = new_fptr;
 <at>  <at>  -298,7 +295,7  <at>  <at>  int FITSImage::saveFITS( const QString &filename )
     if (fits_write_pix(fptr, TFLOAT, fpixel, nelements, image_buffer, &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     /* Write keywords */
 <at>  <at>  -307,21 +304,21  <at>  <at>  int FITSImage::saveFITS( const QString &filename )
     if (fits_update_key(fptr, TDOUBLE, "DATAMIN", &(stats.min), "Minimum value", &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     // Maximum
     if (fits_update_key(fptr, TDOUBLE, "DATAMAX", &(stats.max), "Maximum value", &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     // ISO Date
     if (fits_write_date(fptr, &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
     QString history = QString("Modified by KStars on %1").arg(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"));
 <at>  <at>  -329,10 +326,10  <at>  <at>  int FITSImage::saveFITS( const QString &filename )
     if (fits_write_history(fptr, history.toAscii(), &status))
     {
         fits_report_error(stderr, status);
-        return -1;
+        return status;
     }
 
-    return 0;
+    return status;
 }
 
 int FITSImage::calculateMinMax(bool refresh)
 <at>  <at>  -344,14 +341,10  <at>  <at>  int FITSImage::calculateMinMax(bool refresh)
 
     if (!refresh)
     {
-        if (fits_read_key_dbl(fptr, "DATAMIN", &(stats.min), NULL, &status))
-            fits_report_error(stderr, status);
-        else
+        if (fits_read_key_dbl(fptr, "DATAMIN", &(stats.min), NULL, &status) ==0)
             nfound++;
 
-        if (fits_read_key_dbl(fptr, "DATAMAX", &(stats.max), NULL, &status))
-            fits_report_error(stderr, status);
-        else
+        if (fits_read_key_dbl(fptr, "DATAMAX", &(stats.max), NULL, &status) == 0)
             nfound++;
 
         // If we found both keywords, no need to calculate them
 <at>  <at>  -373,11 +366,10  <at>  <at>  int FITSImage::calculateMinMax(bool refresh)
     return 0;
 }
 
-int FITSImage::rescale(zoomType type)
+int FITSImage::rescale(FITSZoom type)
 {
     float val=0;
     double bscale, bzero;
-    QAction *toolAction = NULL;
 
     // Get Min Max failed, scaling is not possible
     if (type == ZOOM_KEEP_LEVEL)
 <at>  <at>  -433,12 +425,9  <at>  <at>  int FITSImage::rescale(zoomType type)
             currentWidth  = stats.dim[0] * (currentZoom / ZOOM_DEFAULT);
             currentHeight = stats.dim[1] * (currentZoom / ZOOM_DEFAULT);
 
+
             if (currentZoom <= ZOOM_MIN)
-            {
-                toolAction = viewer->actionCollection()->action("view_zoom_out");
-                if (toolAction != NULL)
-                    toolAction->setEnabled (false);
-            }
+                emit actionUpdated("view_zoom_out", false);
 
             image_frame->resize( (int) currentWidth, (int) currentHeight);
 
 <at>  <at>  -471,31 +460,26  <at>  <at>  int FITSImage::rescale(zoomType type)
 
     setWidget(image_frame);
 
-    if (type != ZOOM_KEEP_LEVEL)
-        viewer->statusBar()->changeItem(QString("%1%").arg(currentZoom), 3);
+
+   if (type != ZOOM_KEEP_LEVEL)
+       emit newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
 
     return 0;
 
 }
 
-void FITSImage::fitsZoomIn()
+void FITSImage::ZoomIn()
 {
-    QAction *toolAction = NULL;
 
     if (currentZoom < ZOOM_DEFAULT)
         currentZoom += ZOOM_LOW_INCR;
     else
         currentZoom += ZOOM_HIGH_INCR;
 
-    toolAction = viewer->actionCollection()->action("view_zoom_out");
-    if (toolAction != NULL)
-        toolAction->setEnabled (true);
+
+    emit actionUpdated("view_zoom_out", true);
     if (currentZoom >= ZOOM_MAX)
-    {
-        toolAction = viewer->actionCollection()->action("view_zoom_in");
-        if (toolAction != NULL)
-            toolAction->setEnabled (false);
-    }
+        emit actionUpdated("view_zoom_in", false);
 
     currentWidth  = stats.dim[0] * (currentZoom / ZOOM_DEFAULT);
     currentHeight = stats.dim[1] * (currentZoom / ZOOM_DEFAULT);
 <at>  <at>  -503,22 +487,22  <at>  <at>  void FITSImage::fitsZoomIn()
     image_frame->setPixmap(QPixmap::fromImage(displayImage->scaled( (int) currentWidth, (int) currentHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
     image_frame->resize( (int) currentWidth, (int) currentHeight);
 
-    viewer->statusBar()->changeItem(QString("%1%").arg(currentZoom), 3);
+    newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
 
 }
 
-void FITSImage::fitsZoomOut()
+void FITSImage::ZoomOut()
 {
-    //currentZoom--;
+
     if (currentZoom <= ZOOM_DEFAULT)
         currentZoom -= ZOOM_LOW_INCR;
     else
         currentZoom -= ZOOM_HIGH_INCR;
 
     if (currentZoom <= ZOOM_MIN)
-        viewer->actionCollection()->action("view_zoom_out")->setEnabled (false);
+        emit actionUpdated("view_zoom_out", false);
 
-    viewer->actionCollection()->action("view_zoom_in")->setEnabled (true);
+    emit actionUpdated("view_zoom_in", true);
 
     currentWidth  = stats.dim[0] * (currentZoom / ZOOM_DEFAULT);
     currentHeight = stats.dim[1] * (currentZoom / ZOOM_DEFAULT);
 <at>  <at>  -527,13 +511,14  <at>  <at>  void FITSImage::fitsZoomOut()
 
     image_frame->resize( (int) currentWidth, (int) currentHeight);
 
-    viewer->statusBar()->changeItem(QString("%1%").arg(currentZoom), 3);
+    newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
 }
 
-void FITSImage::fitsZoomDefault()
+void FITSImage::ZoomDefault()
 {
-    viewer->actionCollection()->action("view_zoom_out")->setEnabled (true);
-    viewer->actionCollection()->action("view_zoom_in")->setEnabled (true);
+    emit actionUpdated("view_zoom_out", true);
+    emit actionUpdated("view_zoom_in", true);
+
 
     currentZoom   = ZOOM_DEFAULT;
     currentWidth  = stats.dim[0];
 <at>  <at>  -542,7 +527,7  <at>  <at>  void FITSImage::fitsZoomDefault()
     image_frame->setPixmap(QPixmap::fromImage(*displayImage));
     image_frame->resize( (int) currentWidth, (int) currentHeight);
 
-    viewer->statusBar()->changeItem(QString("%1%").arg(currentZoom), 3);
+    newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
 
     update();
 
diff --git a/kstars/fitsviewer/fitsimage.h b/kstars/fitsviewer/fitsimage.h
index 0b9bc77..c0e4ea8 100644
--- a/kstars/fitsviewer/fitsimage.h
+++ b/kstars/fitsviewer/fitsimage.h
 <at>  <at>  -39,12 +39,16  <at>  <at> 
 
 #include <fitsio.h>
 
-class FITSViewer;
-class FITSImage;
+#include "fitscommon.h"
+
+#define INITIAL_W	640
+#define INITIAL_H	480
 
+class FITSImage;
 
 class FITSLabel : public QLabel
 {
+     Q_OBJECT
 public:
     explicit FITSLabel(FITSImage *img, QWidget *parent=NULL);
     virtual ~FITSLabel();
 <at>  <at>  -54,6 +58,9  <at>  <at>  protected:
 
 private:
     FITSImage *image;
+
+signals:
+    void newStatus(const QString &msg, FITSBar id);
 };
 
 class FITSImage : public QScrollArea
 <at>  <at>  -63,21 +70,17  <at>  <at>  public:
     FITSImage(QWidget *parent = 0);
     ~FITSImage();
 
-    enum scaleType { FITSAuto = 0 , FITSLinear, FITSLog, FITSSqrt, FITSCustom };
-    enum zoomType { ZOOM_FIT_WINDOW, ZOOM_KEEP_LEVEL, ZOOM_FULL };
-
     /* Loads FITS image, scales it, and displays it in the GUI */
-    int  loadFits(const QString &filename);
+    bool  loadFITS(const QString &filename);
     /* Save FITS */
     int saveFITS(const QString &filename);
     /* Rescale image lineary from image_buffer, fit to window if desired */
-    int rescale(zoomType type);
+    int rescale(FITSZoom type);
     /* Calculate stats */
     void calculateStats();
 
-
     // Access functions
-    FITSViewer * getViewer() { return viewer; }
+    //FITSViewer * getViewer() { return viewer; }
     double getCurrentZoom() { return currentZoom; }
     float * getImageBuffer() { return image_buffer; }
     void getFITSSize(double *w, double *h) { *w = stats.dim[0]; *h = stats.dim[1]; }
 <at>  <at>  -105,9 +108,9  <at>  <at>  public:
     } stats;
 
 public slots:
-    void fitsZoomIn();
-    void fitsZoomOut();
-    void fitsZoomDefault();
+    void ZoomIn();
+    void ZoomOut();
+    void ZoomDefault();
 
 private:
 
 <at>  <at>  -116,7 +119,7  <at>  <at>  private:
 
     int calculateMinMax(bool refresh=false);
 
-    FITSViewer *viewer;                 /* parent FITSViewer */
+    //FITSViewer *viewer;                 /* parent FITSViewer */
     FITSLabel *image_frame;
     float *image_buffer;				/* scaled image buffer (0-255) range */
 
 <at>  <at>  -126,6 +129,10  <at>  <at>  private:
     fitsfile* fptr;
     int data_type;                     /* FITS data type when opened */
     QImage  *displayImage;             /* FITS image that is displayed in the GUI */
+
+signals:
+    void newStatus(const QString &msg, FITSBar id);
+    void actionUpdated(const QString &name, bool enable);
 };
 
 #endif
diff --git a/kstars/fitsviewer/fitstab.cpp b/kstars/fitsviewer/fitstab.cpp
new file mode 100644
index 0000000..43b151f
--- /dev/null
+++ b/kstars/fitsviewer/fitstab.cpp
 <at>  <at>  -0,0 +1,273  <at>  <at> 
+#include <QClipboard>
+
+#include <KUndoStack>
+#include <KLocale>
+#include <KMessageBox>
+#include <KFileDialog>
+
+#include "Options.h"
+#include "fitstab.h"
+#include "fitsimage.h"
+#include "fitshistogram.h"
+
+#include "ui_statform.h"
+#include "ui_fitsheaderdialog.h"
+
+
+FITSTab::FITSTab() : QWidget()
+{
+
+    image      = NULL;
+    histogram  = NULL;
+
+    mDirty     = false;
+    undoStack = new KUndoStack();
+    undoStack->setUndoLimit(10);
+    undoStack->clear();
+    connect(undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(modifyFITSState(bool)));
+
+}
+
+void FITSTab::saveUnsaved()
+{
+
+    if( undoStack->isClean() )
+        return;
+
+    QString caption = i18n( "Save Changes to FITS?" );
+    QString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
+    int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStandardGuiItem::save(), KStandardGuiItem::discard() );
+    if( ans == KMessageBox::Yes )
+        saveFile();
+    if( ans == KMessageBox::No )
+    {
+        undoStack->clear();
+        modifyFITSState();
+    }
+}
+
+
+void FITSTab::closeEvent(QCloseEvent *ev)
+{
+    saveUnsaved();
+    if( undoStack->isClean() )
+        ev->accept();
+    else
+        ev->ignore();
+
+}
+
+bool FITSTab::loadFITS(const KUrl *imageURL)
+{
+    if (image == NULL)
+    {
+        image = new FITSImage(this);
+        image->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        QVBoxLayout *vlayout = new QVBoxLayout();
+
+        vlayout->addWidget(image);
+
+        setLayout(vlayout);
+        connect(image, SIGNAL(newStatus(QString,FITSBar)), this, SIGNAL(newStatus(QString,FITSBar)));
+    }
+
+    currentURL = *imageURL;
+
+    if (histogram != NULL)
+    {
+        histogram->close();
+        delete histogram;
+        histogram = NULL;
+    }
+
+    return image->loadFITS(imageURL->url());
+}
+
+void FITSTab::modifyFITSState(bool clean)
+{
+    if (clean)
+    {
+        if (undoStack->isClean() == false)
+            undoStack->setClean();
+
+        mDirty = false;
+    }
+    else
+        mDirty = true;
+
+    emit changeStatus(clean);
+}
+
+int FITSTab::saveFITS(const QString &filename)
+{
+    return image->saveFITS(filename);
+}
+
+void FITSTab::copyFITS()
+{
+    QApplication::clipboard()->setImage( *(image->getDisplayImage()) );
+}
+
+void FITSTab::histoFITS()
+{
+    if (histogram == NULL)
+        histogram = new FITSHistogram(this);
+
+    histogram->show();
+}
+
+
+void FITSTab::statFITS()
+{
+    QDialog statDialog;
+    Ui::statForm stat;
+    stat.setupUi(&statDialog);
+
+    stat.widthOUT->setText(QString::number(image->stats.dim[0]));
+    stat.heightOUT->setText(QString::number(image->stats.dim[1]));
+    stat.bitpixOUT->setText(QString::number(image->stats.bitpix));
+    stat.maxOUT->setText(QString::number(image->stats.max));
+    stat.minOUT->setText(QString::number(image->stats.min));
+    stat.meanOUT->setText(QString::number(image->stats.average));
+    stat.stddevOUT->setText(QString::number(image->stats.stddev));
+
+    statDialog.exec();
+}
+
+void FITSTab::headerFITS()
+{
+    QString recordList;
+    int nkeys;
+    int err_status;
+    char err_text[FLEN_STATUS];
+
+    if ( (err_status = image->getFITSRecord(recordList, nkeys)) < 0)
+    {
+        fits_get_errstatus(err_status, err_text);
+        KMessageBox::error(0, i18n("FITS record error: %1", QString::fromUtf8(err_text)), i18n("FITS Header"));
+        return;
+    }
+
+    //FIXME: possible crash! Must use QPointer<...>!
+    QDialog fitsHeaderDialog;
+    Ui::fitsHeaderDialog header;
+    header.setupUi(&fitsHeaderDialog);
+    header.tableWidget->setRowCount(nkeys);
+    for(int i = 0; i < nkeys; i++)
+    {
+        QString record = recordList.mid(i*80, 80);
+        // I love regexp!
+        QStringList properties = record.split(QRegExp("[=/]"));
+
+        QTableWidgetItem* tempItem = new QTableWidgetItem(properties[0].simplified());
+        tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+        header.tableWidget->setItem(i, 0, tempItem);
+
+        if (properties.size() > 1)
+        {
+            tempItem = new QTableWidgetItem(properties[1].simplified());
+            tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+            header.tableWidget->setItem(i, 1, tempItem);
+        }
+
+        if (properties.size() > 2)
+        {
+            tempItem = new QTableWidgetItem(properties[2].simplified());
+            tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+            header.tableWidget->setItem(i, 2, tempItem);
+        }
+
+    }
+
+    header.tableWidget->resizeColumnsToContents();
+    fitsHeaderDialog.exec();
+
+}
+
+
+void FITSTab::saveFile()
+{
+
+    int err_status;
+    char err_text[FLEN_STATUS];
+
+    KUrl backupCurrent = currentURL;
+    QString currentDir = Options::fitsDir();
+
+    // If no changes made, return.
+    if( mDirty == false && !currentURL.isEmpty())
+        return;
+
+    if (currentURL.isEmpty())
+    {
+        currentURL = KFileDialog::getSaveUrl( currentDir, "*.fits |Flexible Image Transport System");
+        // if user presses cancel
+        if (currentURL.isEmpty())
+        {
+            currentURL = backupCurrent;
+            return;
+        }
+
+        if (currentURL.path().contains('.') == 0)
+            currentURL.setPath(currentURL.path() + ".fits");
+
+        if (QFile::exists(currentURL.path()))
+        {
+            int r = KMessageBox::warningContinueCancel(0,
+                        i18n( "A file named \"%1\" already exists. "
+                              "Overwrite it?", currentURL.fileName() ),
+                        i18n( "Overwrite File?" ),
+                        KGuiItem(i18n( "&Overwrite" )) );
+            if(r==KMessageBox::Cancel) return;
+        }
+    }
+
+    if ( currentURL.isValid() )
+    {
+        if ( (err_status = saveFITS('!' + currentURL.path())) < 0)
+        {
+            fits_get_errstatus(err_status, err_text);
+            // Use KMessageBox or something here
+            KMessageBox::error(0, i18n("FITS file save error: %1",
+                                       QString::fromUtf8(err_text)), i18n("FITS Save"));
+            return;
+        }
+
+        //statusBar()->changeItem(i18n("File saved."), 3);
+
+        emit newStatus(i18n("File saved."), FITS_MESSAGE);
+        modifyFITSState();
+    } else
+    {
+        QString message = i18n( "Invalid URL: %1", currentURL.url() );
+        KMessageBox::sorry( 0, message, i18n( "Invalid URL" ) );
+    }
+}
+
+void FITSTab::saveFileAs()
+{
+    currentURL.clear();
+    saveFile();
+}
+
+void FITSTab::ZoomIn()
+{
+   image->ZoomIn();
+}
+
+void FITSTab::ZoomOut()
+{
+  image->ZoomOut();
+}
+
+void FITSTab::ZoomDefault()
+{
+  image->ZoomDefault();
+}
+
+void FITSTab::tabPositionUpdated()
+{
+    undoStack->setActive(true);
+    emit newStatus(QString("%1%").arg(image->getCurrentZoom()), FITS_ZOOM);
+    emit newStatus(QString("%1x%2").arg(image->getWidth()).arg(image->getHeight()), FITS_RESOLUTION);
+}
diff --git a/kstars/fitsviewer/fitstab.h b/kstars/fitsviewer/fitstab.h
new file mode 100644
index 0000000..9ae4ef4
--- /dev/null
+++ b/kstars/fitsviewer/fitstab.h
 <at>  <at>  -0,0 +1,63  <at>  <at> 
+#ifndef FITSTAB_H
+#define FITSTAB_H
+
+#include <QWidget>
+#include <KUrl>
+
+#include "fitscommon.h"
+
+class QUndoStack;
+class FITSImage;
+class FITSHistogram;
+class FITSHistogramCommand;
+
+class FITSTab : public QWidget
+{
+    Q_OBJECT
+public:
+
+   FITSTab();
+   bool loadFITS(const KUrl *imageURL);
+   int saveFITS(const QString &filename);
+   QUndoStack *getUndoStack() { return undoStack; }
+   KUrl * getCurrentURL() { return &currentURL; }
+   FITSImage *getImage() { return image; }
+   void saveFile();
+   void saveFileAs();
+   void copyFITS();
+   void headerFITS();
+   void histoFITS();
+   void statFITS();
+
+   void saveUnsaved();
+   void tabPositionUpdated();
+
+
+public slots:
+       void modifyFITSState(bool clean=true);
+       void ZoomIn();
+       void ZoomOut();
+       void ZoomDefault();
+
+protected:
+   virtual void closeEvent(QCloseEvent *ev);
+
+private:
+    /** Ask user whether he wants to save changes and save if he do. */
+
+
+    FITSImage *image;           /* FITS image object */
+    FITSHistogram *histogram;   /* FITS Histogram */
+
+    QUndoStack *undoStack;        /* History for undo/redo */
+    KUrl currentURL;            /* FITS File name and path */
+
+    bool mDirty;
+
+signals:
+    void newStatus(const QString &msg, FITSBar id);
+    void changeStatus(bool clean);
+
+};
+
+#endif // FITSTAB_H
diff --git a/kstars/fitsviewer/fitsviewer.cpp b/kstars/fitsviewer/fitsviewer.cpp
index 8869336..70c98e8 100644
--- a/kstars/fitsviewer/fitsviewer.cpp
+++ b/kstars/fitsviewer/fitsviewer.cpp
 <at>  <at>  -33,7 +33,10  <at>  <at> 
 #include <kstatusbar.h>
 #include <klineedit.h>
 #include <kicon.h>
+
 #include <KUndoStack>
+#include <KTabWidget>
+#include <KAction>
 
 #include <QFile>
 #include <QCursor>
 <at>  <at>  -46,8 +49,8  <at>  <at> 
 #include <QTreeWidget>
 #include <QHeaderView>
 #include <QApplication>
-
-
+#include <QUndoStack>
+#include <QUndoGroup>
 
 #include <math.h>
 #ifndef __MINGW32__
 <at>  <at>  -60,87 +63,85  <at>  <at> 
   #include <netinet/in.h>
 #endif
 
+#include "fitstab.h"
 #include "fitsimage.h"
 #include "fitshistogram.h"
-#include "ui_statform.h"
-#include "ui_fitsheaderdialog.h"
 #include "ksutils.h"
 #include "Options.h"
 
-FITSViewer::FITSViewer (const KUrl *url, QWidget *parent)
+FITSViewer::FITSViewer (QWidget *parent)
         : KXmlGuiWindow (parent)
 {
-    image      = NULL;
-    currentURL = *url;
-    histogram  = NULL;
-    m_Dirty    = false;
-
-    history = new KUndoStack();
-    history->setUndoLimit(10);
-    history->clear();
-    connect(history, SIGNAL(cleanChanged(bool)), this, SLOT(fitsRestore(bool)));
-
-    history->createUndoAction(actionCollection());
-    history->createRedoAction(actionCollection());
-
-    /* Setup image widget */
-    image = new FITSImage(this);
-    setCentralWidget(image);
-
-    statusBar()->insertItem(QString(), 0);
-    statusBar()->setItemFixed(0, 100);
-    statusBar()->insertItem(QString(), 1);
-    statusBar()->setItemFixed(1, 100);
-    statusBar()->insertItem(QString(), 2);
-    statusBar()->setItemFixed(2, 100);
-    statusBar()->insertItem(QString(), 3);
-    statusBar()->setItemFixed(3, 50);
-    statusBar()->insertPermanentItem(i18n("Welcome to KStars FITS Viewer"), 4, 1);
-    statusBar()->setItemAlignment(4 , Qt::AlignLeft);
-
-    /* FITS initializations */
-    if (!initFITS()) {
-        close();
-        return;
-    } 
+    fitsTab   = new KTabWidget(this);
+    undoGroup = new QUndoGroup(this);
+
+    fitsTab->setTabsClosable(true);
+
+    setCentralWidget(fitsTab);
+
+    connect(fitsTab, SIGNAL(currentChanged(int)), this, SLOT(tabFocusUpdated(int)));
+    connect(fitsTab, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
+
+    statusBar()->insertItem(QString(), FITS_POSITION);
+    statusBar()->setItemFixed(FITS_POSITION, 100);
+    statusBar()->insertItem(QString(), FITS_VALUE);
+    statusBar()->setItemFixed(FITS_VALUE, 100);
+    statusBar()->insertItem(QString(), FITS_RESOLUTION);
+    statusBar()->setItemFixed(FITS_RESOLUTION, 100);
+    statusBar()->insertItem(QString(), FITS_ZOOM);
+    statusBar()->setItemFixed(FITS_ZOOM, 50);
+    statusBar()->insertPermanentItem(i18n("Welcome to KStars FITS Viewer"), FITS_MESSAGE, 1);
+    statusBar()->setItemAlignment(FITS_MESSAGE , Qt::AlignLeft);
 
     KAction *action;
     QFile tempFile;
-    if (KSUtils::openDataFile( tempFile, "histogram.png" ) ) {
+    if (KSUtils::openDataFile( tempFile, "histogram.png" ) )
+    {
         action = actionCollection()->addAction("image_histogram");
         action->setIcon(KIcon(tempFile.fileName()));
         tempFile.close();
     }
-    else {
+    else
+    {
         action = actionCollection()->addAction("image_histogram");
         action->setIcon(KIcon("tools-wizard"));
     }
+
     action->setText(i18n("Histogram"));
-    connect(action, SIGNAL(triggered(bool)), SLOT (imageHistogram()));
+    connect(action, SIGNAL(triggered(bool)), SLOT (histoFITS()));
     action->setShortcuts(KShortcut( Qt::CTRL+Qt::Key_H ));
 
-    KStandardAction::open(this,   SLOT(fileOpen()),   actionCollection());
-    KStandardAction::save(this,   SLOT(fileSave()),   actionCollection());
-    KStandardAction::saveAs(this, SLOT(fileSaveAs()), actionCollection());
+    KStandardAction::open(this,   SLOT(openFile()),   actionCollection());
+    KStandardAction::save(this,   SLOT(saveFile()),   actionCollection());
+    KStandardAction::saveAs(this, SLOT(saveFileAs()), actionCollection());
     KStandardAction::close(this,  SLOT(slotClose()),  actionCollection());
-    KStandardAction::copy(this,   SLOT(fitsCOPY()),   actionCollection());
-    KStandardAction::zoomIn(image,     SLOT(fitsZoomIn()),      actionCollection());
-    KStandardAction::zoomOut(image,    SLOT(fitsZoomOut()),     actionCollection());
-    KStandardAction::actualSize(image, SLOT(fitsZoomDefault()), actionCollection());
+    KStandardAction::copy(this,   SLOT(copyFITS()),   actionCollection());
+
+    KStandardAction::zoomIn(this,     SLOT(ZoomIn()),      actionCollection());
+    KStandardAction::zoomOut(this,    SLOT(ZoomOut()),     actionCollection());
+    KStandardAction::actualSize(this, SLOT(ZoomDefault()), actionCollection());
+
+    KAction *kundo = KStandardAction::undo(undoGroup, SLOT(undo()), actionCollection());
+    KAction *kredo = KStandardAction::redo(undoGroup, SLOT(redo()), actionCollection());
+
+    connect(undoGroup, SIGNAL(canUndoChanged(bool)), kundo, SLOT(setEnabled(bool)));
+    connect(undoGroup, SIGNAL(canRedoChanged(bool)), kredo, SLOT(setEnabled(bool)));
 
     action = actionCollection()->addAction("image_stats");
     action->setIcon(KIcon("view-statistics"));
     action->setText(i18n( "Statistics"));
-    connect(action, SIGNAL(triggered(bool)), SLOT(fitsStatistics()));
+    connect(action, SIGNAL(triggered(bool)), SLOT(statFITS()));
     
     action = actionCollection()->addAction("fits_editor");
     action->setIcon(KIcon("document-properties"));
     action->setText(i18n( "FITS Header"));
-    connect(action, SIGNAL(triggered(bool) ), SLOT(fitsHeader()));
+    connect(action, SIGNAL(triggered(bool) ), SLOT(headerFITS()));
 
     /* Create GUI */
     createGUI("fitsviewer.rc");
 
+    setWindowTitle(i18n("KStars FITS Viewer"));
+
     /* initially resize in accord with KDE rules */
     resize(INITIAL_W, INITIAL_H);
 }
 <at>  <at>  -148,218 +149,247  <at>  <at>  FITSViewer::FITSViewer (const KUrl *url, QWidget *parent)
 FITSViewer::~FITSViewer()
 {}
 
-bool FITSViewer::initFITS()
+bool FITSViewer::addFITS(const KUrl *imageName, FITSMode mode)
 {
-    /* Display image in the central widget */
-    if (image->loadFits(currentURL.path()) == -1)
-    {
-        QFile::remove(currentURL.path());
-        close();
+
+    FITSTab *tab = new FITSTab();
+
+    if (tab->loadFITS(imageName) == false)
         return false;
+
+    switch (mode)
+    {
+      case FITS_NORMAL:
+        fitsTab->addTab(tab, imageName->fileName());
+        break;
+
+      case FITS_FOCUS:
+        fitsTab->addTab(tab, i18n("Focus"));
+        break;
+
     }
 
-    QFile::remove(currentURL.path());
+    connect(tab, SIGNAL(newStatus(QString,FITSBar)), this, SLOT(updateStatusBar(QString,FITSBar)));
+    connect(tab->getImage(), SIGNAL(actionUpdated(QString,bool)), this, SLOT(updateAction(QString,bool)));
+    connect(tab, SIGNAL(changeStatus(bool)), this, SLOT(updateTabStatus(bool)));
+
+    undoGroup->addStack(tab->getUndoStack());
+    tab->tabPositionUpdated();
+
+    fitsImages.push_back(tab);
+
+    fitsTab->setCurrentWidget(tab);
+
 
-    /* Clear history */
-    history->clear();
-    /* Set new file caption */
-    setWindowTitle(currentURL.fileName());
-    statusBar()->changeItem( QString("%1 x %2").arg( (int) image->stats.dim[0]).arg( (int) image->stats.dim[1]), 2);
     return true;
 }
 
-void FITSViewer::slotClose()
+void FITSViewer::tabFocusUpdated(int currentIndex)
 {
-    saveUnsaved();
-    if( !m_Dirty )
-        close();
+    if (currentIndex < 0 || fitsImages.empty())
+        return;
+
+    fitsImages[currentIndex]->tabPositionUpdated();
 }
 
-void FITSViewer::saveUnsaved()
+void FITSViewer::slotClose()
 {
-    if( !m_Dirty )
-        return;
-    QString caption = i18n( "Save Changes to FITS?" );
-    QString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
-    int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStandardGuiItem::save(), KStandardGuiItem::discard() );
-    if( ans == KMessageBox::Yes )
-        fileSave();
-    if( ans == KMessageBox::No ) {
-        history->clear();
-        fitsRestore();
-    }
+    if (undoGroup->isClean())
+        close();
+    else
+        saveUnsaved();
 }
 
 void FITSViewer::closeEvent(QCloseEvent *ev)
 {
     saveUnsaved();
-    if( !m_Dirty )
+    if( undoGroup->isClean() )
         ev->accept();
     else
         ev->ignore();
 }
 
-void FITSViewer::fileOpen()
+void FITSViewer::openFile()
 {
-    saveUnsaved();
-
     KUrl fileURL = KFileDialog::getOpenUrl( QDir::homePath(), "*.fits *.fit *.fts|Flexible Image Transport System");
     if (fileURL.isEmpty())
         return;
-    currentURL = fileURL;
 
-    // Close FITS if open and delete it
-    if (histogram != NULL) {
-        histogram->close();
-        delete histogram;
-        histogram = NULL;
+    QString fpath = fileURL.path();
+    QString cpath;
+
+
+    // Make sure we don't have it open already, if yes, switch to it
+    foreach (FITSTab *tab, fitsImages)
+    {
+        cpath = tab->getCurrentURL()->path();
+        if (fpath == cpath)
+        {
+            fitsTab->setCurrentWidget(tab);
+            return;
+        }
     }
-    initFITS();
+
+    addFITS(&fileURL);
 }
 
-void FITSViewer::fileSave()
+void FITSViewer::saveFile()
 {
-    int err_status;
-    char err_text[FLEN_STATUS];
+    fitsImages[fitsTab->currentIndex()]->saveFile();
+}
 
-    KUrl backupCurrent = currentURL;
-    QString currentDir = Options::fitsDir();
+void FITSViewer::saveFileAs()
+{
+    //currentURL.clear();
 
-    // If no changes made, return.
-    if( !m_Dirty && !currentURL.isEmpty())
+    if (fitsImages.empty())
         return;
 
-    if (currentURL.isEmpty()) {
-        currentURL = KFileDialog::getSaveUrl( currentDir, "*.fits |Flexible Image Transport System");
-        // if user presses cancel
-        if (currentURL.isEmpty()) {
-            currentURL = backupCurrent;
-            return;
-        }
-        if (currentURL.path().contains('.') == 0)
-            currentURL.setPath(currentURL.path() + ".fits");
-
-        if (QFile::exists(currentURL.path())) {
-            int r = KMessageBox::warningContinueCancel(0,
-                        i18n( "A file named \"%1\" already exists. "
-                              "Overwrite it?", currentURL.fileName() ),
-                        i18n( "Overwrite File?" ),
-                        KGuiItem(i18n( "&Overwrite" )) );
-            if(r==KMessageBox::Cancel) return;
-        }
-    }
+    fitsImages[fitsTab->currentIndex()]->saveFileAs();
 
-    if ( currentURL.isValid() ) {
-        if ( (err_status = image->saveFITS('!' + currentURL.path())) < 0) {
-            fits_get_errstatus(err_status, err_text);
-            // Use KMessageBox or something here
-            KMessageBox::error(0, i18n("FITS file save error: %1",
-                                       QString::fromUtf8(err_text)), i18n("FITS Save"));
-            return;
+}
+
+void FITSViewer::copyFITS()
+{
+    if (fitsImages.empty())
+        return;
+
+   fitsImages[fitsTab->currentIndex()]->copyFITS();
+}
+
+void FITSViewer::histoFITS()
+{
+    if (fitsImages.empty())
+        return;
+
+    fitsImages[fitsTab->currentIndex()]->histoFITS();
+}
+
+void FITSViewer::statFITS()
+{
+    if (fitsImages.empty())
+        return;
+
+  fitsImages[fitsTab->currentIndex()]->statFITS();
+}
+
+void FITSViewer::headerFITS()
+{
+    if (fitsImages.empty())
+        return;
+
+  fitsImages[fitsTab->currentIndex()]->headerFITS();
+}
+
+int FITSViewer::saveUnsaved(int index)
+{
+    QUndoStack *undoStack = NULL;
+    FITSTab *targetTab = NULL;
+    QString caption = i18n( "Save Changes to FITS?" );
+    QString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
+
+    if (undoGroup->isClean())
+        return -1;
+
+    if (index != -1)
+        targetTab = fitsImages[index];
+    else
+    {
+        foreach(FITSTab *tab, fitsImages)
+        {
+            undoStack = tab->getUndoStack();
+
+            if (undoStack->isClean())
+                continue;
+
+            targetTab = tab;
+            break;
         }
+     }
 
-        statusBar()->changeItem(i18n("File saved."), 3);
+    if (targetTab == NULL)
+        return -1;
 
-        m_Dirty = false;
-        history->clear();
-        fitsRestore();
-    } else {
-        QString message = i18n( "Invalid URL: %1", currentURL.url() );
-        KMessageBox::sorry( 0, message, i18n( "Invalid URL" ) );
+    int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStandardGuiItem::save(), KStandardGuiItem::discard() );
+    if( ans == KMessageBox::Yes )
+    {
+            targetTab->saveFile();
+            return 0;
+    }
+    else if( ans == KMessageBox::No )
+    {
+       fitsImages.removeOne(targetTab);
+       delete targetTab;
+       return 1;
     }
+
+    return -1;
 }
 
-void FITSViewer::fileSaveAs()
+void FITSViewer::updateStatusBar(const QString &msg, FITSBar id)
 {
-    currentURL.clear();
-    fileSave();
+   statusBar()->changeItem(msg, id);
 }
 
-void FITSViewer::fitsCOPY()
+void FITSViewer::ZoomIn()
 {
-    QApplication::clipboard()->setImage( *(image->getDisplayImage()) );
+    if (fitsImages.empty())
+        return;
+
+  fitsImages[fitsTab->currentIndex()]->ZoomIn();
 }
 
-void FITSViewer::imageHistogram()
+void FITSViewer::ZoomOut()
 {
-    if (histogram == NULL)
-        histogram = new FITSHistogram(this);
-    histogram->show();
+    if (fitsImages.empty())
+        return;
+
+  fitsImages[fitsTab->currentIndex()]->ZoomOut();
 }
 
-void FITSViewer::fitsRestore(bool clean)
+void FITSViewer::ZoomDefault()
 {
-    if (clean) {
-        m_Dirty = false;
-        setWindowTitle(currentURL.fileName());
-    }
+    if (fitsImages.empty())
+        return;
+
+  fitsImages[fitsTab->currentIndex()]->ZoomDefault();
 }
 
-void FITSViewer::fitsChange()
+void FITSViewer::updateAction(const QString &name, bool enable)
 {
-    m_Dirty = true;
-    setWindowTitle(currentURL.fileName() + i18n(" [modified]"));
+    QAction *toolAction = NULL;
+
+    toolAction = actionCollection()->action(name);
+    if (toolAction != NULL)
+        toolAction->setEnabled (enable);
 }
 
-void FITSViewer::fitsStatistics()
+void FITSViewer::updateTabStatus(bool clean)
 {
-    QDialog statDialog;
-    Ui::statForm stat;
-    stat.setupUi(&statDialog);
-
-    stat.widthOUT->setText(QString::number(image->stats.dim[0]));
-    stat.heightOUT->setText(QString::number(image->stats.dim[1]));
-    stat.bitpixOUT->setText(QString::number(image->stats.bitpix));
-    stat.maxOUT->setText(QString::number(image->stats.max));
-    stat.minOUT->setText(QString::number(image->stats.min));
-    stat.meanOUT->setText(QString::number(image->stats.average));
-    stat.stddevOUT->setText(QString::number(image->stats.stddev));
-
-    statDialog.exec();
+    if (fitsImages.empty())
+        return;
+
+  QString tabText = fitsImages[fitsTab->currentIndex()]->getCurrentURL()->fileName();
+
+  fitsTab->setTabText(fitsTab->currentIndex(), clean ? tabText : tabText + "*");
 }
 
-void FITSViewer::fitsHeader()
+void FITSViewer::closeTab(int index)
 {
-    QString recordList;
-    int nkeys;
-    int err_status;
-    char err_text[FLEN_STATUS];
-
-    if ( (err_status = image->getFITSRecord(recordList, nkeys)) < 0) {
-        fits_get_errstatus(err_status, err_text);
-        KMessageBox::error(0, i18n("FITS record error: %1", QString::fromUtf8(err_text)), i18n("FITS Header"));
+    if (fitsImages.empty())
         return;
-    }
 
-    //FIXME: possible crash! Must use QPointer<...>!
-    QDialog fitsHeaderDialog;
-    Ui::fitsHeaderDialog header;
-    header.setupUi(&fitsHeaderDialog);
-    header.tableWidget->setRowCount(nkeys);
-    for(int i = 0; i < nkeys; i++) {
-        QString record = recordList.mid(i*80, 80);
-        // I love regexp!
-        QStringList properties = record.split(QRegExp("[=/]"));
-
-        QTableWidgetItem* tempItem = new QTableWidgetItem(properties[0].simplified());
-        tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
-        header.tableWidget->setItem(i, 0, tempItem);
-
-        if (properties.size() > 1) {
-            tempItem = new QTableWidgetItem(properties[1].simplified());
-            tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
-            header.tableWidget->setItem(i, 1, tempItem);
-        }
-        if (properties.size() > 2) {
-            tempItem = new QTableWidgetItem(properties[2].simplified());
-            tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
-            header.tableWidget->setItem(i, 2, tempItem);
-        }
+    int status = saveUnsaved(index);
 
+    FITSTab *tab = fitsImages[index];
+
+    if (status != 1)
+    {
+        fitsImages.removeOne(tab);
+        delete tab;
     }
 
-    header.tableWidget->resizeColumnsToContents();
-    fitsHeaderDialog.exec();
+
 }
 
 #include "fitsviewer.moc"
diff --git a/kstars/fitsviewer/fitsviewer.h b/kstars/fitsviewer/fitsviewer.h
index 7ed6531..c1dbab9 100644
--- a/kstars/fitsviewer/fitsviewer.h
+++ b/kstars/fitsviewer/fitsviewer.h
 <at>  <at>  -20,11 +20,10  <at>  <at> 
 #ifndef FITSViewer_H_
 #define FITSViewer_H_
 
-#include <QCloseEvent>
+#include <QList>
 
-#include <kdialog.h>
+#include <KDialog>
 #include <kxmlguiwindow.h>
-#include <kurl.h>
 
 #ifdef WIN32
 // avoid compiler warning when windows.h is included after fitsio.h
 <at>  <at>  -33,54 +32,62  <at>  <at> 
 
 #include <fitsio.h>
 
-#define INITIAL_W	640
-#define INITIAL_H	480
+#include "fitscommon.h"
+
+class QCloseEvent;
+class QUndoGroup;
 
 class KUndoStack;
+class KTabWidget;
+class KUrl;
+
 class FITSImage;
 class FITSHistogram;
-class QCloseEvent;
+class FITSTab;
+
 
 class FITSViewer : public KXmlGuiWindow
 {
     Q_OBJECT
+
 public:
-    friend class FITSImage;
-    friend class FITSHistogram;
-    friend class FITSHistogramCommand;
 
     /**Constructor. */
-    FITSViewer (const KUrl *imageName, QWidget *parent);
+    FITSViewer (QWidget *parent);
     ~FITSViewer();
+    bool addFITS(const KUrl *imageName, FITSMode mode=FITS_NORMAL);
+
 
 protected:
+
     virtual void closeEvent(QCloseEvent *ev);
 
 public slots:
-    void fitsChange();
-
-private slots:
-    void fileOpen();
-    void fileSave();
-    void fileSaveAs();
-    void fitsCOPY();
-    void fitsRestore(bool clean=true);
-    void fitsStatistics();
-    void fitsHeader();
+
+    void openFile();
+    void saveFile();
+    void saveFileAs();
+    void copyFITS();
+    void statFITS();
+    void headerFITS();
     void slotClose();
-    void imageHistogram();
+    void histoFITS();
+    void tabFocusUpdated(int currentIndex);
+    void updateStatusBar(const QString &msg, FITSBar id);
+    void ZoomIn();
+    void ZoomOut();
+    void ZoomDefault();
+    void updateAction(const QString &name, bool enable);
+    void updateTabStatus(bool clean);
+    int saveUnsaved(int index=-1);
+    void closeTab(int index);
 
 private:
-    /** Ask user whether he wants to save changes and save if he do. */
-    void saveUnsaved();
-    bool initFITS();
 
-    FITSImage *image;           /* FITS image object */
-    FITSHistogram *histogram;   /* FITS Histogram */
+    KTabWidget *fitsTab;
+    QUndoGroup *undoGroup;
 
-    KUndoStack *history;        /* History for undo/redo */
-    bool m_Dirty;               /* Document modified? */
-    KUrl currentURL;            /* FITS File name and path */
+    QList<FITSTab*> fitsImages;
 };
 
 #endif
diff --git a/kstars/indi/indistd.cpp b/kstars/indi/indistd.cpp
index 8b4122c..91a872a 100644
--- a/kstars/indi/indistd.cpp
+++ b/kstars/indi/indistd.cpp
 <at>  <at>  -221,14 +221,13  <at>  <at>  void INDIStdDevice::handleBLOB(unsigned char *buffer, int bufferSize, const QStr
     }
 
 
-    // FIXME It appears that FITSViewer causes a possible stack corruption, needs to investigate
-
     // Unless we have cfitsio, we're done.
     #ifdef HAVE_CFITSIO_H
     KUrl fileURL(filename);
 
-    FITSViewer * fv = new FITSViewer(&fileURL, ksw);
-    fv->fitsChange();
+    FITSViewer * fv = new FITSViewer(ksw);
+    fv->addFITS(&fileURL);
+    //fv->fitsChange();
     fv->show();
     #endif
 
diff --git a/kstars/kstarsactions.cpp b/kstars/kstarsactions.cpp
index 3cc9cad..df2165e 100644
--- a/kstars/kstarsactions.cpp
+++ b/kstars/kstarsactions.cpp
 <at>  <at>  -536,7 +536,8  <at>  <at>  void KStars::slotOpenFITS()
     if (fileURL.isEmpty())
         return;
 
-    FITSViewer * fv = new FITSViewer(&fileURL, this);
+    FITSViewer * fv = new FITSViewer(this);
+    fv->addFITS(&fileURL);
     fv->show();
 #endif
 }
Jasem Mutlaq | 25 Jan 22:57

[kstars] kstars: Introducing Ekos, a new tool for KStars that will empower amateur astronomers to utilize INDI based devices to perform essential functions in astrophotography:

Git commit 18227de7bc08503da826f930c93720c8d1cc29e4 by Jasem Mutlaq.
Committed on 25/01/2012 at 22:51.
Pushed by mutlaqja into branch 'master'.

Introducing Ekos, a new tool for KStars that will empower amateur astronomers to utilize INDI based devices to perform essential functions in astrophotography:

+ Alignment: Facilitates polar alignment procedure.
+ Focus: Manual and Auto Focusing algorithms.
+ Guide: Guide the mount for accurate tracking for extended periods of time.
+ Capture: Capture CCDs images individually or in batch along with filter selection support.

This is a very early release and only basic functionality is provided. The above features will be added with time.

CCMAIL:kstars-devel <at> kde.org

M  +15   -2    kstars/CMakeLists.txt
M  +1    -1    kstars/dialogs/detaildialog.cpp
A  +12   -0    kstars/ekos/ccd.cpp     [License: UNKNOWN]  *
A  +21   -0    kstars/ekos/ccd.h     [License: UNKNOWN]  *
A  +290  -0    kstars/ekos/ekos.cpp     [License: GPL (v2+)]
A  +66   -0    kstars/ekos/ekos.h     [License: GPL (v2+)]
A  +366  -0    kstars/ekos/ekos.ui
A  +12   -0    kstars/ekos/filter.cpp     [License: UNKNOWN]  *
A  +19   -0    kstars/ekos/filter.h     [License: UNKNOWN]  *
A  +12   -0    kstars/ekos/focuser.cpp     [License: UNKNOWN]  *
A  +19   -0    kstars/ekos/focuser.h     [License: UNKNOWN]  *
A  +116  -0    kstars/ekos/genericdevice.cpp     [License: UNKNOWN]  *
A  +67   -0    kstars/ekos/genericdevice.h     [License: UNKNOWN]  *
A  +12   -0    kstars/ekos/telescope.cpp     [License: UNKNOWN]  *
A  +19   -0    kstars/ekos/telescope.h     [License: UNKNOWN]  *
M  +13   -5    kstars/indi/devicemanager.cpp
M  +1    -0    kstars/indi/devicemanager.h
M  +13   -13   kstars/indi/imagesequence.cpp
M  +53   -45   kstars/indi/indidevice.cpp
M  +15   -4    kstars/indi/indidevice.h
M  +3    -4    kstars/indi/indidriver.cpp
M  +2    -2    kstars/indi/indidriver.h
M  +15   -15   kstars/indi/indielement.cpp
M  +3    -9    kstars/indi/indielement.h
M  +31   -30   kstars/indi/indiproperty.cpp
M  +3    -3    kstars/indi/indiproperty.h
M  +21   -15   kstars/indi/indistd.cpp
M  +1    -1    kstars/indi/telescopewizardprocess.cpp
M  +1    -1    kstars/kspopupmenu.cpp
M  +2    -2    kstars/kstars.cpp
M  +5    -0    kstars/kstars.h
M  +13   -0    kstars/kstarsactions.cpp
M  +9    -0    kstars/kstarsinit.cpp
M  +1    -0    kstars/kstarsui-indi.rc
M  +33   -17   kstars/oal/equipmentwriter.ui
M  +2    -2    kstars/skymapdrawabstract.cpp
M  +1    -1    kstars/tools/observinglist.cpp

The files marked with a * at the end have a non valid license. Please read: http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


http://commits.kde.org/kstars/18227de7bc08503da826f930c93720c8d1cc29e4

diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt
index 6f7ed20..80acbf2 100644
--- a/kstars/CMakeLists.txt
+++ b/kstars/CMakeLists.txt
 <at>  <at>  -43,6 +43,19  <at>  <at>  if (INDI_FOUND)
     indi/streamform.ui
     indi/telescopewizard.ui
     )
+
+   set(ekosui_SRCS
+      ekos/ekos.ui
+   )
+
+   set(ekos_SRCS
+      ekos/ekos.cpp
+      ekos/genericdevice.cpp
+      ekos/telescope.cpp
+      ekos/ccd.cpp
+      ekos/filter.cpp
+      ekos/focuser.cpp
+   )
   
   include_directories(${INDI_INCLUDE_DIR})
 endif(INDI_FOUND)
 <at>  <at>  -385,7 +398,7  <at>  <at>  set(printingui_SRCS
     printing/pwizwelcome.ui
 )
 
-set(kstars_SRCS ${indi_SRCS} ${fits_SRCS} 
+set(kstars_SRCS ${indi_SRCS} ${ekos_SRCS} ${fits_SRCS}
 	${libkstarswidgets_SRCS} ${libkstarscomponents_SRCS} ${libkstarstools_SRCS} 
 	${kstars_extra_SRCS}  ${kstars_gl_SRCS} ${kstars_projection_SRCS} ${xplanet_SRCS}
 	${kstars_options_SRCS} ${kstars_skyobjects_SRCS} ${kstars_dialogs_SRCS} ${oal_SRCS}
 <at>  <at>  -401,7 +414,7  <at>  <at>  kde4_add_kcfg_files(kstars_SRCS ${kstars_KCFG_SRCS})
 #kde4_add_dcop_skels(kstars_SRCS kstarsinterface.h simclockinterface.h )
 
 kde4_add_ui_files(kstars_SRCS
-       ${indiui_SRCS} ${fitsui_SRCS} ${xplanetui_SRCS} ${kstars_optionsui_SRCS} ${kstars_dialogsui_SRCS}
+       ${indiui_SRCS} ${ekosui_SRCS} ${fitsui_SRCS} ${xplanetui_SRCS} ${kstars_optionsui_SRCS} ${kstars_dialogsui_SRCS}
        ${printingui_SRCS}
          thumbnailpicker.ui thumbnaileditor.ui oal/observeradd.ui oal/equipmentwriter.ui oal/execute.ui
 )
diff --git a/kstars/dialogs/detaildialog.cpp b/kstars/dialogs/detaildialog.cpp
index a6407d5..5d643c6 100644
--- a/kstars/dialogs/detaildialog.cpp
+++ b/kstars/dialogs/detaildialog.cpp
 <at>  <at>  -1015,7 +1015,7  <at>  <at>  void DetailDialog::centerTelescope()
             ConnectEle = indidev->findElem("CONNECT");
             if (!ConnectEle) continue;
 
-            if (ConnectEle->state == PS_OFF)
+            if (ConnectEle->switch_state == ISS_OFF)
             {
                 KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", indidev->label));
                 return;
diff --git a/kstars/ekos/ccd.cpp b/kstars/ekos/ccd.cpp
new file mode 100644
index 0000000..0e1d9a4
--- /dev/null
+++ b/kstars/ekos/ccd.cpp
 <at>  <at>  -0,0 +1,12  <at>  <at> 
+#include "ccd.h"
+
+namespace EkoDevice
+{
+
+CCD::CCD(INDI_D *idp) : GenericDevice(idp)
+{
+}
+
+} // namespace EkoDevice
+
+#include "ccd.moc"
diff --git a/kstars/ekos/ccd.h b/kstars/ekos/ccd.h
new file mode 100644
index 0000000..390661f
--- /dev/null
+++ b/kstars/ekos/ccd.h
 <at>  <at>  -0,0 +1,21  <at>  <at> 
+#ifndef EKODEVICE_CCD_H
+#define EKODEVICE_CCD_H
+
+#include "ekos/genericdevice.h"
+
+class INDI_D;
+
+namespace EkoDevice
+{
+
+class CCD : public GenericDevice
+{
+    Q_OBJECT
+
+public:
+    CCD(INDI_D *dp);
+};
+
+} // namespace EkoDevice
+
+#endif // EKODEVICE_CCD_H
diff --git a/kstars/ekos/ekos.cpp b/kstars/ekos/ekos.cpp
new file mode 100644
index 0000000..4aa2cde
--- /dev/null
+++ b/kstars/ekos/ekos.cpp
 <at>  <at>  -0,0 +1,290  <at>  <at> 
+/*  Ekos
+    Copyright (C) 2012 Jasem Mutlaq <mutlaqja <at> ikarustech.com>
+
+    This application is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+ */
+
+#include "ekos.h"
+
+#include "kstars.h"
+
+#include <KMessageBox>
+
+#include <config-kstars.h>
+
+
+#include "indi/devicemanager.h"
+#include "indi/indimenu.h"
+#include "indi/indielement.h"
+#include "indi/indidriver.h"
+#include "indi/indiproperty.h"
+#include "indi/indidevice.h"
+#include "indi/indistd.h"
+
+#include "ekos/telescope.h"
+#include "ekos/ccd.h"
+#include "ekos/filter.h"
+#include "ekos/focuser.h"
+
+Ekos::Ekos( KStars *_ks )
+        : QDialog( _ks )
+{
+    setupUi(this);
+
+    deviceManager = NULL;
+    nDevices=0;
+    useGuiderFromCCD=false;
+    useFilterFromCCD=false;
+
+    scope   =  NULL;
+    ccd     =  NULL;
+    guider  =  NULL;
+    focuser =  NULL;
+    filter  =  NULL;
+
+    telescopeCombo->addItem("--");
+    ccdCombo->addItem("--");
+    guiderCombo->addItem("--");
+    focuserCombo->addItem("--");
+    filterCombo->addItem("--");
+
+    foreach(IDevice *dv, KStars::Instance()->indiDriver()->devices)
+    {
+        switch (dv->type)
+        {
+        case KSTARS_TELESCOPE:
+             telescopeCombo->addItem(dv->tree_label);
+             break;
+
+        case KSTARS_CCD:
+            ccdCombo->addItem(dv->tree_label);
+            guiderCombo->addItem(dv->tree_label);
+            break;
+
+        case KSTARS_FOCUSER:
+            focuserCombo->addItem(dv->tree_label);
+            break;
+
+        case KSTARS_FILTER:
+            filterCombo->addItem(dv->tree_label);
+            break;
+
+        }
+    }
+
+    toolsWidget->setTabEnabled(1, false);
+    toolsWidget->setTabEnabled(2, false);
+    toolsWidget->setTabEnabled(3, false);
+    toolsWidget->setTabEnabled(4, false);
+
+    connect(processINDIB, SIGNAL(clicked()), this, SLOT(processINDI()));
+
+    connect(connectB, SIGNAL(clicked()), this, SLOT(connectDevices()));
+
+    connect(disconnectB, SIGNAL(clicked()), this, SLOT(disconnectDevices()));
+
+    connect(controlPanelB, SIGNAL(clicked()), KStars::Instance()->indiMenu(), SLOT(show()));
+
+}
+
+Ekos::~Ekos() {}
+
+void Ekos::processINDI()
+{
+    int port=0;
+
+    if (processINDIB->text() == i18n("Stop INDI"))
+    {
+        cleanDevices();
+        return;
+    }
+
+    IDevice *scope_dv=NULL, *ccd_dv=NULL, *guider_dv=NULL, *filter_dv=NULL, *focuser_dv=NULL;
+
+    // Don't start INDI if it's already started
+    if (deviceManager != NULL)
+        return;
+
+    // Let's clear cache
+    nDevices=0;
+    processed_devices.clear();
+
+    scope_dv   = KStars::Instance()->indiDriver()->findDeviceByLabel(telescopeCombo->currentText());
+    ccd_dv     = KStars::Instance()->indiDriver()->findDeviceByLabel(ccdCombo->currentText());
+    guider_dv  = KStars::Instance()->indiDriver()->findDeviceByLabel(guiderCombo->currentText());
+    filter_dv  = KStars::Instance()->indiDriver()->findDeviceByLabel(filterCombo->currentText());
+    focuser_dv = KStars::Instance()->indiDriver()->findDeviceByLabel(focuserCombo->currentText());
+
+    if (guider_dv == ccd_dv)
+        useGuiderFromCCD = true;
+
+    if (filter_dv == ccd_dv)
+        useFilterFromCCD = true;
+
+    if (scope_dv != NULL)
+        processed_devices.append(scope_dv);
+    if (ccd_dv != NULL)
+        processed_devices.append(ccd_dv);
+    if (guider_dv != NULL && useGuiderFromCCD == false)
+        processed_devices.append(guider_dv);
+    if (filter_dv != NULL && useFilterFromCCD == false)
+        processed_devices.append(filter_dv);
+
+    if (focuser_dv != NULL)
+        processed_devices.append(focuser_dv);
+
+
+    if (processed_devices.empty()) return;
+
+    nDevices = processed_devices.size();
+
+    // Select random port within range is none specified.
+     port = KStars::Instance()->indiDriver()->getINDIPort(port);
+
+     if (port <= 0)
+     {
+           KMessageBox::error(0, i18n("Cannot start INDI server: port error."));
+          return;
+     }
+
+         deviceManager = KStars::Instance()->indiMenu()->initDeviceManager("localhost", ((uint) port), localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER);
+
+         if (deviceManager == NULL)
+         {
+                 kWarning() << "Warning: device manager has not been established properly";
+                 return;
+         }
+
+         deviceManager->appendManagedDevices(processed_devices);
+         deviceManager->startServer();
+         //connect(deviceManager, SIGNAL(deviceManagerError(DeviceManager *)), this, SLOT(disconnectDevices()));
+
+         connect(deviceManager, SIGNAL(deviceManagerError(DeviceManager *)), this, SLOT(cleanDevices()));
+
+         connect(deviceManager, SIGNAL(newDevice(INDI_D*)), this, SLOT(processNewDevice(INDI_D*)));
+
+         connectB->setEnabled(false);
+         disconnectB->setEnabled(false);
+         controlPanelB->setEnabled(false);
+
+         processINDIB->setText(i18n("Stop INDI"));
+
+}
+
+void Ekos::connectDevices()
+{
+    if (scope != NULL)
+        scope->Connect();
+
+    if (ccd != NULL)
+        ccd->Connect();
+
+    if (guider != NULL)
+        guider->Connect();
+
+    if (focuser != NULL)
+        focuser->Connect();
+
+    if (filter != NULL)
+        filter->Connect();
+}
+
+void Ekos::disconnectDevices()
+{
+
+    if (scope != NULL)
+        scope->Disconnect();
+
+    if (ccd != NULL)
+        ccd->Disconnect();
+
+    if (guider != NULL)
+        guider->Disconnect();
+
+    if (focuser != NULL)
+        focuser->Disconnect();
+
+    if (filter != NULL)
+        filter->Disconnect();
+
+}
+
+void Ekos::cleanDevices()
+{
+    KStars::Instance()->indiMenu()->stopDeviceManager(processed_devices);
+
+    processINDIB->setText(i18n("Start INDI"));
+    processINDIB->setEnabled(true);
+    connectB->setEnabled(false);
+    disconnectB->setEnabled(false);
+    controlPanelB->setEnabled(false);
+}
+
+void Ekos::processNewDevice(INDI_D *dp)
+{
+    // Let's wait until we get a CONNECTION property from the device
+
+    //connect(dp, SIGNAL(newProperty(INDI_P*)), this, SLOT(NewProperty(INDI_P*)));
+
+    foreach (IDevice *dv, processed_devices)
+    {
+        if (dv->name == dp->name)
+        {
+            switch (dv->type)
+            {
+               case KSTARS_TELESCOPE:
+                if (scope == NULL && dp->name == telescopeCombo->currentText())
+                {
+                    scope = new EkoDevice::Telescope(dp);
+                    connect(dp, SIGNAL(newProperty(INDI_P*)), scope, SLOT(processNewProperty(INDI_P*)));
+                }
+                break;
+
+            case KSTARS_CCD:
+                if (ccd == NULL && dp->name == ccdCombo->currentText())
+                {
+                     ccd = new EkoDevice::CCD(dp);
+                     connect(dp, SIGNAL(newProperty(INDI_P*)), ccd, SLOT(processNewProperty(INDI_P*)));
+                }
+               else if (guider == NULL && useGuiderFromCCD == false && dp->name == guiderCombo->currentText())
+               {
+                     guider = new EkoDevice::CCD(dp);
+                     connect(dp, SIGNAL(newProperty(INDI_P*)), guider, SLOT(processNewProperty(INDI_P*)));
+               }
+               break;
+
+            case KSTARS_FILTER:
+                if (filter == NULL && useFilterFromCCD == false && dp->name == filterCombo->currentText())
+                {
+                     filter = new EkoDevice::Filter(dp);
+                     connect(dp, SIGNAL(newProperty(INDI_P*)), filter, SLOT(processNewProperty(INDI_P*)));
+                 }
+                 break;
+
+            case KSTARS_FOCUSER:
+                if (focuser == NULL && dp->name == focuserCombo->currentText())
+                {
+                     focuser = new EkoDevice::Focuser(dp);
+                     connect(dp, SIGNAL(newProperty(INDI_P*)), focuser, SLOT(processNewProperty(INDI_P*)));
+                }
+                break;
+
+            }
+        }
+    }
+
+    nDevices--;
+
+    if (nDevices == 0)
+    {
+        connectB->setEnabled(true);
+        disconnectB->setEnabled(false);
+        controlPanelB->setEnabled(true);
+    }
+
+}
+
+#include "ekos.moc"
diff --git a/kstars/ekos/ekos.h b/kstars/ekos/ekos.h
new file mode 100644
index 0000000..71f1d7b
--- /dev/null
+++ b/kstars/ekos/ekos.h
 <at>  <at>  -0,0 +1,66  <at>  <at> 
+#ifndef EKOS_H
+#define EKOS_H
+
+/*  Ekos
+    Copyright (C) 2012 Jasem Mutlaq <mutlaqja <at> ikarustech.com>
+
+    This application is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+ */
+
+
+#include "ui_ekos.h"
+
+#include <QDialog>
+
+class DeviceManager;
+class KStars;
+class INDI_D;
+class INDI_P;
+class IDevice;
+
+namespace EkoDevice
+{
+   class GenericDevice;
+   class Telescope;
+   class CCD;
+   class Focuser;
+   class Filter;
+}
+
+class Ekos : public QDialog, public Ui::Ekos
+{
+    Q_OBJECT
+
+public:
+    Ekos( KStars *_ks );
+    ~Ekos();
+
+public slots:
+    void processINDI();
+    void connectDevices();
+    void disconnectDevices();
+    void cleanDevices();
+    void processNewDevice(INDI_D *dp);
+
+
+ private:
+    DeviceManager *deviceManager;
+    bool useGuiderFromCCD;
+    bool useFilterFromCCD;
+
+    EkoDevice::Telescope *scope;
+    EkoDevice::CCD *ccd;
+    EkoDevice::CCD *guider;
+    EkoDevice::Focuser *focuser;
+    EkoDevice::Filter *filter;
+
+    unsigned short nDevices;
+    QList<IDevice *> processed_devices;
+
+};
+
+
+#endif // EKOS_H
diff --git a/kstars/ekos/ekos.ui b/kstars/ekos/ekos.ui
new file mode 100644
index 0000000..5ac7fd3
--- /dev/null
+++ b/kstars/ekos/ekos.ui
 <at>  <at>  -0,0 +1,366  <at>  <at> 
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Ekos</class>
+ <widget class="QWidget" name="Ekos">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1014</width>
+    <height>195</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Ekos</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="toolsWidget">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>150</height>
+      </size>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="setupTab">
+      <attribute name="title">
+       <string>Setup</string>
+      </attribute>
+      <layout class="QHBoxLayout" name="horizontalLayout_4">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_4">
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout">
+           <item>
+            <layout class="QVBoxLayout" name="verticalLayout_2">
+             <item>
+              <widget class="QLabel" name="label_6">
+               <property name="text">
+                <string>Telescope:</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QLabel" name="label_7">
+               <property name="text">
+                <string>CCD:</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QLabel" name="label_8">
+               <property name="text">
+                <string>Guider:</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <layout class="QVBoxLayout" name="verticalLayout_3">
+             <item>
+              <widget class="QComboBox" name="telescopeCombo">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="ccdCombo">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="guiderCombo">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <spacer name="verticalSpacer_3">
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>40</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_7">
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_2">
+           <item>
+            <layout class="QVBoxLayout" name="verticalLayout_5">
+             <item>
+              <widget class="QLabel" name="label_9">
+               <property name="text">
+                <string>Filter Wheel:</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QLabel" name="label_10">
+               <property name="text">
+                <string>Focuser:</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <layout class="QVBoxLayout" name="verticalLayout_6">
+             <item>
+              <widget class="QComboBox" name="filterCombo">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="focuserCombo">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <spacer name="verticalSpacer_4">
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>40</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>18</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_8">
+         <item>
+          <widget class="QGroupBox" name="groupBox_2">
+           <property name="title">
+            <string>Mode</string>
+           </property>
+           <property name="flat">
+            <bool>false</bool>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout_9">
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_16">
+              <item>
+               <widget class="QRadioButton" name="localR">
+                <property name="text">
+                 <string>Local</string>
+                </property>
+                <property name="checked">
+                 <bool>true</bool>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QRadioButton" name="remoteR">
+                <property name="text">
+                 <string>Remote</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_17">
+              <item>
+               <spacer name="horizontalSpacer_5">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+              <item>
+               <widget class="KPushButton" name="configureB">
+                <property name="enabled">
+                 <bool>false</bool>
+                </property>
+                <property name="text">
+                 <string>Configure...</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_3">
+           <item>
+            <widget class="KPushButton" name="processINDIB">
+             <property name="enabled">
+              <bool>true</bool>
+             </property>
+             <property name="text">
+              <string>Start INDI</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="KPushButton" name="controlPanelB">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Control Panel...</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <spacer name="horizontalSpacer_6">
+             <property name="orientation">
+              <enum>Qt::Horizontal</enum>
+             </property>
+             <property name="sizeType">
+              <enum>QSizePolicy::Maximum</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>40</width>
+               <height>20</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+           <item>
+            <widget class="KPushButton" name="connectB">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Connect</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="KPushButton" name="disconnectB">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Disconnect</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="alignTab">
+      <property name="enabled">
+       <bool>false</bool>
+      </property>
+      <attribute name="title">
+       <string>Align</string>
+      </attribute>
+     </widget>
+     <widget class="QWidget" name="focusTab">
+      <property name="enabled">
+       <bool>false</bool>
+      </property>
+      <attribute name="title">
+       <string>Focus</string>
+      </attribute>
+     </widget>
+     <widget class="QWidget" name="guideTab">
+      <property name="enabled">
+       <bool>false</bool>
+      </property>
+      <attribute name="title">
+       <string>Guide</string>
+      </attribute>
+     </widget>
+     <widget class="QWidget" name="captureTab">
+      <property name="enabled">
+       <bool>false</bool>
+      </property>
+      <attribute name="title">
+       <string>Capture</string>
+      </attribute>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KPushButton</class>
+   <extends>QPushButton</extends>
+   <header>kpushbutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/kstars/ekos/filter.cpp b/kstars/ekos/filter.cpp
new file mode 100644
index 0000000..11a404a
--- /dev/null
+++ b/kstars/ekos/filter.cpp
 <at>  <at>  -0,0 +1,12  <at>  <at> 
+#include "filter.h"
+
+namespace EkoDevice
+{
+
+Filter::Filter(INDI_D *idp) : GenericDevice(idp)
+{
+}
+
+} // namespace EkoDevice
+
+#include "filter.moc"
diff --git a/kstars/ekos/filter.h b/kstars/ekos/filter.h
new file mode 100644
index 0000000..fe49ed2
--- /dev/null
+++ b/kstars/ekos/filter.h
 <at>  <at>  -0,0 +1,19  <at>  <at> 
+#ifndef EKODEVICE_FILTER_H
+#define EKODEVICE_FILTER_H
+
+#include "ekos/genericdevice.h"
+
+class INDI_D;
+
+namespace EkoDevice
+{
+
+class Filter : public GenericDevice
+{
+public:
+    Filter(INDI_D *dp);
+};
+
+} // namespace EkoDevice
+
+#endif // EKODEVICE_FILTER_H
diff --git a/kstars/ekos/focuser.cpp b/kstars/ekos/focuser.cpp
new file mode 100644
index 0000000..e106a8e
--- /dev/null
+++ b/kstars/ekos/focuser.cpp
 <at>  <at>  -0,0 +1,12  <at>  <at> 
+#include "focuser.h"
+
+namespace EkoDevice
+{
+
+Focuser::Focuser(INDI_D *idp)  : GenericDevice(idp)
+{
+}
+
+} // namespace EkoDevice
+
+#include "focuser.moc"
diff --git a/kstars/ekos/focuser.h b/kstars/ekos/focuser.h
new file mode 100644
index 0000000..9c9f2c6
--- /dev/null
+++ b/kstars/ekos/focuser.h
 <at>  <at>  -0,0 +1,19  <at>  <at> 
+#ifndef EKODEVICE_FOCUSER_H
+#define EKODEVICE_FOCUSER_H
+
+#include "ekos/genericdevice.h"
+
+class INDI_D;
+
+namespace EkoDevice
+{
+
+class Focuser : public GenericDevice
+{
+public:
+    Focuser(INDI_D *);
+};
+
+} // namespace EkoDevice
+
+#endif // EKODEVICE_FOCUSER_H
diff --git a/kstars/ekos/genericdevice.cpp b/kstars/ekos/genericdevice.cpp
new file mode 100644
index 0000000..3d4d132
--- /dev/null
+++ b/kstars/ekos/genericdevice.cpp
 <at>  <at>  -0,0 +1,116  <at>  <at> 
+#include "genericdevice.h"
+
+#include "indi/indielement.h"
+#include "indi/indiproperty.h"
+namespace EkoDevice
+{
+
+GenericDevice::GenericDevice(INDI_D *idp) : QObject()
+{
+  dp = idp;
+}
+
+
+ bool GenericDevice::Connect()
+ {
+     INDI_P * pp = dp->findProp("CONNECTION");
+
+     if (pp == NULL)
+         return false;
+
+     INDI_E * el = pp->findElement(("CONNECT"));
+
+     if (el == NULL)
+         return false;
+
+     pp->newSwitch(el);
+
+     return true;
+ }
+
+ bool GenericDevice::Disconnect()
+ {
+     INDI_P * pp = dp->findProp("CONNECTION");
+
+     if (pp == NULL)
+         return false;
+
+     INDI_E * el = pp->findElement(("DISCONNECT"));
+
+     if (el == NULL)
+         return false;
+
+     pp->newSwitch(el);
+
+
+     return true;
+ }
+
+ INDI_P * GenericDevice::getProperty(const QString &pp)
+ {
+     return dp->findProp(pp);
+ }
+
+ INDI_E * GenericDevice::getElement(const QString &el)
+ {
+     return dp->findElem(el);
+ }
+
+ bool GenericDevice::setSwitch(const QString &sw, ISState value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::setNumber(const QString &nm, double value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::setText(const QString &tx, QString value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::setLight(const QString &lg, IPState value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::setBLOB(const QString &bb, char *buf, unsigned int size)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processSwitch(const QString &sw, ISState value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processNumber(const QString &nm, double value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processText(const QString &tx, QString value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processLight(const QString &lg, IPState value)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processBLOB(const QString &bb, unsigned char *buffer, int bufferSize, const QString &dataFormat, INDI_D::DTypes dataType)
+ {
+     return true;
+ }
+
+ bool GenericDevice::processNewProperty(INDI_P * pp)
+ {
+     return true;
+ }
+
+} // namespace EkoDevice
+
+#include "genericdevice.moc"
diff --git a/kstars/ekos/genericdevice.h b/kstars/ekos/genericdevice.h
new file mode 100644
index 0000000..289b4a4
--- /dev/null
+++ b/kstars/ekos/genericdevice.h
 <at>  <at>  -0,0 +1,67  <at>  <at> 
+#ifndef EKODEVICE_GENERICDEVICE_H
+#define EKODEVICE_GENERICDEVICE_H
+
+#include <indiapi.h>
+#include <QObject>
+
+#include "indi/indidevice.h"
+
+class QString;
+
+class INDI_P;
+class INDI_E;
+
+namespace EkoDevice
+{
+
+class GenericDevice : public QObject
+{
+    Q_OBJECT
+
+public:
+    GenericDevice(INDI_D *idp);
+
+    virtual bool Connect();
+    virtual bool Disconnect();
+
+    virtual bool setSwitch(const QString &sw, ISState value);
+    virtual bool setNumber(const QString &nm, double value);
+    virtual bool setText(const QString &tx, QString value);
+    virtual bool setLight(const QString &lg, IPState value);
+    virtual bool setBLOB(const QString &bb, char *buf, unsigned int size);
+
+    INDI_D * getDevice() { return dp; }
+    INDI_P * getProperty(const QString &pp);
+    INDI_E * getElement(const QString &el);
+
+    INDI_D *getDP() { return dp; }
+
+public slots:
+    virtual bool processSwitch(const QString &sw, ISState value);
+    virtual bool processNumber(const QString &nm, double value);
+    virtual bool processText(const QString &tx, QString value);
+    virtual bool processLight(const QString &lg, IPState value);
+    virtual bool processBLOB(const  QString &bb, unsigned char *buffer, int bufferSize, const QString &dataFormat, INDI_D::DTypes dataType);
+    virtual bool processNewProperty(INDI_P *p);
+
+signals:
+    void deviceConnected();
+    void deviceDisconnected();
+
+    // We don't emit those directly, they are emitted by INDI_D
+    void newSwitch(const QString &sw, ISState value);
+    void newNumber(const QString &nm, double value);
+    void newText(const QString &tx, QString value);
+    void newLight(const QString &lg, IPState value);
+    void newBLOB(const QString &bb, unsigned char *buffer, int bufferSize, const QString &dataFormat, INDI_D::DTypes dataType);
+    void newProperty(INDI_P *p);
+
+private:
+
+    INDI_D *dp;
+
+};
+
+} // namespace EkoDevice
+
+#endif // EKODEVICE_GENERICDEVICE_H
diff --git a/kstars/ekos/telescope.cpp b/kstars/ekos/telescope.cpp
new file mode 100644
index 0000000..abadf36
--- /dev/null
+++ b/kstars/ekos/telescope.cpp
 <at>  <at>  -0,0 +1,12  <at>  <at> 
+#include "telescope.h"
+
+namespace EkoDevice
+{
+
+Telescope::Telescope(INDI_D *idp) : GenericDevice(idp)
+{
+}
+
+} // namespace EkoDevice
+
+#include "telescope.moc"
diff --git a/kstars/ekos/telescope.h b/kstars/ekos/telescope.h
new file mode 100644
index 0000000..34cb535
--- /dev/null
+++ b/kstars/ekos/telescope.h
 <at>  <at>  -0,0 +1,19  <at>  <at> 
+#ifndef EKODEVICE_TELESCOPE_H
+#define EKODEVICE_TELESCOPE_H
+
+#include "ekos/genericdevice.h"
+
+class INDI_D;
+
+namespace EkoDevice
+{
+
+class Telescope : public GenericDevice
+{
+public:
+    Telescope(INDI_D *idp);
+};
+
+} // namespace EkoDevice
+
+#endif // EKODEVICE_TELESCOPE_H
diff --git a/kstars/indi/devicemanager.cpp b/kstars/indi/devicemanager.cpp
index 68631a5..038fda1 100644
--- a/kstars/indi/devicemanager.cpp
+++ b/kstars/indi/devicemanager.cpp
 <at>  <at>  -93,7 +93,7  <at>  <at>  void DeviceManager::startServer()
     foreach(IDevice *device, managed_devices)
     {
         // JM: Temporary workaround for indiserver limit of client BLOBs for CCDs.
-        if (device->deviceType == KSTARS_CCD)
+        if (device->type == KSTARS_CCD)
         {
                 *serverProcess << "-m" << "100";
                 break;
 <at>  <at>  -118,6 +118,11  <at>  <at>  void DeviceManager::startServer()
     if (mode == DeviceManager::M_LOCAL)
     	connectToServer();
 }
+
+void DeviceManager::stopServer()
+{
+    serverProcess->terminate();
+}
   
 void DeviceManager::connectToServer()
 {
 <at>  <at>  -229,7 +234,8  <at>  <at>  void DeviceManager::dataReceived()
 		     // Silenty ignore property duplication errors
 		     if (err_code != INDI_PROPERTY_DUPLICATED)
 		     {
-	                    kDebug() << "Dispatch command error: " << err_cmd << endl;
+                            //kDebug() << "Dispatch command error: " << err_cmd << endl;
+                            fprintf(stderr, "Dispatch command error: %d for command %s\n", err_code, err_cmd.toStdString().c_str());
 	       	            prXMLEle (stderr, root, 0);
                      }
                 }
 <at>  <at>  -362,6 +368,7  <at>  <at>  INDI_D * DeviceManager::addDevice (XMLEle *dep, QString & errmsg)
     INDI_D *dp;
     XMLAtt *ap;
     QString device_name, unique_label;
+    IDevice *targetDevice=NULL;
 
     /* allocate new INDI_D on indi_dev */
     ap = findAtt (dep, "device", errmsg);
 <at>  <at>  -382,10 +389,11  <at>  <at>  INDI_D * DeviceManager::addDevice (XMLEle *dep, QString & errmsg)
 			// of IDevice because IDevice can have several names. It can have the tree_label which is the name it has in the local tree widget. Finally, the name that shows
 			// up in the INDI control panel is the unique name of the driver, which is for most cases tree_label, but if that exists already then we get tree_label_1..etc
 
-			if (device->driver_class == device_name && device->state == IDevice::DEV_TERMINATE)
+                        if (device->name == device_name && device->state == IDevice::DEV_TERMINATE)
 			{
 	 			device->state = IDevice::DEV_START;
 			        unique_label = device->unique_label = parent->getUniqueDeviceLabel(device->tree_label);
+                                targetDevice = device;
 				break;
 			}
 		}
 <at>  <at>  -394,7 +402,7  <at>  <at>  INDI_D * DeviceManager::addDevice (XMLEle *dep, QString & errmsg)
 	if (unique_label.isEmpty())
 	  unique_label = parent->getUniqueDeviceLabel(device_name);
 
-    	dp = new INDI_D(parent, this, device_name, unique_label);
+        dp = new INDI_D(parent, this, device_name, unique_label, targetDevice);
 	indi_dev.append(dp);
 	emit newDevice(dp);
 
 <at>  <at>  -553,7 +561,7  <at>  <at>  void DeviceManager::sendNewSwitch (INDI_P *pp, INDI_E *lp)
     serverFP << QString("  name='%1'>\n").arg(qPrintable( pp->name));
     serverFP << QString("  <oneSwitch\n");
     serverFP << QString("    name='%1'>\n").arg(qPrintable( lp->name));
-    serverFP << QString("      %1\n").arg(lp->state == PS_ON ? "On" : "Off");
+    serverFP << QString("      %1\n").arg(lp->switch_state == ISS_ON ? "On" : "Off");
     serverFP << QString("  </oneSwitch>\n");
 
     serverFP <<  QString("</newSwitchVector>\n");
diff --git a/kstars/indi/devicemanager.h b/kstars/indi/devicemanager.h
index 2ff6e31..79e2144 100644
--- a/kstars/indi/devicemanager.h
+++ b/kstars/indi/devicemanager.h
 <at>  <at>  -76,6 +76,7  <at>  <at>  public:
 
     void appendManagedDevices(QList<IDevice *> & processed_devices);
     void startServer();
+    void stopServer();
     void connectToServer();
     void enableBLOB(bool enable, QString device = QString(), QString property = QString());
 
diff --git a/kstars/indi/imagesequence.cpp b/kstars/indi/imagesequence.cpp
index 46609b3..29d04fd 100644
--- a/kstars/indi/imagesequence.cpp
+++ b/kstars/indi/imagesequence.cpp
 <at>  <at>  -36,15 +36,15  <at>  <at> 
 imagesequence::imagesequence(QWidget* parent): QDialog(parent)
 {
     ksw = (KStars *) parent;
-    INDIMenu *devMenu = ksw->indiMenu();
+    //INDIMenu *devMenu = ksw->indiMenu();
 
     setupUi(this);
 
-    if (devMenu)
+    /*if (devMenu)
     {
         connect (devMenu, SIGNAL(newDevice()), this, SLOT(newCCD()));
         connect (devMenu, SIGNAL(newDevice()), this, SLOT(newFilter()));
-    }
+    }*/
 
     seqTimer = new QTimer(this);
 
 <at>  <at>  -113,7 +113,7  <at>  <at>  bool imagesequence::setupCCDs()
     {
         for (int j=0; j < devMenu->managers.at(i)->indi_dev.size(); j++)
         {
-            imgProp = devMenu->managers.at(i)->indi_dev.at(j)->findProp("CCD_EXPOSURE");
+            imgProp = devMenu->managers.at(i)->indi_dev.at(j)->findProp("CCD_EXPOSURE_REQUEST");
             if (!imgProp)
                 continue;
 
 <at>  <at>  -147,17 +147,17  <at>  <at>  bool imagesequence::setupCCDs()
         INDI_P *exposeProp;
         INDI_E *exposeElem;
 
-        exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSURE");
+        exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSURE_REQUEST");
         if (!exposeProp)
         {
-            KMessageBox::error(this, i18n("Device does not support CCD_EXPOSURE property."));
+            KMessageBox::error(this, i18n("Device does not support CCD_EXPOSURE_REQUEST property."));
             return false;
         }
 
         exposeElem = exposeProp->findElement("CCD_EXPOSURE_VALUE");
         if (!exposeElem)
         {
-            KMessageBox::error(this, i18n("CCD_EXPOSURE property is missing CCD_EXPOSURE_VALUE element."));
+            KMessageBox::error(this, i18n("CCD_EXPOSURE_REQUEST property is missing CCD_EXPOSURE_VALUE element."));
             return false;
         }
 
 <at>  <at>  -393,17 +393,17  <at>  <at>  bool imagesequence::verifyCCDIntegrity()
     }
 
     stdDevCCD = idevice->stdDev;
-    exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSURE");
+    exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSURE_REQUEST");
     if (!exposeProp)
     {
-        KMessageBox::error(this, i18n("Device does not support CCD_EXPOSURE property."));
+        KMessageBox::error(this, i18n("Device does not support CCD_EXPOSURE_REQUEST property."));
         return false;
     }
 
     exposeElem = exposeProp->findElement("CCD_EXPOSURE_VALUE");
     if (!exposeElem)
     {
-        KMessageBox::error(this, i18n("CCD_EXPOSURE property is missing CCD_EXPOSURE_VALUE element."));
+        KMessageBox::error(this, i18n("CCD_EXPOSURE_REQUEST property is missing CCD_EXPOSURE_VALUE element."));
         return false;
     }
 
 <at>  <at>  -533,7 +533,7  <at>  <at>  void imagesequence::captureImage()
     seqTimer->stop();
 
     // Make sure it's not busy, if it is then schedual.
-    if (exposeProp->state == PS_BUSY)
+    if (exposeProp->state == IPS_BUSY)
     {
         retries++;
 
 <at>  <at>  -550,7 +550,7  <at>  <at>  void imagesequence::captureImage()
     }
 
     // Set duration if applicable. We check the property permission, min, and max values
-    if (exposeProp->perm == PP_RW || exposeProp->perm == PP_WO)
+    if (exposeProp->perm == IP_RW || exposeProp->perm == IP_WO)
     {
         if (seqExpose < exposeElem->min || seqExpose > exposeElem->max)
         {
 <at>  <at>  -646,7 +646,7  <at>  <at>  void imagesequence::selectFilter()
         return;
     }
 
-    if (filterProp && (filterProp->perm == PP_RW || filterProp->perm == PP_WO))
+    if (filterProp && (filterProp->perm == IP_RW || filterProp->perm == IP_WO))
     {
         filterElem->targetValue = filterPosCombo->currentIndex();
         if (filterElem->spin_w)
diff --git a/kstars/indi/indidevice.cpp b/kstars/indi/indidevice.cpp
index 60c6437..5bc5e83 100644
--- a/kstars/indi/indidevice.cpp
+++ b/kstars/indi/indidevice.cpp
 <at>  <at>  -85,12 +85,13  <at>  <at>  const char * indi_std[NINDI_STD] =
 ** INDI Device: The work-horse. Responsible for handling its
 ** child properties and managing signal and changes.
 *******************************************************************/
-INDI_D::INDI_D(INDIMenu *menuParent, DeviceManager *InParentManager, const QString &inName, const QString &inLabel) : KDialog( 0 )
+INDI_D::INDI_D(INDIMenu *menuParent, DeviceManager *InParentManager, const QString &inName, const QString &inLabel, IDevice *dv) : KDialog( 0 )
   {
     name      		= inName;
     label     		= inLabel;
     parent		= menuParent;
     deviceManager 	= InParentManager;
+    deviceDriver        = dv;
   
     deviceVBox     	= new QSplitter();
     deviceVBox->setOrientation(Qt::Vertical);
 <at>  <at>  -130,9 +131,12  <at>  <at>  void INDI_D::registerProperty(INDI_P *pp)
 {
 
     if (isINDIStd(pp))
+    {
         pp->pg->dp->INDIStdSupport = true;
+        stdDev->registerProperty(pp);
+    }
 
-    stdDev->registerProperty(pp);
+    newProperty(pp);
 
 }
 
 <at>  <at>  -289,12 +293,14  <at>  <at>  int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, QString & errmsg)
         //fprintf(stderr, "tag okay, getting perm\n");
         switch (pp->perm)
         {
-        case PP_RW:	// FALLTHRU
-        case PP_RO:
+        case IP_RW:	// FALLTHRU
+        case IP_RO:
             if (pp->guitype == PG_TEXT)
             {
                 lp->text = QString(pcdataXMLEle(ep));
                 lp->read_w->setText(lp->text);
+
+                newText(lp->name, lp->text);
             }
             else if (pp->guitype == PG_NUMERIC)
             {
 <at>  <at>  -315,24 +321,14  <at>  <at>  int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, QString & errmsg)
                 lp->spinChanged(lp->value);
                 }*/
 
+                newNumber(lp->name, lp->value);
+
             }
             break;
 
-        case PP_WO:
-            // FIXME for WO properties, only min/max needs to be updated
-            /* if (pp->guitype == PG_TEXT)
-               lp->write_w->setText(QString(pcdataXMLEle(ep)));
-             else*/ if (pp->guitype == PG_NUMERIC)
+        case IP_WO:
+            if (pp->guitype == PG_NUMERIC)
             {
-                /*lp->value = atof(pcdataXMLEle(ep));
-                numberFormat(iNumber, lp->format.toAscii(), lp->value);
-                lp->text = iNumber;
-
-                if (lp->spin_w)
-                         lp->spin_w->setValue(lp->value);
-                else
-                   lp->write_w->setText(lp->text);*/
-
                 ap = findXMLAtt (ep, "min");
                 if (ap) { min = (int) atof(valuXMLAtt(ap)); lp->setMin(min); }
                 ap = findXMLAtt (ep, "max");
 <at>  <at>  -366,7 +362,8  <at>  <at>  int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, QString & errmsg)
     XMLAtt *ap;
     INDI_E *lp = NULL;
     int islight;
-    PState state;
+    IPState light_state;
+    ISState switch_state;
 
     /* for each child element */
     for (ep = nextXMLEle (root, 1), i=0; ep != NULL; ep = nextXMLEle (root, 0), i++)
 <at>  <at>  -385,8 +382,8  <at>  <at>  int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, QString & errmsg)
             return (-1);
         }
 
-        if ((islight && crackLightState (pcdataXMLEle(ep), &state) < 0)
-                || (!islight && crackSwitchState (pcdataXMLEle(ep), &state) < 0))
+        if ((islight && crackLightState (pcdataXMLEle(ep), &light_state) < 0)
+                || (!islight && crackSwitchState (pcdataXMLEle(ep), &switch_state) < 0))
         {
             errmsg = QString("INDI: <%1> unknown state %2 for %3 %4 %5").arg(tagXMLEle(root)).arg(pcdataXMLEle(ep)).arg(name).arg(pp->name).arg(tagXMLEle(ep));
             return (-1);
 <at>  <at>  -404,7 +401,10  <at>  <at>  int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, QString & errmsg)
 
         QFont buttonFont;
         /* engage new state */
-        lp->state = state;
+        if (islight)
+            lp->light_state = light_state;
+        else
+            lp->switch_state = switch_state;
 
         switch (pp->guitype)
         {
 <at>  <at>  -412,18 +412,22  <at>  <at>  int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, QString & errmsg)
             if (islight)
                 break;
 
-            lp->push_w->setDown(state == PS_ON ? true : false);
+            lp->push_w->setDown(switch_state == ISS_ON ? true : false);
             buttonFont = lp->push_w->font();
-            buttonFont.setBold(state == PS_ON ? true : false);
+            buttonFont.setBold(switch_state == ISS_ON ? true : false);
             lp->push_w->setFont(buttonFont);
 
+            newSwitch(lp->name, switch_state);
+
             break;
 
         case PG_RADIO:
-            lp->check_w->setChecked(state == PS_ON ? true : false);
+            lp->check_w->setChecked(switch_state == ISS_ON ? true : false);
+            newSwitch(lp->name, switch_state);
             break;
+
         case PG_MENU:
-            if (state == PS_ON)
+            if (switch_state == ISS_ON)
             {
                 if (menuChoice)
                 {
 <at>  <at>  -433,10 +437,12  <at>  <at>  int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, QString & errmsg)
                 menuChoice = 1;
                 pp->om_w->setCurrentIndex(i);
             }
+            newSwitch(lp->name, switch_state);
             break;
 
         case PG_LIGHTS:
             lp->drawLt();
+            newLight(lp->name, light_state);
             break;
 
         default:
 <at>  <at>  -579,17 +585,19  <at>  <at>  int INDI_D::processBlob(INDI_E *blobEL, XMLEle *ep, QString & errmsg)
         memcpy(dataBuffer, blobBuffer, dataSize);
     }
 
-    if (dataType == ASCII_DATA_STREAM && blobEL->pp->state != PS_BUSY)
+    if (dataType == ASCII_DATA_STREAM && blobEL->pp->state != IPS_BUSY)
     {
         stdDev->asciiFileDirty = true;
 
-        if (blobEL->pp->state == PS_IDLE)
+        if (blobEL->pp->state == IPS_IDLE)
         {
             free (blobBuffer);
             return(0);
         }
     }
 
+    newBLOB(blobEL->name, dataBuffer, dataSize, dataFormat, dataType);
+
     stdDev->handleBLOB(dataBuffer, dataSize, dataFormat, dataType);
 
     free (blobBuffer);
 <at>  <at>  -721,7 +729,7  <at>  <at>  INDI_G *  INDI_D::findGroup (const QString &grouptag,
  * return 0 if ok else -1 with excuse in errmsg[]
  */
 
-int INDI_D::findPerm (INDI_P *pp, XMLEle *root, PPerm *permp, QString & errmsg)
+int INDI_D::findPerm (INDI_P *pp, XMLEle *root, IPerm *permp, QString & errmsg)
 {
     XMLAtt *ap;
 
 <at>  <at>  -732,11 +740,11  <at>  <at>  int INDI_D::findPerm (INDI_P *pp, XMLEle *root, PPerm *permp, QString & errmsg)
         return (-1);
     }
     if (!strcmp(valuXMLAtt(ap), "ro") || !strcmp(valuXMLAtt(ap), "r"))
-        *permp = PP_RO;
+        *permp = IP_RO;
     else if (!strcmp(valuXMLAtt(ap), "wo"))
-        *permp = PP_WO;
+        *permp = IP_WO;
     else if (!strcmp(valuXMLAtt(ap), "rw") || !strcmp(valuXMLAtt(ap), "w"))
-        *permp = PP_RW;
+        *permp = IP_RW;
     else {
         errmsg = QString("INDI: <%1> unknown perm %2 for %3 %4").arg(tagXMLEle(root)).arg(valuXMLAtt(ap)).arg(pp->pg->dp->name).arg(pp->name);
         return (-1);
 <at>  <at>  -748,20 +756,20  <at>  <at>  int INDI_D::findPerm (INDI_P *pp, XMLEle *root, PPerm *permp, QString & errmsg)
 /* convert the given light/property state string to the PState at psp.
  * return 0 if successful, else -1 and leave *psp unchanged.
  */
-int INDI_D::crackLightState (char *name, PState *psp)
+int INDI_D::crackLightState (char *name, IPState *psp)
 {
     typedef struct
     {
-        PState s;
+        IPState s;
         const char *name;
     } PSMap;
 
     PSMap psmap[] =
         {
-            {PS_IDLE,  "Idle"},
-            {PS_OK,    "Ok"},
-            {PS_BUSY,  "Busy"},
-            {PS_ALERT, "Alert"},
+            {IPS_IDLE,  "Idle"},
+            {IPS_OK,    "Ok"},
+            {IPS_BUSY,  "Busy"},
+            {IPS_ALERT, "Alert"},
         };
 
     for (int i = 0; i < 4; i++)
 <at>  <at>  -776,18 +784,18  <at>  <at>  int INDI_D::crackLightState (char *name, PState *psp)
 /* convert the given switch state string to the PState at psp.
  * return 0 if successful, else -1 and leave *psp unchanged.
  */
-int INDI_D::crackSwitchState (char *name, PState *psp)
+int INDI_D::crackSwitchState (char *name, ISState *psp)
 {
     typedef struct
     {
-        PState s;
+        ISState s;
         const char *name;
     } PSMap;
 
     PSMap psmap[] =
         {
-            {PS_ON,  "On"},
-            {PS_OFF, "Off"},
+            {ISS_ON,  "On"},
+            {ISS_OFF, "Off"},
         };
 
 
 <at>  <at>  -805,7 +813,7  <at>  <at>  int INDI_D::buildTextGUI(XMLEle *root, QString & errmsg)
 {
     INDI_P *pp = NULL;
     int err_code=0;
-    PPerm p;
+    IPerm p;
     bool isGroupVisible=false;
 
     /* build a new property */
 <at>  <at>  -852,7 +860,7  <at>  <at>  int INDI_D::buildNumberGUI (XMLEle *root, QString & errmsg)
 {
     INDI_P *pp = NULL;
     int err_code=0;
-    PPerm p;
+    IPerm p;
     bool isGroupVisible=false;
 
     /* build a new property */
 <at>  <at>  -1040,7 +1048,7  <at>  <at>  int INDI_D::buildBLOBGUI  (XMLEle *root, QString & errmsg)
 {
     INDI_P *pp;
     int err_code=0;
-    PPerm p;
+    IPerm p;
     bool isGroupVisible=false;
 
     // build a new property
diff --git a/kstars/indi/indidevice.h b/kstars/indi/indidevice.h
index 009a69d..2b434e7 100644
--- a/kstars/indi/indidevice.h
+++ b/kstars/indi/indidevice.h
 <at>  <at>  -28,6 +28,7  <at>  <at>  class INDI_G;
 class INDI_E;
 class INDIMenu;
 class INDIStdDevice;
+class IDevice;
 
 
 class QLabel;
 <at>  <at>  -54,13 +55,14  <at>  <at>  class QSplitter;
                                           Device Manager  INDI Menu
 **************************************************************************/
 
+#include <indiapi.h>
 
 /* INDI device */
 class INDI_D : public KDialog
 {
     Q_OBJECT
 public:
-    INDI_D(INDIMenu *parentMenu, DeviceManager *InParentManager, const QString &inName, const QString &inLabel);
+    INDI_D(INDIMenu *parentMenu, DeviceManager *InParentManager, const QString &inName, const QString &inLabel, IDevice *dv);
     ~INDI_D();
 
     QString 	name;			/* device name */
 <at>  <at>  -80,6 +82,7  <at>  <at>  public:
 
     INDIMenu      *parent;
     DeviceManager *deviceManager;
+    IDevice       *deviceDriver;
 
     enum DTypes { DATA_FITS, ASCII_DATA_STREAM, VIDEO_STREAM, DATA_OTHER, DATA_CCDPREVIEW };
 
 <at>  <at>  -104,7 +107,7  <at>  <at>  public:
     INDI_P *   findProp    (const QString &name);
     INDI_E *   findElem    (const QString &name);
     INDI_G *   findGroup   (const QString &grouptag, int create, QString & errmsg);
-    int        findPerm    (INDI_P *pp  , XMLEle *root, PPerm *permp, QString & errmsg);
+    int        findPerm    (INDI_P *pp  , XMLEle *root, IPerm *permp, QString & errmsg);
 
     /*****************************************************************
     * Set/New
 <at>  <at>  -125,8 +128,8  <at>  <at>  public:
     /*****************************************************************
     * Crack
     ******************************************************************/
-    int crackLightState  (char *name, PState *psp);
-    int crackSwitchState (char *name, PState *psp);
+    int crackLightState  (char *name, IPState *psp);
+    int crackSwitchState (char *name, ISState *psp);
 
     /*****************************************************************
     * Data processing
 <at>  <at>  -143,6 +146,14  <at>  <at>  public:
 public slots:
     void engageTracking();
 
+signals:
+    void newSwitch(const QString &sw, ISState value);
+    void newNumber(const QString &nm, double value);
+    void newText(const QString &tx, QString value);
+    void newLight(const QString &lg, IPState value);
+    void newBLOB(const QString &bb, unsigned char *buffer, int bufferSize, const QString &dataFormat, INDI_D::DTypes dataType);
+    void newProperty(INDI_P *p);
+
 };
 
 #endif
diff --git a/kstars/indi/indidriver.cpp b/kstars/indi/indidriver.cpp
index 6982812..f972774 100644
--- a/kstars/indi/indidriver.cpp
+++ b/kstars/indi/indidriver.cpp
 <at>  <at>  -393,7 +393,6  <at>  <at>  void INDIDriver::newCCDDiscovered()
 {
 
     emit newCCD();
-
 }
 
 void INDIDriver::resizeDeviceColumn()
 <at>  <at>  -710,7 +709,7  <at>  <at>  bool INDIDriver::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, int g
         driversList.insert(driver, name);
 
     dv = new IDevice(name, label, driver, version);
-    dv->deviceType = groupType;
+    dv->type = groupType;
     dv->xmlSource = xmlSource;
     //connect(dv, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
     if (focal_length > 0)
 <at>  <at>  -768,7 +767,7  <at>  <at>  void INDIDriver::updateCustomDrivers()
             lastDevice = device;
 
             dv = new IDevice(name, label, driver, version);
-            dv->deviceType = KSTARS_TELESCOPE;
+            dv->type = KSTARS_TELESCOPE;
             dv->xmlSource = IDevice::EM_XML;
             dv->focal_length = focal_length;
             dv->aperture = aperture;
 <at>  <at>  -982,7 +981,7  <at>  <at>  IDevice::IDevice(const QString &inName, const QString &inLabel, const QString &i
 {
     tree_label	 = inLabel;
     unique_label.clear();
-    driver_class = inName;
+    name = inName;
     driver	 = inDriver;
     version	 = inVersion;
 
diff --git a/kstars/indi/indidriver.h b/kstars/indi/indidriver.h
index ce3211b..ea517ab 100644
--- a/kstars/indi/indidriver.h
+++ b/kstars/indi/indidriver.h
 <at>  <at>  -56,7 +56,7  <at>  <at>  public:
 
     QString tree_label;
     QString unique_label;
-    QString driver_class;
+    QString name;
     QString driver;
     QString version;
     QString id;
 <at>  <at>  -65,7 +65,7  <at>  <at>  public:
     XMLSource xmlSource;
 
     DeviceManager *deviceManager;
-    int deviceType;
+    int type;
   
       /* Telescope specific attributes */
       double focal_length;
diff --git a/kstars/indi/indielement.cpp b/kstars/indi/indielement.cpp
index f28f2be..330f4e0 100644
--- a/kstars/indi/indielement.cpp
+++ b/kstars/indi/indielement.cpp
 <at>  <at>  -140,17 +140,17  <at>  <at>  int INDI_E::buildTextGUI(const QString &initText)
 
     switch (pp->perm)
     {
-    case PP_RW:
+    case IP_RW:
         setupElementRead(ELEMENT_READ_WIDTH);
         setupElementWrite(ELEMENT_WRITE_WIDTH);
 
         break;
 
-    case PP_RO:
+    case IP_RO:
         setupElementRead(ELEMENT_FULL_WIDTH);
         break;
 
-    case PP_WO:
+    case IP_WO:
         setupElementWrite(ELEMENT_FULL_WIDTH);
         break;
     }
 <at>  <at>  -169,18 +169,18  <at>  <at>  int INDI_E::buildBLOBGUI()
 
     switch (pp->perm)
     {
-    case PP_RW:
+    case IP_RW:
         setupElementRead(ELEMENT_READ_WIDTH);
         setupElementWrite(ELEMENT_WRITE_WIDTH);
         setupBrowseButton();
 
         break;
 
-    case PP_RO:
+    case IP_RO:
         setupElementRead(ELEMENT_FULL_WIDTH);
         break;
 
-    case PP_WO:
+    case IP_WO:
         setupElementWrite(ELEMENT_FULL_WIDTH);
         setupBrowseButton();
         break;
 <at>  <at>  -207,7 +207,7  <at>  <at>  int INDI_E::buildNumberGUI  (double initValue)
 
     switch (pp->perm)
     {
-    case PP_RW:
+    case IP_RW:
         setupElementRead(ELEMENT_READ_WIDTH);
         if (scale)
             setupElementScale(ELEMENT_WRITE_WIDTH);
 <at>  <at>  -217,12 +217,12  <at>  <at>  int INDI_E::buildNumberGUI  (double initValue)
         pp->PVBox->addLayout(EHBox);
         break;
 
-    case PP_RO:
+    case IP_RO:
         setupElementRead(ELEMENT_READ_WIDTH);
         pp->PVBox->addLayout(EHBox);
         break;
 
-    case PP_WO:
+    case IP_WO:
         if (scale)
             setupElementScale(ELEMENT_FULL_WIDTH);
         else
 <at>  <at>  -257,21 +257,21  <at>  <at>  int INDI_E::buildLightGUI()
 void INDI_E::drawLt()
 {
     /* set state light */
-    switch (state)
+    switch (light_state)
     {
-    case PS_IDLE:
+    case IPS_IDLE:
         led_w->setColor(Qt::gray);
         break;
 
-    case PS_OK:
+    case IPS_OK:
         led_w->setColor(Qt::green);
         break;
 
-    case PS_BUSY:
+    case IPS_BUSY:
         led_w->setColor(Qt::yellow);
         break;
 
-    case PS_ALERT:
+    case IPS_ALERT:
         led_w->setColor(Qt::red);
         break;
 
 <at>  <at>  -465,7 +465,7  <at>  <at>  void INDI_E::actionTriggered()
         // Just issue a new generic switch.
         if (pp->indistd->actionTriggered(this))
             return;
-        else if (state == PS_OFF)
+        else if (switch_state == ISS_OFF)
             pp->newSwitch(this);
         break;
 
diff --git a/kstars/indi/indielement.h b/kstars/indi/indielement.h
index 2d7e81c..cd05717 100644
--- a/kstars/indi/indielement.h
+++ b/kstars/indi/indielement.h
 <at>  <at>  -13,7 +13,7  <at>  <at> 
 #define INDIELEMENT_H_
 
 #include <lilxml.h>
-
+#include <indiapi.h>
 
 #include <kdialog.h>
 #include <unistd.h>
 <at>  <at>  -40,13 +40,6  <at>  <at> 
 // Pulse tracking
 #define INDI_PULSE_TRACKING   15000
 
-/* decoded elements.
- * lights use PState, TB's use the alternate binary names.
- */
-typedef enum {PS_IDLE = 0, PS_OK, PS_BUSY, PS_ALERT, PS_N} PState;
-#define	PS_OFF	PS_IDLE		/* alternate name */
-#define	PS_ON	PS_OK		/* alternate name */
-typedef enum {PP_RW = 0, PP_WO, PP_RO} PPerm;
 typedef enum {PG_NONE = 0, PG_TEXT, PG_NUMERIC, PG_BUTTONS,
               PG_RADIO, PG_MENU, PG_LIGHTS, PG_BLOB} PGui;
 
 <at>  <at>  -97,7 +90,8  <at>  <at>  public:
     ~INDI_E();
     QString name;			/* name */
     QString label;			/* label is the name by default, unless specified */
-    PState state;			/* control on/off t/f etc */
+    IPState light_state;		/* control light state */
+    ISState switch_state;		/* control switch state */
     INDI_P *pp;				/* parent property */
 
     QHBoxLayout    *EHBox;   		/* Horizontal layout */
diff --git a/kstars/indi/indiproperty.cpp b/kstars/indi/indiproperty.cpp
index f9acf40..8c59aca 100644
--- a/kstars/indi/indiproperty.cpp
+++ b/kstars/indi/indiproperty.cpp
 <at>  <at>  -102,7 +102,7  <at>  <at>  bool INDI_P::isOn(const QString &component)
     if (lp->check_w && lp->check_w->isChecked())
         return true;
 
-    if (lp->push_w && lp->state == PS_ON)
+    if (lp->push_w && lp->switch_state == ISS_ON)
         return true;
 
     return false;
 <at>  <at>  -117,28 +117,28  <at>  <at>  INDI_E * INDI_P::findElement(const QString &elementName)
     return NULL;
 }
 
-void INDI_P::drawLt(PState lstate)
+void INDI_P::drawLt(IPState lstate)
 {
 
 
     /* set state light */
     switch (lstate)
     {
-    case PS_IDLE:
+    case IPS_IDLE:
         light->setColor(Qt::gray);
         break;
 
-    case PS_OK:
+    case IPS_OK:
         light->setColor(Qt::green);
         emit okState();
         disconnect( this, SIGNAL(okState()), 0, 0 );
         break;
 
-    case PS_BUSY:
+    case IPS_BUSY:
         light->setColor(Qt::yellow);
         break;
 
-    case PS_ALERT:
+    case IPS_ALERT:
         light->setColor(Qt::red);
         break;
 
 <at>  <at>  -162,7 +162,7  <at>  <at>  void INDI_P::newText()
         {
             switch (perm)
             {
-            case PP_RW:
+            case IP_RW:
                 // FIXME is this problematic??
                 if (lp->write_w->text().isEmpty())
                     lp->text = lp->read_w->text();
 <at>  <at>  -170,10 +170,10  <at>  <at>  void INDI_P::newText()
                     lp->text = lp->write_w->text();
                 break;
 
-            case PP_RO:
+            case IP_RO:
                 break;
 
-            case PP_WO:
+            case IP_WO:
                 // Ignore if it's empty
                 if (lp->write_w->text().isEmpty())
                     return;
 <at>  <at>  -194,7 +194,7  <at>  <at>  void INDI_P::newText()
         }
     }
 
-    state = PS_BUSY;
+    state = IPS_BUSY;
 
     drawLt(state);
 
 <at>  <at>  -236,18 +236,19  <at>  <at>  void INDI_P::newSwitch(INDI_E *lp)
 {
     QFont buttonFont;
 
-    assert(lp != NULL);
+    if (lp == NULL)
+        return;
 
     switch (guitype)
     {
     case PG_MENU:
-        if (lp->state == PS_ON)
+        if (lp->switch_state == ISS_ON)
             return;
 
         foreach( INDI_E *elm, el)
-        elm->state = PS_OFF;
+            elm->switch_state = ISS_OFF;
 
-        lp->state = PS_ON;
+        lp->switch_state = ISS_ON;
         break;
 
     case PG_BUTTONS:
 <at>  <at>  -261,27 +262,27  <at>  <at>  void INDI_P::newSwitch(INDI_E *lp)
             buttonFont = elm->push_w->font();
             buttonFont.setBold(false);
             elm->push_w->setFont(buttonFont);
-            elm->state = PS_OFF;
+            elm->switch_state = ISS_OFF;
         }
 
         lp->push_w->setDown(true);
         buttonFont = lp->push_w->font();
         buttonFont.setBold(true);
         lp->push_w->setFont(buttonFont);
-        lp->state = PS_ON;
+        lp->switch_state = ISS_ON;
 
         break;
 
     case PG_RADIO:
-        lp->state = lp->state == PS_ON ? PS_OFF : PS_ON;
-        lp->check_w->setChecked(lp->state == PS_ON);
+        lp->switch_state = (lp->switch_state == ISS_ON ? ISS_OFF : ISS_ON);
+        lp->check_w->setChecked(lp->switch_state == ISS_ON);
         break;
 
     default:
         break;
 
     }
-    state = PS_BUSY;
+    state = IPS_BUSY;
 
     drawLt(state);
 
 <at>  <at>  -372,9 +373,9  <at>  <at>  void INDI_P::newBlob()
         pg->dp->deviceManager->finishBlob();
 
     if (valid)
-        state = PS_BUSY;
+        state = IPS_BUSY;
     else
-        state = PS_ALERT;
+        state = IPS_ALERT;
 
     drawLt(state);
 }
 <at>  <at>  -476,7 +477,7  <at>  <at>  int INDI_P::buildTextGUI(XMLEle *root, QString & errmsg)
 
     }
 
-    if (perm == PP_RO)
+    if (perm == IP_RO)
         return 0;
 
     // INDI STD, but we use our own controls
 <at>  <at>  -562,7 +563,7  <at>  <at>  int INDI_P::buildNumberGUI  (XMLEle *root, QString & errmsg)
 
     setlocale(LC_NUMERIC,"");
 
-    if (perm == PP_RO)
+    if (perm == IP_RO)
         return 0;
 
 
 <at>  <at>  -638,7 +639,7  <at>  <at>  int INDI_P::buildMenuGUI(XMLEle *root, QString & errmsg)
 
         lp = new INDI_E(this, switchName, switchLabel);
 
-        if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0)
+        if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->switch_state)) < 0)
         {
             errmsg = QString("INDI: <%1> unknown state %2 for %3 %4 %5").arg(tagXMLEle(root)).arg(valuXMLAtt(ap)).arg(name).arg(lp->name).arg(name);
             return DeviceManager::INDI_PROPERTY_INVALID;
 <at>  <at>  -646,7 +647,7  <at>  <at>  int INDI_P::buildMenuGUI(XMLEle *root, QString & errmsg)
 
         menuOptions.append(switchLabel);
 
-        if (lp->state == PS_ON)
+        if (lp->switch_state == ISS_ON)
         {
             if (onItem != -1)
             {
 <at>  <at>  -720,7 +721,7  <at>  <at>  int INDI_P::buildSwitchesGUI(XMLEle *root, QString & errmsg)
 
         lp = new INDI_E(this, switchName, switchLabel);
 
-        if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0)
+        if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->switch_state)) < 0)
         {
             errmsg = QString("INDI: <%1> unknown state %2 for %3 %4 %5").arg(tagXMLEle(root)).arg(valuXMLAtt(ap)).arg(name).arg(name).arg(lp->name);
             return DeviceManager::INDI_PROPERTY_INVALID;
 <at>  <at>  -737,7 +738,7  <at>  <at>  int INDI_P::buildSwitchesGUI(XMLEle *root, QString & errmsg)
             //groupB->insert(button, j);
             groupB->addButton(button);
 
-            if (lp->state == PS_ON)
+            if (lp->switch_state == ISS_ON)
             {
                 button->setDown(true);
                 buttonFont = button->font();
 <at>  <at>  -758,7 +759,7  <at>  <at>  int INDI_P::buildSwitchesGUI(XMLEle *root, QString & errmsg)
             //groupB->insert(checkbox, j);
             groupB->addButton(button);
 
-            if (lp->state == PS_ON)
+            if (lp->switch_state == ISS_ON)
                 checkbox->setChecked(true);
 
             lp->check_w = checkbox;
 <at>  <at>  -818,7 +819,7  <at>  <at>  int INDI_P::buildLightsGUI(XMLEle *root, QString & errmsg)
 
         lp = new INDI_E(this, sname, slabel);
 
-        if (pg->dp->crackLightState (pcdataXMLEle(lep), &lp->state) < 0)
+        if (pg->dp->crackLightState (pcdataXMLEle(lep), &lp->light_state) < 0)
         {
             errmsg = QString("INDI: <%1> unknown state %2 for %3 %4 %5").arg(tagXMLEle(root)).arg(valuXMLAtt(ap)).arg(pg->dp->name).arg(name).arg(sname);
             return DeviceManager::INDI_PROPERTY_INVALID;
 <at>  <at>  -891,7 +892,7  <at>  <at>  int INDI_P::buildBLOBGUI(XMLEle *root, QString & errmsg)
 
     connect(enableBLOBC, SIGNAL(stateChanged(int)), this, SLOT(setBLOBOption(int)));
 
-    if (perm != PP_RO)
+    if (perm != IP_RO)
     {
         setupSetButton(i18n("Upload"));
         QObject::connect(set_w, SIGNAL(clicked()), this, SLOT(newBlob()));
diff --git a/kstars/indi/indiproperty.h b/kstars/indi/indiproperty.h
index 7f951c0..b2974f1 100644
--- a/kstars/indi/indiproperty.h
+++ b/kstars/indi/indiproperty.h
 <at>  <at>  -47,9 +47,9  <at>  <at>  public:
     INDI_G	*pg;			/* parent group */
     INDIStdProperty *indistd;		/* Associated std routines class */
     double	timeout;		/* timeout, seconds */
-    PState	state;			/* state light code */
+    IPState	state;			/* state light code */
     KLed	*light;			/* state LED */
-    PPerm       perm;		        /* permissions wrt client */
+    IPerm       perm;		        /* permissions wrt client */
     PGui        guitype;		/* type of GUI, if any */
     QCheckBox    *enableBLOBC;
 
 <at>  <at>  -69,7 +69,7  <at>  <at>  public:
     QList<INDI_E*> el;		/* list of elements */
 
     /* Draw state LED */
-    void drawLt(PState lstate);
+    void drawLt(IPState lstate);
 
     /* First step in adding a new GUI element */
     void addGUI(XMLEle *root);
diff --git a/kstars/indi/indistd.cpp b/kstars/indi/indistd.cpp
index 1faaa48..8b4122c 100644
--- a/kstars/indi/indistd.cpp
+++ b/kstars/indi/indistd.cpp
 <at>  <at>  -306,8 +306,9  <at>  <at>  void INDIStdDevice::setTextValue(INDI_P *pp)
         break;
 
     case CCD_EXPOSURE_REQUEST:
-        if (pp->state == PS_IDLE || pp->state == PS_OK)
+        if (pp->state == IPS_IDLE || pp->state == IPS_OK)
             pp->set_w->setText(i18n("Capture"));
+
         break;
 
     case CCD_FRAME:
 <at>  <at>  -380,7 +381,7  <at>  <at>  void INDIStdDevice::setLabelState(INDI_P *pp)
         lp = pp->findElement("CONNECT");
         if (!lp) return;
 
-        if (lp->state == PS_ON)
+        if (lp->switch_state == ISS_ON)
         {
             createDeviceInit();
             emit linkAccepted();
 <at>  <at>  -418,7 +419,7  <at>  <at>  void INDIStdDevice::setLabelState(INDI_P *pp)
     case VIDEO_STREAM:
         lp = pp->findElement("ON");
         if (!lp) return;
-        if (lp->state == PS_ON)
+        if (lp->switch_state == ISS_ON)
             streamWindow->enableStream(true);
         else
             streamWindow->enableStream(false);
 <at>  <at>  -444,7 +445,7  <at>  <at>  void INDIStdDevice::streamDisabled()
     el = pp->findElement("OFF");
     if (!el) return;
 
-    if (el->state == PS_ON)
+    if (el->switch_state == ISS_ON)
         return;
 
     // Turn stream off
 <at>  <at>  -636,14 +637,14  <at>  <at>  void INDIStdDevice::registerProperty(INDI_P *pp)
             {
                 if (device->deviceManager == dp->deviceManager)
                 {
-                    if (device->deviceType == KSTARS_TELESCOPE)
+                    if (device->type == KSTARS_TELESCOPE)
                     {
                         portEle->read_w->setText( Options::telescopePort() );
                         portEle->write_w->setText( Options::telescopePort() );
                         portEle->text = Options::telescopePort();
                         break;
                     }
-                    else if (device->deviceType == KSTARS_VIDEO)
+                    else if (device->type == KSTARS_VIDEO)
                     {
                         portEle->read_w->setText( Options::videoPort() );
                         portEle->write_w->setText( Options::videoPort() );
 <at>  <at>  -661,9 +662,14  <at>  <at>  void INDIStdDevice::registerProperty(INDI_P *pp)
         emit newTelescope();
         break;
 
-        // Update Device menu actions
+    case CCD_EXPOSURE_REQUEST:
         drivers->updateMenuActions();
+        break;
+
+
     }
+
+
 }
 
 /*********************************************************************************/
 <at>  <at>  -752,7 +758,7  <at>  <at>  bool INDIStdDevice::handleNonSidereal()
 
                 /* Send object name if available */
                 nameEle = dp->findElem("OBJECT_NAME");
-                if (nameEle && nameEle->pp->perm != PP_RO)
+                if (nameEle && nameEle->pp->perm != IP_RO)
                 {
                     nameEle->write_w->setText(currentObject->name());
                     nameEle->pp->newText();
 <at>  <at>  -811,7 +817,7  <at>  <at>  void INDIStdDevice::timerDone()
     el   = prop->findElement("TRACK");
     if (!el) return;
 
-    if (el->state != PS_ON)
+    if (el->switch_state != ISS_ON)
     {
         devTimer->stop();
         return;
 <at>  <at>  -833,7 +839,7  <at>  <at>  void INDIStdDevice::timerDone()
         return;
 
     // wait until slew is done
-    if (prop->state == PS_BUSY)
+    if (prop->state == IPS_BUSY)
         return;
 
     kDebug() << "Timer called, starting processing";
 <at>  <at>  -900,11 +906,11  <at>  <at>  void INDIStdProperty::newText()
             {
                 if (device->deviceManager == stdDev->dp->deviceManager)
                 {
-                    if (device->deviceType == KSTARS_TELESCOPE)
+                    if (device->type == KSTARS_TELESCOPE)
                     {
                         Options::setTelescopePort( lp->text );
                     }
-                    else if (device->deviceType == KSTARS_VIDEO)
+                    else if (device->type == KSTARS_VIDEO)
                     {
                         Options::setVideoPort( lp->text );
                     }
 <at>  <at>  -995,10 +1001,10  <at>  <at>  bool INDIStdDevice::slew_scope(SkyPoint *scope_target, INDI_E *lp)
 
     HorProp = dp->findProp("HORIZONTAL_COORD_REQUEST");
 
-    if (EqProp && EqProp->perm == PP_RO)
+    if (EqProp && EqProp->perm == IP_RO)
 		EqProp = NULL;
 
-    if (HorProp && HorProp->perm == PP_RO)
+    if (HorProp && HorProp->perm == IP_RO)
           	HorProp = NULL;
 
     //kDebug() << "Skymap click - RA: " << scope_target->ra().toHMSString() << " DEC: " << scope_target->dec().toDMSString();
 <at>  <at>  -1081,7 +1087,7  <at>  <at>  bool INDIStdProperty::actionTriggered(INDI_E *lp)
                 return true;
 
            nameEle = stdDev->dp->findElem("OBJECT_NAME");
-       	   if (nameEle && nameEle->pp->perm != PP_RO)
+           if (nameEle && nameEle->pp->perm != IP_RO)
            {
                if (stdDev->currentObject == NULL)
 			nameEle->write_w->setText("--");
diff --git a/kstars/indi/telescopewizardprocess.cpp b/kstars/indi/telescopewizardprocess.cpp
index 5b4aac9..9ce1cc4 100644
--- a/kstars/indi/telescopewizardprocess.cpp
+++ b/kstars/indi/telescopewizardprocess.cpp
 <at>  <at>  -78,7 +78,7  <at>  <at>  telescopeWizardProcess::telescopeWizardProcess( QWidget* parent, const char* /*n
 
 
     foreach (IDevice *device, indidriver->devices)
-    if (device->deviceType == KSTARS_TELESCOPE)
+    if (device->type == KSTARS_TELESCOPE)
         ui->telescopeCombo->addItem(device->tree_label);
 
     //if ( !Options::telescopePort().isEmpty())
diff --git a/kstars/kspopupmenu.cpp b/kstars/kspopupmenu.cpp
index ce70573..8350258 100644
--- a/kstars/kspopupmenu.cpp
+++ b/kstars/kspopupmenu.cpp
 <at>  <at>  -495,7 +495,7  <at>  <at>  void KSPopupMenu::addINDI()
                         if (prop->stdID == ON_COORD_SET)
                             continue;
 
-                        a->setChecked( element->state == PS_ON );
+                        a->setChecked( element->switch_state == ISS_ON );
                     }
                 } // end property
             } // end group
diff --git a/kstars/kstars.cpp b/kstars/kstars.cpp
index ed757a3..bd84e28 100644
--- a/kstars/kstars.cpp
+++ b/kstars/kstars.cpp
 <at>  <at>  -57,8 +57,8  <at>  <at>  KStars::KStars( bool doSplash, bool clockrun, const QString &startdate ) :
         AAVSODialog(0), findDialog(0), imgExportDialog(0), obsList(0),
         execute(0),
         avt(0), wut(0), skycal(0),
-        sb(0), pv(0), jmt(0), fm(0), astrocalc(0), printingWizard(0), indimenu(0), indidriver(0),
-        indiseq(0), DialogIsObsolete(false), StartClockRunning( clockrun ),
+    sb(0), pv(0), jmt(0), fm(0), astrocalc(0), printingWizard(0), indimenu(0), indidriver(0),
+        indiseq(0), ekosmenu(0), DialogIsObsolete(false), StartClockRunning( clockrun ),
         StartDateString( startdate )
 {
     new KstarsAdaptor(this);
diff --git a/kstars/kstars.h b/kstars/kstars.h
index aa76633..8ac71f5 100644
--- a/kstars/kstars.h
+++ b/kstars/kstars.h
 <at>  <at>  -64,6 +64,7  <at>  <at>  class OpsSatellites;
 class OpsColors;
 class OpsAdvanced;
 class OpsINDI;
+class Ekos;
 #ifdef HAVE_XPLANET
 class OpsXplanet;
 #endif
 <at>  <at>  -533,6 +534,9  <at>  <at>  private slots:
     /** action slot: open INDI control panel */
     void slotINDIPanel();
 
+    /** action slot: open Ekos panel */
+    void slotEkos();
+
     /** action slot: open dialog for setting the view options */
     void slotViewOps();
 
 <at>  <at>  -659,6 +663,7  <at>  <at>  private:
     INDIMenu *indimenu;
     INDIDriver *indidriver;
     imagesequence *indiseq;  /* We need imgsequence here because it runs in batch mode */
+    Ekos *ekosmenu;
 
     QActionGroup *projectionGroup, *cschemeGroup;
 
diff --git a/kstars/kstarsactions.cpp b/kstars/kstarsactions.cpp
index 15d2552..d20f9b6 100644
--- a/kstars/kstarsactions.cpp
+++ b/kstars/kstarsactions.cpp
 <at>  <at>  -97,6 +97,9  <at>  <at> 
 
 #ifdef HAVE_CFITSIO_H
 #include "fitsviewer/fitsviewer.h"
+#ifdef HAVE_INDI_H
+#include "ekos/ekos.h"
+#endif
 #endif
 
 #ifdef HAVE_XPLANET
 <at>  <at>  -343,6 +346,16  <at>  <at>  void KStars::slotINDIDriver()
 #endif
 }
 
+void KStars::slotEkos()
+{
+#ifdef HAVE_INDI_H
+    if (ekosmenu == NULL)
+        ekosmenu = new Ekos(this);
+
+    ekosmenu->show();
+#endif
+}
+
 void KStars::slotGeoLocator() {
     QPointer<LocationDialog> locationdialog = new LocationDialog(this);
     if ( locationdialog->exec() == QDialog::Accepted ) {
diff --git a/kstars/kstarsinit.cpp b/kstars/kstarsinit.cpp
index 1ed0a9b..fa0e4b4 100644
--- a/kstars/kstarsinit.cpp
+++ b/kstars/kstarsinit.cpp
 <at>  <at>  -378,6 +378,13  <at>  <at>  void KStars::initActions() {
     actionCollection()->addAction("skycalendar", this, SLOT( slotCalendar() ) )
         << i18n("Sky Calendar...");
 
+#ifdef HAVE_INDI_H
+#ifndef Q_WS_WIN
+    actionCollection()->addAction("ekos", this, SLOT( slotEkos() ) )
+        << i18n("Ekos...");
+#endif
+#endif
+
 //FIXME: implement glossary
 //     ka = actionCollection()->addAction("glossary");
 //     ka->setText( i18n("Glossary...") );
 <at>  <at>  -427,6 +434,8  <at>  <at>  void KStars::initActions() {
         << i18n("INDI Control Panel...");
     ka->setEnabled(false);
 
+
+
 #endif
 #endif
 
diff --git a/kstars/kstarsui-indi.rc b/kstars/kstarsui-indi.rc
index b8a2a09..6903a36 100644
--- a/kstars/kstarsui-indi.rc
+++ b/kstars/kstarsui-indi.rc
 <at>  <at>  -72,6 +72,7  <at>  <at> 
 		<Action name="lightcurvegenerator" />
 		<Action name="altitude_vs_time" />
 		<Action name="whats_up_tonight" />
+                <Action name="ekos" />
 		<Action name="glossary" />
 		<Action name="scriptbuilder" />
 		<Action name="solarsystem" />
diff --git a/kstars/oal/equipmentwriter.ui b/kstars/oal/equipmentwriter.ui
index b5693d1..4b3a23f 100644
--- a/kstars/oal/equipmentwriter.ui
+++ b/kstars/oal/equipmentwriter.ui
 <at>  <at>  -7,7 +7,7  <at>  <at> 
     <x>0</x>
     <y>0</y>
     <width>411</width>
-    <height>324</height>
+    <height>344</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="_2">
 <at>  <at>  -20,7 +20,7  <at>  <at> 
       </size>
      </property>
      <property name="currentIndex">
-      <number>3</number>
+      <number>0</number>
      </property>
      <property name="documentMode">
       <bool>false</bool>
 <at>  <at>  -86,6 +86,9  <at>  <at> 
            </item>
            <item row="5" column="1">
             <widget class="KComboBox" name="Type">
+             <property name="focusPolicy">
+              <enum>Qt::TabFocus</enum>
+             </property>
              <item>
               <property name="text">
                <string>Refractor</string>
 <at>  <at>  -128,9 +131,18  <at>  <at> 
            <item row="6" column="1">
             <layout class="QHBoxLayout" name="horizontalLayout_5">
              <item>
-              <widget class="KDoubleNumInput" name="Aperture" native="true">
+              <widget class="KDoubleNumInput" name="Aperture">
                <property name="focusPolicy">
-                <enum>Qt::TabFocus</enum>
+                <enum>Qt::ClickFocus</enum>
+               </property>
+               <property name="label">
+                <string/>
+               </property>
+               <property name="singleStep">
+                <double>0.100000000000000</double>
+               </property>
+               <property name="sliderEnabled">
+                <bool>false</bool>
                </property>
               </widget>
              </item>
 <at>  <at>  -153,9 +165,9  <at>  <at> 
            <item row="7" column="1">
             <layout class="QHBoxLayout" name="horizontalLayout_4">
              <item>
-              <widget class="KDoubleNumInput" name="FocalLength" native="true">
+              <widget class="KDoubleNumInput" name="FocalLength">
                <property name="focusPolicy">
-                <enum>Qt::TabFocus</enum>
+                <enum>Qt::ClickFocus</enum>
                </property>
               </widget>
              </item>
 <at>  <at>  -177,6 +189,9  <at>  <at> 
            </item>
            <item row="4" column="1">
             <widget class="QComboBox" name="driverComboBox">
+             <property name="focusPolicy">
+              <enum>Qt::TabFocus</enum>
+             </property>
              <item>
               <property name="text">
                <string>None</string>
 <at>  <at>  -273,7 +288,7  <at>  <at> 
            <item row="4" column="1">
             <layout class="QHBoxLayout" name="horizontalLayout_6">
              <item>
-              <widget class="KDoubleNumInput" name="e_focalLength" native="true">
+              <widget class="KDoubleNumInput" name="e_focalLength">
                <property name="focusPolicy">
                 <enum>Qt::TabFocus</enum>
                </property>
 <at>  <at>  -317,7 +332,7  <at>  <at> 
             </widget>
            </item>
            <item>
-            <widget class="KDoubleNumInput" name="Fov" native="true">
+            <widget class="KDoubleNumInput" name="Fov">
              <property name="focusPolicy">
               <enum>Qt::TabFocus</enum>
              </property>
 <at>  <at>  -467,7 +482,7  <at>  <at> 
             </widget>
            </item>
            <item row="4" column="1">
-            <widget class="KDoubleNumInput" name="l_Factor" native="true">
+            <widget class="KDoubleNumInput" name="l_Factor">
              <property name="focusPolicy">
               <enum>Qt::TabFocus</enum>
              </property>
 <at>  <at>  -649,9 +664,9  <at>  <at> 
  </widget>
  <customwidgets>
   <customwidget>
-   <class>KLineEdit</class>
-   <extends>QLineEdit</extends>
-   <header>klineedit.h</header>
+   <class>KDoubleNumInput</class>
+   <extends>QWidget</extends>
+   <header>knuminput.h</header>
   </customwidget>
   <customwidget>
    <class>KComboBox</class>
 <at>  <at>  -659,9 +674,10  <at>  <at> 
    <header>kcombobox.h</header>
   </customwidget>
   <customwidget>
-   <class>KDoubleNumInput</class>
-   <extends>QWidget</extends>
-   <header>knuminput.h</header>
+   <class>KLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>klineedit.h</header>
+   <container>1</container>
   </customwidget>
   <customwidget>
    <class>KListWidget</class>
 <at>  <at>  -681,13 +697,13  <at>  <at> 
   <tabstop>NewScope</tabstop>
   <tabstop>AddScope</tabstop>
   <tabstop>RemoveScope</tabstop>
-  <tabstop>EyepieceList</tabstop>
   <tabstop>e_Vendor</tabstop>
-  <tabstop>e_Model</tabstop>
+  <tabstop>EyepieceList</tabstop>
   <tabstop>e_focalLength</tabstop>
   <tabstop>Fov</tabstop>
   <tabstop>FovUnit</tabstop>
   <tabstop>NewEyepiece</tabstop>
+  <tabstop>e_Model</tabstop>
   <tabstop>AddEyepiece</tabstop>
   <tabstop>RemoveEyepiece</tabstop>
   <tabstop>LensList</tabstop>
diff --git a/kstars/skymapdrawabstract.cpp b/kstars/skymapdrawabstract.cpp
index 82db1ae..c724bff 100644
--- a/kstars/skymapdrawabstract.cpp
+++ b/kstars/skymapdrawabstract.cpp
 <at>  <at>  -201,7 +201,7  <at>  <at>  void SkyMapDrawAbstract::drawTelescopeSymbols(QPainter &psky)
             // make sure the dev is on first
             if (devMenu->managers.at(i)->indi_dev.at(j)->isOn()) {
                 portConnect = devMenu->managers.at(i)->indi_dev.at(j)->findProp("CONNECTION");
-                if( !portConnect || portConnect->state == PS_BUSY )
+                if( !portConnect || portConnect->state == IPS_BUSY )
                     return;
 
                 eqNum = devMenu->managers.at(i)->indi_dev.at(j)->findProp("EQUATORIAL_EOD_COORD");
 <at>  <at>  -342,7 +342,7  <at>  <at>  void SkyMapDrawAbstract::exportSkyImage(SkyQPainter *painter, bool scale)
 void SkyMapDrawAbstract::calculateFPS()
 {
     if(m_framecount == 25) {
-        float sec = m_fpstime.elapsed()/1000.;
+        //float sec = m_fpstime.elapsed()/1000.;
         // kDebug() << "FPS " << m_framecount/sec;
         m_framecount = 0;
         m_fpstime.restart();
diff --git a/kstars/tools/observinglist.cpp b/kstars/tools/observinglist.cpp
index 6f1b6fa..3d4ac99 100644
--- a/kstars/tools/observinglist.cpp
+++ b/kstars/tools/observinglist.cpp
 <at>  <at>  -603,7 +603,7  <at>  <at>  void ObservingList::slotSlewToObject()
             ConnectEle = indidev->findElem("CONNECT");
             if (!ConnectEle) continue;
 
-            if (ConnectEle->state == PS_OFF)
+            if (ConnectEle->switch_state == ISS_OFF)
             {
                 KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", indidev->label));
                 return;
Aleksey Khudyakov | 11 Jan 15:43
Picon
Gravatar

Re: [bug] KStars should use system time zone and DST data

On 11.01.2012 13:33, Bogdan Marinov wrote:
> Some perspective from another project:
>
> Stellarium uses this approach (getting the time from the system clock
> and storing it as JD). It works OK in the typical case, when the user
> is interested in the sky over their (computer's) current location, but
> we often get bug reports and puzzled questions from people who try to
> use it from other locations or have incorrect system time zone
> settings. (Stellarium does not offer an easy way of overriding the
> time zone.)
>
> I have volunteered to overhaul Stellarium's time zone system, though I
> haven't done anything on it so far. The idea is to implement support
> for the tzdata database and to pull updates from its ftp server. As
> Stellarium is multi-platform, I'll have to reinvent the wheel, but
> KStars is a KDE/Linux application and it can use the regularly updated
> tzdata package (if it doesn't already do so).
>
I think it has more to do with inability to override time zone. End user 
only need ability to choose location and time zone. He need not to care 
about tnternal representation as long as calculations are correct.

P.S. I'm CCing kstars-devel
Aleksey Khudyakov | 10 Jan 20:48
Picon
Gravatar

[bug] KStars should use system time zone and DST data

Hello everyone!

I've just submitted a bug about time handling in KStars. Does anyone 
have comments? Bug description is pasted inlne

Now KStars uses its own system for handling time zones and daylsight 
saving time (DST) rules. This is wrong for several reasons:

1. It's not correct. Time zones and DST change over time. KStars cannot 
handle it.

2. They'll become out of date when rules change. In fact they are out of 
date already.

3. It's very complex. We canot make it better than it's already done. 
It's just a wasted effort.

In my opinion KStars should store time internally as julian date or 
something similar. It's simple since it doesn't have to deal with abrupt 
jumps in time when DST kicks in. It's just a number so it's easy to 
manipulate time intervals. Transformation to/from YYYY:MM:DD HH:MM:SS is 
required only  when  time is presented/being read  from user and could 
be done on demand.

On downside it require overhaul of all time handling in kstars.
Guido Broich | 5 Jan 20:37
Picon

KSTARS

I cannot succeed in downloading the USNO NOMAD stars catalogue, I have a
fast connection and KSTRS is working for 2 days now ... something must
be wrong. 

Con you give me a different direct link please?

thanks

--

-- 
*********************************************************
Guido Broich
http://www.guidobroich.it
Public PGP key ID 53A510F3 hkp://keys.gnupg.net
If you wish to send encrypted mails, send your public key ID
*********************************************************
_______________________________________________
Kstars-devel mailing list
Kstars-devel <at> kde.org
https://mail.kde.org/mailman/listinfo/kstars-devel
Samikshan Bairagya | 25 Dec 12:30
Picon
Gravatar

Review Request: Dialog for Supernova options. Implemented the option for users to see supernovae on the skymap.

This is an automatically generated e-mail. To reply, visit: http://git.reviewboard.kde.org/r/103530/

Review request for KStars, Rafal Kulaga and Akarsh Simha.
By Samikshan Bairagya.

Description

Worked on the problems of my earlier review request and fixed them. There's no icon for Supernova now. Users can choose to either have or not have supernovae on their skymap. Notifications for new supernovae is not yet implemented.

Diffs

  • kstars/kstarsactions.cpp (15d2552)
  • kstars/kstarsdcop.cpp (c045e47)
  • kstars/options/opssupernovae.h (PRE-CREATION)
  • kstars/options/opssupernovae.cpp (PRE-CREATION)
  • kstars/options/opssupernovae.ui (PRE-CREATION)
  • kstars/CMakeLists.txt (6f7ed20)
  • kstars/kstars.h (aa76633)
  • kstars/kstars.kcfg (986f322)
  • kstars/skycomponents/supernovaecomponent.h (ab824c6)
  • kstars/skycomponents/supernovaecomponent.cpp (5c3c8bb)

View Diff

_______________________________________________
Kstars-devel mailing list
Kstars-devel <at> kde.org
https://mail.kde.org/mailman/listinfo/kstars-devel
Gilbert Weingarten | 9 Dec 14:56
Gravatar

kstars 4:4.7.2-0ubuntu1 Won't Run on Ubuntu 11.10

version: kstars 4:4.7.2-0ubuntu1 faults under Ubuntu 11.10
with this message:

Executable: kstars PID: 25985 Signal: Segmentation fault (11)

I've attached the 'backtrace' provided by KDE

Hope you can help ....  I've never been able to successfully run kstars
since before ubuntu 10.04, when it used to run my ETX-125 perfectly

gil
=======
Application: KStars (kstars), signal: Segmentation fault
[Current thread is 1 (Thread 0x7f00793e77c0 (LWP 25985))]

Thread 2 (Thread 0x7f0062a40700 (LWP 25988)):
#0  0x00007f0074dd5773 in poll () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007f006f263f68 in g_main_context_poll (n_fds=3, fds=0x1030690, timeout=-1,
context=0x1024930, priority=<optimized out>) at /build/buildd/glib2.0-2.30.0/./glib/gmain.c:3402
#2  g_main_context_iterate (context=0x1024930, block=<optimized out>, dispatch=1, self=<optimized
out>) at /build/buildd/glib2.0-2.30.0/./glib/gmain.c:3084
#3  0x00007f006f264792 in g_main_loop_run (loop=0x10248e0) at /build/buildd/glib2.0-2.30.0/./glib/gmain.c:3297
#4  0x00007f006abcd516 in gdbus_shared_thread_func (user_data=0x1024900) at /build/buildd/glib2.0-2.30.0/./gio/gdbusprivate.c:276
#5  0x00007f006f2892b6 in g_thread_create_proxy (data=0x1024a20) at /build/buildd/glib2.0-2.30.0/./glib/gthread.c:1962
#6  0x00007f0077bc456c in ?? () from /usr/lib/nvidia-current/libGL.so.1
#7  0x00007f00746b8efc in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0
#8  0x00007f0074de189d in clone () from /lib/x86_64-linux-gnu/libc.so.6
#9  0x0000000000000000 in ?? ()

Thread 1 (Thread 0x7f00793e77c0 (LWP 25985)):
[KCrash Handler]
#6  0x00007f0076825f6c in QLocalePrivate::stringToLongLong(QString const&, int, bool*,
QLocalePrivate::GroupSeparatorMode) const () from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#7  0x00007f0076840552 in QString::toLongLong(bool*, int) const () from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#8  0x00007f0076840679 in QString::toInt(bool*, int) const () from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#9  0x000000000048934a in AsteroidsComponent::loadData (this=0x16a23f0) at ../../kstars/skycomponents/asteroidscomponent.cpp:117
#10 0x000000000048cc8c in SolarSystemComposite::SolarSystemComposite (this=0x10a6270,
parent=<optimized out>) at ../../kstars/skycomponents/solarsystemcomposite.cpp:60
#11 0x00000000004838f4 in SkyMapComposite::SkyMapComposite (this=0x134afc0, parent=<optimized
out>) at ../../kstars/skycomponents/skymapcomposite.cpp:92
#12 0x000000000058b4a4 in KStarsData::initialize (this=0x1193d90) at ../../kstars/kstarsdata.cpp:185
#13 0x0000000000575a2a in KStars::KStars (this=0x118a940, doSplash=<optimized out>,
clockrun=<optimized out>, startdate=<optimized out>, __in_chrg=<optimized out>,
__vtt_parm=<optimized out>) at ../../kstars/kstars.cpp:106
#14 0x0000000000575baa in KStars::createInstance (doSplash=true, clockrun=true, startdate=...) at ../../kstars/kstars.cpp:121
#15 0x000000000043543f in main (argc=5, argv=0x7fff50da73a8) at ../../kstars/main.cpp:189
_______________________________________________
Kstars-devel mailing list
Kstars-devel <at> kde.org
https://mail.kde.org/mailman/listinfo/kstars-devel
mario bertolotto | 8 Dec 09:19
Picon

USNO NOMAD Catalog download

Dear Sirs,

I would need your help to solve the following problem:

I presently use KStars 1.6.0 and I would like to download USNO NOMAD Catalog.

I tried many times from inside KStars (File --> Download new data) but, although the process starts OK is anyhow extremely slow and stops after few hours with no success.

Tyco -2 catalog is installed OK.

I would appreciate if you can give indication how to solve this problem.

Thank you very much for your help.

Best Regards.

Mario Bertolotto
_______________________________________________
Kstars-devel mailing list
Kstars-devel <at> kde.org
https://mail.kde.org/mailman/listinfo/kstars-devel
Akarsh Simha | 17 Nov 11:58
Picon
Gravatar

[urgent] Google Code-In Mentors to be signed up and Tasks to be created before Monday

Hi

If anyone is interested in mentoring for Google Code-In and wishes to
create some GCI tasks, they should be done before Monday on Melange.

Please read:
http://code.google.com/p/google-code-in/wiki/GCIAdminMentorInformation
for more information.

If you wish to mentor for Google Code-In, please create a profile on
http://www.google-melange.com/gci/profile/mentor/google/gci2011
and apply to be a mentor for KDE.

Please do let me know if you need help. I have assignments due and an
exam lined up in the coming days, so if I don't respond, you may
contact any of the other org-admins (Valorie Zimmerman, Lydia
Pintscher, Sandro Andrade, Roger Pixley, Anne-Marie Mahfouf).

Regards
Akarsh
Jasem Mutlaq | 12 Nov 10:59

[kstars] kstars/indi: Fixed an issue where atof is not working properly in some locales resulting in inaccurate coordinate conversion from INDI devices. Thanks for Igancio Mas for catching this!

Git commit 6eefdf3311be45743763c52ad851b52fd23fc7ee by Jasem Mutlaq.
Committed on 12/11/2011 at 10:55.
Pushed by mutlaqja into branch 'master'.

Fixed an issue where atof is not working properly in some locales resulting in inaccurate coordinate
conversion from INDI devices. Thanks for Igancio Mas for catching this!

CCMAIL:kstars-devel <at> kde.org
CCMAIL:mas.ignacio <at> gmail.com

M  +12   -0    kstars/indi/indidevice.cpp
M  +4    -0    kstars/indi/indiproperty.cpp

http://commits.kde.org/kstars/6eefdf3311be45743763c52ad851b52fd23fc7ee

diff --git a/kstars/indi/indidevice.cpp b/kstars/indi/indidevice.cpp
index c46c7e1..60c6437 100644
--- a/kstars/indi/indidevice.cpp
+++ b/kstars/indi/indidevice.cpp
@@ -212,7 +212,11 @@ int INDI_D::setValue (INDI_P *pp, XMLEle *root, QString & errmsg)
     /* allow changing the timeout */
     ap = findXMLAtt (root, "timeout");
     if (ap)
+    {
+        setlocale(LC_NUMERIC,"C");
         pp->timeout = atof(valuXMLAtt(ap));
+        setlocale(LC_NUMERIC,"");
+    }

     /* process specific GUI features */
     switch (pp->guitype)
@@ -257,6 +261,9 @@ int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, QString & errmsg)
     char iNumber[32];
     double min, max;

+    // Ensure that atof works with decimal points
+    setlocale(LC_NUMERIC,"C");
+
     for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
     {
         if (strcmp (tagXMLEle(ep), "oneText") && strcmp(tagXMLEle(ep), "oneNumber"))
@@ -291,6 +298,7 @@ int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, QString & errmsg)
             }
             else if (pp->guitype == PG_NUMERIC)
             {
+
                 lp->value = atof(pcdataXMLEle(ep));
                 numberFormat(iNumber, lp->format.toAscii(), lp->value);
                 lp->text = iNumber;
@@ -341,6 +349,8 @@ int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, QString & errmsg)
     // suppress warning
     errmsg = errmsg;

+    setlocale(LC_NUMERIC,"");
+
     return (0);
 }

@@ -655,7 +665,9 @@ INDI_P * INDI_D::addProperty (XMLEle *root, QString & errmsg)
     /* init timeout */
     ap = findAtt (root, "timeout", errmsg);
     /* default */
+    setlocale(LC_NUMERIC,"C");
     pp->timeout = ap ? atof(valuXMLAtt(ap)) : 0;
+    setlocale(LC_NUMERIC,"");

     /* log any messages */
     deviceManager->checkMsg (root, this);
diff --git a/kstars/indi/indiproperty.cpp b/kstars/indi/indiproperty.cpp
index c79cae3..f9acf40 100644
--- a/kstars/indi/indiproperty.cpp
+++ b/kstars/indi/indiproperty.cpp
@@ -504,6 +504,8 @@ int INDI_P::buildNumberGUI  (XMLEle *root, QString & errmsg)
     INDI_E *lp;
     QString numberName, numberLabel;

+    setlocale(LC_NUMERIC,"C");
+
     for (number = nextXMLEle (root, 1); number != NULL; number = nextXMLEle (root, 0))
     {
         if (strcmp (tagXMLEle(number), "defNumber"))
@@ -558,6 +560,8 @@ int INDI_P::buildNumberGUI  (XMLEle *root, QString & errmsg)

     }

+    setlocale(LC_NUMERIC,"");
+
     if (perm == PP_RO)
         return 0;

Gmane