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

Jasem Mutlaq mutlaqja at ikarustech.com
Mon Jan 30 01:15:53 UTC 2012


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
@@ -10,6 +10,7 @@ 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
@@ -190,6 +190,9 @@ void Ekos::connectDevices()
 
     if (filter != NULL)
         filter->Connect();
+
+    connectB->setEnabled(false);
+    disconnectB->setEnabled(true);
 }
 
 void Ekos::disconnectDevices()
@@ -210,6 +213,9 @@ void Ekos::disconnectDevices()
     if (filter != NULL)
         filter->Disconnect();
 
+    connectB->setEnabled(true);
+    disconnectB->setEnabled(false);
+
 }
 
 void Ekos::cleanDevices()
@@ -225,10 +231,6 @@ 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
@@ -0,0 +1,9 @@
+#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
@@ -16,7 +16,7 @@
  ***************************************************************************/
 
 #include "fitshistogram.h"
-#include "fitsviewer.h"
+#include "fitstab.h"
 #include "fitsimage.h"
 
 #include <math.h>
@@ -47,9 +47,10 @@ 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;
@@ -86,12 +87,13 @@ 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;
 
@@ -120,21 +122,23 @@ 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);
 
@@ -190,6 +194,7 @@ void FITSHistogram::constructHistogram(int hist_width, int hist_height)
     updateBoxes(ui->histFrame->getLowerLimit(), ui->histFrame->getUpperLimit());
 
     ui->histFrame->update();
+
 }
 
 
@@ -201,10 +206,11 @@ 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]));
 
+
 }
 
 
@@ -226,12 +232,11 @@ 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;
@@ -249,27 +254,18 @@ 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++)
             {
@@ -281,7 +277,7 @@ void FITSHistogramCommand::redo()
             }
         break;
 
-    case FITSImage::FITSLog:
+    case FITSLog:
         coeff = max / log(1 + max);
 
         for (int i=0; i < height; i++)
@@ -297,7 +293,7 @@ void FITSHistogramCommand::redo()
             }
         break;
 
-    case FITSImage::FITSSqrt:
+    case FITSSqrt:
         coeff = max / sqrt(max);
 
         for (int i=0; i < height; i++)
@@ -316,23 +312,22 @@ 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
@@ -31,7 +31,7 @@
 
 const int INITIAL_MAXIMUM_WIDTH = 1024;
 
-class FITSViewer;
+class FITSTab;
 class QPixmap;
 
 
@@ -61,6 +61,7 @@ public:
     int napply;
     double histFactor;
     double fits_min, fits_max;
+    FITSTab *tab;
 
 private:
 
@@ -68,7 +69,6 @@ private:
     histogramUI *ui;
     int histogram_height, histogram_width;
     QVarLengthArray<int, INITIAL_MAXIMUM_WIDTH> histArray;
-    FITSViewer * viewer;
 
 public slots:
     void applyScale();
@@ -94,7 +94,7 @@ 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
@@ -1,7 +1,8 @@
-<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>
@@ -9,192 +10,186 @@
     <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>
@@ -202,54 +197,54 @@
        </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>
@@ -259,22 +254,22 @@
     </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>
@@ -283,24 +278,24 @@
       </spacer>
      </item>
      <item>
-      <widget class="QPushButton" name="applyB" >
-       <property name="text" >
+      <widget class="QPushButton" name="applyB">
+       <property name="text">
         <string>&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>&Close</string>
        </property>
-       <property name="autoDefault" >
+       <property name="autoDefault">
         <bool>true</bool>
        </property>
       </widget>
@@ -309,7 +304,7 @@
    </item>
   </layout>
  </widget>
-  <customwidgets>
+ <customwidgets>
   <customwidget>
    <class>histDrawArea</class>
    <extends>QFrame</extends>
@@ -330,11 +325,11 @@
    <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
@@ -39,7 +39,6 @@
 #include <KMessageBox>
 #include <KFileDialog>
 
-#include "fitsviewer.h"
 #include "ksutils.h"
 
 #define ZOOM_DEFAULT	100.0
@@ -71,13 +70,14 @@ 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();
@@ -85,8 +85,6 @@ 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;
@@ -94,6 +92,8 @@ 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
@@ -104,14 +104,13 @@ 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;
@@ -123,18 +122,18 @@ 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);
@@ -143,28 +142,26 @@ 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);
@@ -177,7 +174,7 @@ 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);
@@ -186,18 +183,17 @@ 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);
 
@@ -210,19 +206,19 @@ 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;
@@ -231,32 +227,33 @@ 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;
 
 }
 
@@ -275,21 +272,21 @@ 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;
@@ -298,7 +295,7 @@ 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 */
@@ -307,21 +304,21 @@ 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"));
@@ -329,10 +326,10 @@ 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)
@@ -344,14 +341,10 @@ 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
@@ -373,11 +366,10 @@ 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)
@@ -433,12 +425,9 @@ 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);
 
@@ -471,31 +460,26 @@ 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);
@@ -503,22 +487,22 @@ 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);
@@ -527,13 +511,14 @@ 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];
@@ -542,7 +527,7 @@ 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
@@ -39,12 +39,16 @@
 
 #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();
@@ -54,6 +58,9 @@ protected:
 
 private:
     FITSImage *image;
+
+signals:
+    void newStatus(const QString &msg, FITSBar id);
 };
 
 class FITSImage : public QScrollArea
@@ -63,21 +70,17 @@ 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]; }
@@ -105,9 +108,9 @@ public:
     } stats;
 
 public slots:
-    void fitsZoomIn();
-    void fitsZoomOut();
-    void fitsZoomDefault();
+    void ZoomIn();
+    void ZoomOut();
+    void ZoomDefault();
 
 private:
 
@@ -116,7 +119,7 @@ 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 */
 
@@ -126,6 +129,10 @@ 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
@@ -0,0 +1,273 @@
+#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
@@ -0,0 +1,63 @@
+#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 ¤tURL; }
+   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
@@ -33,7 +33,10 @@
 #include <kstatusbar.h>
 #include <klineedit.h>
 #include <kicon.h>
+
 #include <KUndoStack>
+#include <KTabWidget>
+#include <KAction>
 
 #include <QFile>
 #include <QCursor>
@@ -46,8 +49,8 @@
 #include <QTreeWidget>
 #include <QHeaderView>
 #include <QApplication>
-
-
+#include <QUndoStack>
+#include <QUndoGroup>
 
 #include <math.h>
 #ifndef __MINGW32__
@@ -60,87 +63,85 @@
   #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);
 }
@@ -148,218 +149,247 @@ 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
@@ -20,11 +20,10 @@
 #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
@@ -33,54 +32,62 @@
 
 #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
@@ -221,14 +221,13 @@ 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
@@ -536,7 +536,8 @@ void KStars::slotOpenFITS()
     if (fileURL.isEmpty())
         return;
 
-    FITSViewer * fv = new FITSViewer(&fileURL, this);
+    FITSViewer * fv = new FITSViewer(this);
+    fv->addFITS(&fileURL);
     fv->show();
 #endif
 }


More information about the Kstars-devel mailing list