changeset 277:8acd30ed735c

* Fix up and simplify the LayerTreeModel, removing a horrible memory leak * Move phase-unwrapped frequency estimation from SpectrogramLayer to FFTDataServer * Make the spectrum show peak phase-unwrapped frequencies as well (still needs work) * Start adding piano keyboard horizontal scale to spectrum * Debug output for id3 tags
author Chris Cannam
date Tue, 03 Jul 2007 12:46:18 +0000
parents 21c7152ddba8
children a078aa2932cc
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/SpectrumLayer.cpp layer/SpectrumLayer.h view/Pane.cpp view/Pane.h view/PaneStack.cpp view/PaneStack.h view/View.h widgets/LayerTree.cpp
diffstat 10 files changed, 220 insertions(+), 235 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Mon Jul 02 14:58:34 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -1064,48 +1064,6 @@
     }
 }
 
-float
-SpectrogramLayer::calculateFrequency(size_t bin,
-				     size_t windowSize,
-				     size_t windowIncrement,
-				     size_t sampleRate,
-				     float oldPhase,
-				     float newPhase,
-				     bool &steadyState)
-{
-    // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec.
-    // At hopsize h and sample rate sr, one hop happens in h/sr sec.
-    // At window size w, for bin b, f is b*sr/w.
-    // thus 2pi phase shift happens in w/(b*sr) sec.
-    // We need to know what phase shift we expect from h/sr sec.
-    // -> 2pi * ((h/sr) / (w/(b*sr)))
-    //  = 2pi * ((h * b * sr) / (w * sr))
-    //  = 2pi * (h * b) / w.
-
-    float frequency = (float(bin) * sampleRate) / windowSize;
-
-    float expectedPhase =
-	oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize;
-
-    float phaseError = princargf(newPhase - expectedPhase);
-	    
-    if (fabsf(phaseError) < (1.1f * (windowIncrement * M_PI) / windowSize)) {
-
-	// The new frequency estimate based on the phase error
-	// resulting from assuming the "native" frequency of this bin
-
-	float newFrequency =
-	    (sampleRate * (expectedPhase + phaseError - oldPhase)) /
-	    (2 * M_PI * windowIncrement);
-
-	steadyState = true;
-	return newFrequency;
-    }
-
-    steadyState = false;
-    return frequency;
-}
-
 unsigned char
 SpectrogramLayer::getDisplayValue(View *v, float input) const
 {
@@ -1363,6 +1321,10 @@
 					     float &adjFreqMin, float &adjFreqMax)
 const
 {
+    if (!m_model || !m_model->isOK() || !m_model->isReady()) {
+	return false;
+    }
+
     FFTModel *fft = getFFTModel(v);
     if (!fft) return false;
 
@@ -1407,13 +1369,7 @@
 	    
 	    if (s < int(fft->getWidth()) - 1) {
 
-		freq = calculateFrequency(q, 
-					  windowSize,
-					  windowIncrement,
-					  sr, 
-					  fft->getPhaseAt(s, q),
-					  fft->getPhaseAt(s+1, q),
-					  steady);
+                fft->estimateStableFrequency(s, q, freq);
 	    
 		if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
 		if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
@@ -1435,6 +1391,10 @@
 				      float &min, float &max,
 				      float &phaseMin, float &phaseMax) const
 {
+    if (!m_model || !m_model->isOK() || !m_model->isReady()) {
+	return false;
+    }
+
     float q0 = 0, q1 = 0;
     if (!getYBinRange(v, y, q0, q1)) return false;
 
@@ -2133,14 +2093,8 @@
 		if (m_binDisplay == PeakFrequencies &&
 		    s < int(fft->getWidth()) - 1) {
 
-		    bool steady = false;
-                    float f = calculateFrequency(q,
-						 m_windowSize,
-						 increment,
-						 sr,
-						 fft->getPhaseAt(s, q),
-						 fft->getPhaseAt(s+1, q),
-						 steady);
+                    float f = 0;
+                    fft->estimateStableFrequency(s, q, f);
 
 		    y0 = y1 = v->getYForFrequency
 			(f, displayMinFreq, displayMaxFreq, logarithmic);
@@ -2855,6 +2809,8 @@
 
     if (m_frequencyScale == LogFrequencyScale) {
 
+        // piano keyboard
+
 	paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
 
 	float minf = getEffectiveMinFrequency();
@@ -2879,11 +2835,9 @@
                 // C# -- fill the C from here
                 if (ppy - y > 2) {
                     paint.fillRect(w - pkw,
-//                                   y - (py - y) / 2 - (py - y) / 4, 
                                    y,
                                    pkw,
                                    (py + ppy) / 2 - y,
-//                                   py - y + 1,
                                    Qt::gray);
                 }
             }
--- a/layer/SpectrogramLayer.h	Mon Jul 02 14:58:34 2007 +0000
+++ b/layer/SpectrogramLayer.h	Tue Jul 03 12:46:18 2007 +0000
@@ -296,14 +296,6 @@
     void initialisePalette();
     void rotatePalette(int distance);
 
-    static float calculateFrequency(size_t bin,
-				    size_t windowSize,
-				    size_t windowIncrement,
-				    size_t sampleRate,
-				    float previousPhase,
-				    float currentPhase,
-				    bool &steadyState);
-
     unsigned char getDisplayValue(View *v, float input) const;
     float getInputForDisplayValue(unsigned char uc) const;
 
--- a/layer/SpectrumLayer.cpp	Mon Jul 02 14:58:34 2007 +0000
+++ b/layer/SpectrumLayer.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -21,6 +21,7 @@
 #include "base/AudioLevel.h"
 #include "base/Preferences.h"
 #include "base/RangeMapper.h"
+#include "base/Pitch.h"
 #include "ColourMapper.h"
 
 #include <QPainter>
@@ -53,6 +54,14 @@
 {
     if (m_originModel == model) return;
     m_originModel = model;
+
+    if (m_sliceableModel) {
+        const Model *oldModel = m_sliceableModel;
+        setSliceableModel(0);
+        // surprised I'm allowed to delete a const pointer -- may be a
+        // source of future compiler rejection?
+        delete oldModel;
+    }
 //!!!    setupFFT();
 }
 
@@ -549,8 +558,112 @@
         const_cast<SpectrumLayer *>(this)->setupFFT(); //ugh
         m_newFFTNeeded = false;
     }
+
+    FFTModel *fft = dynamic_cast<FFTModel *>
+        (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel));
+
+    float thresh = powf(10, -8) / m_gain; // -80dB
+
+    int xorigin = getVerticalScaleWidth(v, paint) + 1;
+    int w = v->width() - xorigin - 1;
+
+    if (fft) {
+
+        // draw peak lines
+        //!!! should be optional
+
+        size_t col = v->getCentreFrame() / fft->getResolution();
+
+        paint.save();
+        paint.setRenderHint(QPainter::Antialiasing, false);
+        paint.setPen(QColor(160, 160, 160)); //!!!
+
+        ColourMapper mapper(m_colourMap, 0, 1);
+
+        BiasCurve curve;
+        getBiasCurve(curve);
+        size_t cs = curve.size();
+        
+        for (size_t bin = 0; bin < fft->getHeight(); ++bin) {
+            
+            if (!fft->isLocalPeak(col, bin)) continue;
+            if (!fft->isOverThreshold(col, bin, thresh)) continue;
+            
+            float freq = 0;
+            bool haveFreq = fft->estimateStableFrequency(col, bin, freq);
+            if (!haveFreq) continue;
+            
+            int x = lrintf(getXForFrequency(freq, w));
+
+            float value = m_sliceableModel->getValueAt(col, bin);
+            if (bin < cs) value *= curve[bin];
+            float norm = 0.f;
+            float y = getYForValue(value, v, norm); // don't need y, need norm
+
+            paint.setPen(mapper.map(norm));
+            paint.drawLine(xorigin + x, 0, xorigin + x, v->height());
+        }
+
+        paint.restore();
+    }
     
     SliceLayer::paint(v, paint, rect);
+
+    if (m_binScale == LogBins) {
+
+        int pkh = 10;
+        int h = v->height();
+
+        // piano keyboard
+        //!!! should be in a new paintHorizontalScale()?
+
+	paint.drawLine(xorigin, h - pkh - 1, w, h - pkh - 1);
+
+	int px = xorigin, ppx = xorigin;
+//	paint.setBrush(paint.pen().color());
+
+	for (int i = 0; i < 128; ++i) {
+
+	    float f = Pitch::getFrequencyForPitch(i);
+	    int x = lrintf(getXForFrequency(f, w));
+
+            if (x < 0) break;
+            if (x + xorigin > w) {
+                continue;
+            }
+                           
+            x += xorigin;
+
+	    int n = (i % 12);
+
+            if (n == 1) {
+                // C# -- fill the C from here
+                if (x - ppx > 2) {
+                    paint.fillRect(x,
+                                   h - pkh,
+                                   x - (px + ppx) / 2,
+                                   pkh,
+                                   Qt::gray);
+                }
+            }
+
+	    if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
+		// black notes
+		paint.drawLine(x, h - pkh, x, h);
+		int rw = ((px - x) / 4) * 2;
+		if (rw < 2) rw = 2;
+		paint.drawRect(x - (px-x)/4, h - pkh, rw, pkh/2);
+	    } else if (n == 0 || n == 5) {
+		// C, F
+		if (px < w) {
+		    paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h);
+		}
+	    }
+
+            ppx = px;
+	    px = x;
+	}
+    }
 }
 
 void
--- a/layer/SpectrumLayer.h	Mon Jul 02 14:58:34 2007 +0000
+++ b/layer/SpectrumLayer.h	Tue Jul 03 12:46:18 2007 +0000
@@ -46,6 +46,10 @@
 
     virtual void paint(View *v, QPainter &paint, QRect rect) const;
 
+    virtual VerticalPosition getPreferredFrameCountPosition() const {
+	return PositionTop;
+    }
+
     virtual PropertyList getProperties() const;
     virtual QString getPropertyLabel(const PropertyName &) const;
     virtual PropertyType getPropertyType(const PropertyName &) const;
--- a/view/Pane.cpp	Mon Jul 02 14:58:34 2007 +0000
+++ b/view/Pane.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -397,14 +397,18 @@
     }
 
     Layer *topLayer = getTopLayer();
+    bool haveSomeTimeXAxis = false;
 
     const Model *waveformModel = 0; // just for reporting purposes
     for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
         --vi;
+        if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) {
+            haveSomeTimeXAxis = true;
+        }
         if (dynamic_cast<WaveformLayer *>(*vi)) {
             waveformModel = (*vi)->getModel();
-            break;
         }
+        if (waveformModel && haveSomeTimeXAxis) break;
     }
 
     m_scaleWidth = 0;
@@ -423,7 +427,7 @@
     if (m_centreLineVisible &&
         m_manager &&
         m_manager->shouldShowCentreLine()) {
-        drawCentreLine(sampleRate, paint);
+        drawCentreLine(sampleRate, paint, !haveSomeTimeXAxis);
     }
     
     paint.setPen(QColor(50, 50, 50));
@@ -628,7 +632,7 @@
 }
 
 void
-Pane::drawCentreLine(int sampleRate, QPainter &paint)
+Pane::drawCentreLine(int sampleRate, QPainter &paint, bool omitLine)
 {
     int fontHeight = paint.fontMetrics().height();
     int fontAscent = paint.fontMetrics().ascent();
@@ -637,13 +641,17 @@
     if (!hasLightBackground()) {
         c = QColor(240, 240, 240);
     }
+
     paint.setPen(c);
     int x = width() / 2;
-    paint.drawLine(x, 0, x, height() - 1);
-    paint.drawLine(x-1, 1, x+1, 1);
-    paint.drawLine(x-2, 0, x+2, 0);
-    paint.drawLine(x-1, height() - 2, x+1, height() - 2);
-    paint.drawLine(x-2, height() - 1, x+2, height() - 1);
+
+    if (!omitLine) {
+        paint.drawLine(x, 0, x, height() - 1);
+        paint.drawLine(x-1, 1, x+1, 1);
+        paint.drawLine(x-2, 0, x+2, 0);
+        paint.drawLine(x-1, height() - 2, x+1, height() - 2);
+        paint.drawLine(x-2, height() - 1, x+2, height() - 1);
+    }
     
     paint.setPen(QColor(50, 50, 50));
     
--- a/view/Pane.h	Mon Jul 02 14:58:34 2007 +0000
+++ b/view/Pane.h	Tue Jul 03 12:46:18 2007 +0000
@@ -92,7 +92,7 @@
 
     void drawVerticalScale(QRect r, Layer *, QPainter &);
     void drawFeatureDescription(Layer *, QPainter &);
-    void drawCentreLine(int, QPainter &);
+    void drawCentreLine(int, QPainter &, bool omitLine);
     void drawDurationAndRate(QRect, const Model *, int, QPainter &);
     void drawLayerNames(QRect, QPainter &);
     void drawEditingSelection(QPainter &);
--- a/view/PaneStack.cpp	Mon Jul 02 14:58:34 2007 +0000
+++ b/view/PaneStack.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -172,7 +172,22 @@
 Pane *
 PaneStack::getPane(int n)
 {
-    return m_panes[n].pane;
+    if (n < m_panes.size()) {
+        return m_panes[n].pane;
+    } else {
+        return 0;
+    }
+}
+
+int
+PaneStack::getPaneIndex(Pane *pane)
+{
+    for (int i = 0; i < getPaneCount(); ++i) {
+        if (pane == getPane(i)) {
+            return i;
+        }
+    }
+    return -1;
 }
 
 Pane *
--- a/view/PaneStack.h	Mon Jul 02 14:58:34 2007 +0000
+++ b/view/PaneStack.h	Tue Jul 03 12:46:18 2007 +0000
@@ -43,6 +43,7 @@
 
     int getPaneCount() const; // Returns only count of visible panes
     Pane *getPane(int n); // Of visible panes; I own the returned value
+    int getPaneIndex(Pane *pane); // so getPane(index)==pane; -1 if absent
 
     void hidePane(Pane *pane); // Also removes pane from getPane/getPaneCount
     void showPane(Pane *pane); // Returns pane to getPane/getPaneCount
--- a/view/View.h	Mon Jul 02 14:58:34 2007 +0000
+++ b/view/View.h	Tue Jul 03 12:46:18 2007 +0000
@@ -150,7 +150,9 @@
      * Return a layer, counted in stacking order.  That is, layer 0 is
      * the bottom layer and layer "getLayerCount()-1" is the top one.
      */
-    virtual Layer *getLayer(int n) { return m_layers[n]; }
+    virtual Layer *getLayer(int n) {
+        if (n < m_layers.size()) return m_layers[n]; else return 0;
+    }
 
     /**
      * Return the top layer.  This is the same as
--- a/widgets/LayerTree.cpp	Mon Jul 02 14:58:34 2007 +0000
+++ b/widgets/LayerTree.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -24,30 +24,6 @@
 #include <iostream>
 
 
-class ViewObjectAssoc : public QObject
-{
-public:
-    ViewObjectAssoc(View *v, QObject *o) :
-	QObject(0), view(v), object(o) {
-	++extantCount;
-	std::cerr << "ViewObjectAssoc (now " << extantCount << " extant)"
-		  << std::endl;
-    }
-
-    virtual ~ViewObjectAssoc() {
-	std::cerr << "~ViewObjectAssoc (now " << --extantCount << " extant)"
-		  << std::endl;
-    }
-
-    View *view;
-    QObject *object;
-
-    static int extantCount;
-};
-
-int ViewObjectAssoc::extantCount = 0;
-
-
 LayerTreeModel::LayerTreeModel(PaneStack *stack, QObject *parent) :
     QAbstractItemModel(parent),
     m_stack(stack)
@@ -64,43 +40,43 @@
 LayerTreeModel::data(const QModelIndex &index, int role) const
 {
     if (!index.isValid()) return QVariant();
-    if (role != Qt::DisplayRole) return QVariant();
-
-    std::cerr << "LayerTreeModel::data(" << &index << ", role " << role << ")" << std::endl;
 
     QObject *obj = static_cast<QObject *>(index.internalPointer());
-    
-    PaneStack *paneStack = dynamic_cast<PaneStack *>(obj);
-    if (paneStack) {
-	std::cerr << "node is pane stack" << std::endl;
-	return QVariant("Pane stack");
+    int row = index.row(), col = index.column();
+
+    Pane *pane = dynamic_cast<Pane *>(obj);
+    if (!pane) {
+        if (col == 0 && row < m_stack->getPaneCount()) {
+            switch (role) {
+            case Qt::DisplayRole:
+                return QVariant(QString("Pane %1").arg(row + 1));
+            case Qt::DecorationRole:
+                return QVariant(QIcon(QString(":/icons/pane.png")));
+            default: break;
+            }
+        }
     }
 
-    Pane *pane = dynamic_cast<Pane *>(obj);
-    if (pane) {
-	// need index of pane in pane stack
-	for (int i = 0; i < m_stack->getPaneCount(); ++i) {
-	    if (pane == m_stack->getPane(i)) {
-		std::cerr << "node is pane " << i << std::endl;
-		return QVariant(QString("Pane %1").arg(i + 1));
-	    }
-	}
-	return QVariant();
-    }
-
-    ViewObjectAssoc *assoc = dynamic_cast<ViewObjectAssoc *>(obj);
-    if (assoc) {
-	std::cerr << "node is assoc" << std::endl;
-	Layer *layer = dynamic_cast<Layer *>(assoc->object);
-	if (layer) {
-	    std::cerr << "with layer" << std::endl;
-	    return QVariant(layer->objectName());
-	}
-	Model *model = dynamic_cast<Model *>(assoc->object);
-	if (model) {
-	    std::cerr << "with model" << std::endl;
-	    return QVariant(model->objectName());
-	}
+    if (pane && pane->getLayerCount() > row) {
+        Layer *layer = pane->getLayer(row);
+        if (layer) {
+            if (col == 0) {
+                switch (role) {
+                case Qt::DisplayRole:
+                    return QVariant(layer->objectName());
+                case Qt::DecorationRole:
+                    return QVariant
+                        (QIcon(QString(":/icons/%1.png")
+                               .arg(layer->getPropertyContainerIconName())));
+                default: break;
+                }
+            } else if (col == 1) {
+                Model *model = layer->getModel();
+                if (model && role == Qt::DisplayRole) {
+                    return QVariant(model->objectName());
+                }
+            }
+        }
     }
 
     return QVariant();
@@ -129,145 +105,65 @@
 QModelIndex
 LayerTreeModel::index(int row, int column, const QModelIndex &parent) const
 {
-    std::cerr << "LayerTreeModel::index(" << row << ", " << column << ", "
-	      << &parent << ")" << std::endl;
+    // cell for a pane contains row, column, pane stack
+    // -> its parent is the invalid cell
+
+    // cell for a layer contains row, column, pane
+    // -> its parent is row, column, pane stack (which identify the pane)
 
     if (!parent.isValid()) {
-	// this is the pane stack
-	std::cerr << "parent invalid, returning pane stack as root" << std::endl;
-	if (column > 0) return QModelIndex();
+        if (row >= m_stack->getPaneCount() || column > 0) return QModelIndex();
 	return createIndex(row, column, m_stack);
     }
 
     QObject *obj = static_cast<QObject *>(parent.internalPointer());
-    
-    PaneStack *paneStack = dynamic_cast<PaneStack *>(obj);
-    if (paneStack) {
-	if (column > 0) return QModelIndex();
-	if (paneStack == m_stack && row < m_stack->getPaneCount()) {
-	    std::cerr << "parent is pane stack, returning a pane" << std::endl;
-	    return createIndex(row, column, m_stack->getPane(row));
-	}
-	std::cerr << "parent is wrong pane stack, returning nothing" << std::endl;
-	return QModelIndex();
+
+    if (obj == m_stack) {
+        Pane *pane = m_stack->getPane(parent.row());
+        if (!pane || parent.column() > 0) return QModelIndex();
+        return createIndex(row, column, pane);
     }
 
-    Pane *pane = dynamic_cast<Pane *>(obj);
-    if (pane) {
-	std::cerr << "parent is pane" << std::endl;
-	if (row < pane->getLayerCount()) {
-	    Layer *layer = pane->getLayer(row);
-	    if (column == 0) {
-		std::cerr << "parent is pane, returning layer" << std::endl;
-		ViewObjectAssoc *assoc = new ViewObjectAssoc
-//		    (const_cast<LayerTreeModel *>(this), pane, layer);
-		    (pane, layer);
-		return createIndex(row, column, assoc);
-	    } else {
-		std::cerr << "parent is pane, column != 0, returning model" << std::endl;
-		ViewObjectAssoc *assoc = new ViewObjectAssoc
-//		    (const_cast<LayerTreeModel *>(this), pane, layer->getModel());
-		    (pane, layer->getModel());
-		return createIndex(row, column, assoc);
-	    }		
-	}
-    }
-
-    std::cerr << "unknown parent, returning nothing" << std::endl;
     return QModelIndex();
 }
 
 QModelIndex
 LayerTreeModel::parent(const QModelIndex &index) const
 {
-    std::cerr << "LayerTreeModel::parent(" << &index << ")" << std::endl;
-
     QObject *obj = static_cast<QObject *>(index.internalPointer());
-    
-    PaneStack *paneStack = dynamic_cast<PaneStack *>(obj);
-    if (paneStack) {
-	std::cerr << "node is pane stack, returning no parent" << std::endl;
-	return QModelIndex();
-    }
 
     Pane *pane = dynamic_cast<Pane *>(obj);
     if (pane) {
-	std::cerr << "node is pane, returning pane stack as parent" << std::endl;
-	return createIndex(0, 0, m_stack);
+        int index = m_stack->getPaneIndex(pane);
+        if (index >= 0) return createIndex(index, 0, m_stack);
     }
 
-    ViewObjectAssoc *assoc = dynamic_cast<ViewObjectAssoc *>(obj);
-    if (assoc) {
-	View *view = assoc->view;
-	Pane *pane = dynamic_cast<Pane *>(view);
-	if (pane) {
-	    // need index of pane in pane stack
-	    for (int i = 0; i < m_stack->getPaneCount(); ++i) {
-		if (pane == m_stack->getPane(i)) {
-		    std::cerr << "node is assoc, returning pane " << i << " as parent" << std::endl;
-		    return createIndex(i, 0, pane);
-		}
-	    }
-	}
-	std::cerr << "node is assoc, but no parent found" << std::endl;
-	return QModelIndex();
-    }
-
-    std::cerr << "unknown node" << std::endl;
     return QModelIndex();
 }
 
 int
 LayerTreeModel::rowCount(const QModelIndex &parent) const
 {
-    std::cerr << "LayerTreeModel::rowCount(" << &parent << ")" << std::endl;
-
-    if (!parent.isValid()) {
-	std::cerr << "parent invalid, returning 1 for the pane stack" << std::endl;
-	return 1; // the pane stack
-    }
+    if (!parent.isValid()) return m_stack->getPaneCount();
 
     QObject *obj = static_cast<QObject *>(parent.internalPointer());
     
-    PaneStack *paneStack = dynamic_cast<PaneStack *>(obj);
-    if (paneStack) {
-	if (paneStack == m_stack) {
-	    std::cerr << "parent is pane stack, returning "
-		      << m_stack->getPaneCount() << " panes" << std::endl;
-	    return m_stack->getPaneCount();
-	} else {
-	    return 0;
-	}
-    }
- 
-    Pane *pane = dynamic_cast<Pane *>(obj);
-    if (pane) {
-	std::cerr << "parent is pane, returning "
-		  << pane->getLayerCount() << " layers" << std::endl;
-	return pane->getLayerCount();
+    if (obj == m_stack) {
+        Pane *pane = m_stack->getPane(parent.row());
+        if (!pane || parent.column() > 0) return 0;
+        return pane->getLayerCount();
     }
 
-    std::cerr << "parent unknown, returning 0" << std::endl;
     return 0;
 }
 
 int
 LayerTreeModel::columnCount(const QModelIndex &parent) const
 {
-    if (!parent.isValid()) {
-	std::cerr << "LayerTreeModel::columnCount: parent invalid, returning 2" << std::endl;
-	return 2;
-    }
+    if (!parent.isValid()) return 2;
 
     QObject *obj = static_cast<QObject *>(parent.internalPointer());
-    
-    Pane *pane = dynamic_cast<Pane *>(obj);
-    if (pane) {
-	std::cerr << "LayerTreeModel::columnCount: pane, returning 2" << std::endl;
-	return 2; // layer and model
-    }
-
-    std::cerr << "LayerTreeModel::columnCount: returning 1" << std::endl;
+    if (obj == m_stack) return 2;
 
     return 1;
 }