//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: layout.cpp,v 1.1.1.1 2003/10/29 10:05:28 wschweer Exp $
//  (C) Copyright 1999,2000 Werner Schweer (ws@seh.de)
//=========================================================

#include "layout.h"
#include "song.h"
#include "globals.h"
#include "symbols.h"
#include <assert.h>
#include <values.h>
#include <algorithm>
#include "event.h"

#define A_LYRICS 0

//---------------------------------------------------------
//   split
//    split note into two notes, connected with a tie
//---------------------------------------------------------

void SystemLayout::split(NoteItem* item, int t)
      {
      NoteItem* i2 = new NoteItem(*item);
      i2->setTieTo(0);
      i2->setGhost(true);
      i2->setTick(item->tick() + t);
      i2->setLen(item->len() - t);
      StaveRow* sr = item->staveRow();
      System* sys  = sr->system;
      int sr_no    = sr->no;

      if ((item->tick()+t) >= sys->endTick) {
            //
            // split note goes to next system
            //
            Page* page     = sys->page;
            SystemList* sl = &(page->systems);
            sr             = 0;
            for (iSystem is = sl->begin(); is != sl->end(); ++is) {
                  if (&*is == sys) {
                        ++is;
                        if (is != sl->end()) {
                              sys = &*is;
                              StaveRowList* srl = &(sys->staves);
                              iStaveRow isr = srl->begin();
                              for (int i = 0; i < sr_no; ++i) {
                                    if (isr == srl->end())
                                          break;
                                    ++isr;
                                    }
                              if (isr != srl->end())
                                    sr = &*isr;
                              else
                                    printf("stave Row %d missing\n", sr_no);
                              }
                        break;
                        }
                  }
            if (sr) {
                  Tie* tie = new Tie(item->tick(), item, i2);
                  i2->setTieFrom(tie);
                  sr->add(tie);
                  }
            }
      if (sr == 0) {
            printf("missing staveRow\n");
            return;
            }
      sr->add(i2);
      item->setLen(t);
      Tie* tie = new Tie(item->tick(), item, i2);
      i2->setTieFrom(tie);
      item->setTieTo(tie);
      item->staveRow()->add(tie);
      }

//---------------------------------------------------------
//   next
//---------------------------------------------------------

StaveRow* StaveRow::next() const
      {
      StaveRowList* srl = &system->staves;
      for (iStaveRow isr = srl->begin(); isr != srl->end(); ++isr) {
            if (&*isr == this) {
                  ++isr;
                  if (isr == srl->end()) {
                        printf("at end\n");
                        return 0;
                        }
                  return &*isr;
                  }
            }
      printf("..not found\n");
      return 0;
      }

//---------------------------------------------------------
//   Stave
//---------------------------------------------------------

Stave::Stave(MidiTrack* t, SplitMode m, int l)
      {
      track = t;
      mode  = m;
      nq    = track->noteQuant;
      rq    = track->restQuant;

      if (m == LOWER) {
            scale = track->scaleL;
            key   = track->keyL;
            }
      else {
            scale = track->scale;
            key   = track->key;
            }
      lines = l;
      }

//
//  Liste der möglichen enharmonischen Verwechslungen
//    für jeden Ton

static signed char table0[12][5] = {
      //  -   #  b   ## bb
      { 0, -1, 0, -1, 1  },   // c
      { 0,  0, 1, -1, 1  },   // #c
      { 1,  1, 1,  0, 2  },   // d
      { 2,  1, 2,  2, 3  },   // #d
      { 2,  2, 3,  1, 3  },   // e
      { 3,  2, 3,  2, 4  },   // f
      { 3,  3, 4,  2, 4  },   // #f
      { 4,  4, 4,  3, 5  },   // g
      { 4,  4, 5,  4, 5  },   // #g
      { 5,  5, 5,  4, 6  },   // a
      { 6,  5, 6,  5, 7  },   // #a
      { 6,  6, 7,  5, 7  }    // h
      };

//
// Notenlinie für jede Note einer Oktave,
//    abhängig von der Tonart (feste Versetzungszeichen)
//

static signed char table1[15][12] = {
      { 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7 },  // ces
      { 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7 },  // ges
      { 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7 },  // des
      { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7 },  // as
      { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6 },  // es
      { 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 },  // B
      { 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 },  // F

      { 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6 },  // C

      { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6 },  // G
      { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 },  // D
      { 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6 },  // A
      { 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6 },  // E
      { 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6 },  // H
      { 0, 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6 },  // fis
      { 0, 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6 }   // cis
      };

//---------------------------------------------------------
//   tick2xy
//    gibt die Position der Note auf Liniensystem zurück
//---------------------------------------------------------

int SystemLayout::tick2xy(int tick, MidiEvent* ev, int& xx, int& y, int& prefix)
      {
      int pitch = ev->pitch();
      int enh   = ev->enh();

      xx = ((tick-sbtick) * measureWidth)/ticksBar + sbx + xoffset;

      //
      //  wenn eine enharmonische Verwechslung vorgegeben ist,
      //  dann wird die Notenlinie aus Tabelle 0 gezogen,
      //  ansonsten in Abhängigkeit der Tonleiter aus Tabelle 1

      int l1;
      int mtone  = pitch % 12;
      int octave = pitch / 12;

      if (enh)
            l1 = table0[mtone][enh];
      else
            l1 = table1[stave->scale.idx()+7][mtone];

      while (l1 < 0) {
            l1 += 7;
            octave--;
            mtone += 12;
            }

      int l2 = 45 - l1 - (octave * 7) + stave->key.offset();

      char table2[8] = { 0, 2, 4, 5, 7, 9, 11, 12 };
      int offset     = mtone - table2[l1];

      switch (offset) {
            case -2:   prefix = 4; break;
            case -1:   prefix = 2; break;
            case 0:    prefix = 0; break;
            case 1:    prefix = 1; break;
            case 2:    prefix = 3; break;
            default:
                  printf("tick2xy: tick %d: internal error: bad val %d\n",
                     tick, offset);
                  assert(false);
                  break;
            }

      //
      //    feste Versetzungszeichen
      //
      int tab3[15][9] = {
            //c  d  e  f  g  a  h  c
            //------------------------
            { 2, 2, 2, 2, 2, 2, 2, 2 },    // ces
            { 2, 2, 2, 0, 2, 2, 2, 2 },    // ges
            { 0, 2, 2, 0, 2, 2, 2, 0 },    // des
            { 0, 2, 2, 0, 0, 2, 2, 0 },    // as
            { 0, 0, 2, 0, 0, 2, 2, 0 },    // es
            { 0, 0, 2, 0, 0, 0, 2, 0 },    // B
            { 0, 0, 0, 0, 0, 0, 2, 0 },    // F

            { 0, 0, 0, 0, 0, 0, 0, 0 },    // C

            { 0, 0, 0, 1, 0, 0, 0, 0 },    // G
            { 1, 0, 0, 1, 0, 0, 0, 1 },    // D
            { 1, 0, 0, 1, 1, 0, 0, 1 },    // A
            { 1, 1, 0, 1, 1, 0, 0, 1 },    // E
            { 1, 1, 0, 1, 1, 1, 0, 1 },    // H
            { 1, 1, 1, 1, 1, 1, 0, 1 },    // fis
            { 1, 1, 1, 1, 1, 1, 1, 1 }     // cis
            };

      assert((l2+45) >= 0 && (l2+45) < 128);

      int cprefix = tversatz[45+l2];
      if (cprefix == 0)
            cprefix = tab3[stave->scale.idx()+7][l1];
      if (cprefix == 5)
            cprefix = 0;
      if (cprefix) {
            if (prefix == cprefix)
                  prefix = 0;             // keine Versetzung
            else if (prefix == 0) {
                  prefix = 5;             // Auflösung
                  }
            }
      y = l2 * (6/2);         // lineD=6
      return l2;
      }

//---------------------------------------------------------
//   tick2len
//
//    gibt zu einer Tick-Position
//    die zugehörige maximale Länge einer Note in ticks
//---------------------------------------------------------

int SystemLayout::tick2len(int tick) const
      {
      int rtick = sbtick - tick;    // rel tick to begin of bar
      if (rtick == 0)
            return ticksBar;
      //
      // noten, die auf eine zählzeit fallen, sollen
      // beliebig lang sein

      int z, n;
      sigmap.timesig(tick, z, n);

      if ((rtick % (ticksBar/z)) == 0)
            return ticksBar-rtick;


      if ((ticksBar % 768) == 0) {                    // 1/1
            if ((rtick % 768) == 0)
                  return 768;
            }
      if ((ticksBar % 384) == 0) {                    // 1/2
            if ((rtick % 384) == 0)
                  return 384;
            }
      if ((ticksBar % 192) == 0) {                   // 1/4
            if ((rtick % 192) == 0)
                  return 192;
            }
      if ((ticksBar % 96) == 0) {                   // 1/8
            if ((rtick % 96) == 0)
                  return 96;
            }
      if ((ticksBar % 48) == 0) {                   // 1/16
            if ((rtick % 48) == 0)
                  return 48;
            }
      if ((ticksBar % 24) == 0) {                   // 1/32
            if ((rtick % 24) == 0)
                  return 24;
            }
      if ((ticksBar % 12) == 0) {                   // 1/64
            if ((rtick % 12) == 0)
                  return 12;
            }
      return 1;
      }

//---------------------------------------------------------
//   insertRest
//    at position tick insert rest with len "ticks"
//---------------------------------------------------------

void SystemLayout::insertRest(StaveRow* sr, int tick, int ticks)
      {
      int z, n;
      sigmap.timesig(tick, z, n);

      while (ticks) {
            int nlen = tick2len(tick);    // get max restlen at pos
            if (nlen > ticks)
                  nlen = ticks;
            int x = ((tick-sbtick+nlen/2) * measureWidth)/ticksBar + xoffset +sbx;

            // restrict nlen to valid rest lenghts:
            int nn = nlen/48;        // 1/32
            if (nlen != ticksBar) { // special case
                  if (nn >= 32)
                        nlen = 32*48;
                  else if (nn >= 16)
                        nlen = 16*48;
                  else if (nn >= 8)
                        nlen = 8*48;
                  else if (nn >= 4)
                        nlen = 4*48;
                  else if (nn >= 2)
                        nlen = 2*48;
                  else if (nn >= 1)
                        nlen = 48;
                  }
            sr->add(new RestItem(tick, QPoint(x, 0), nlen, ticksBar));
            tick  += nlen;
            ticks -= nlen;
            if (ticks < 0) {
                  printf("insertRest::TICKS < 0\n");
                  ticks = 0;
                  }
            }
      }

//---------------------------------------------------------
//   constructSystem
//---------------------------------------------------------

void SystemLayout::constructSystem(System* s)
      {
      measureWidth = s->measureWidth;

      for (iStaveRow i = s->staves.begin(); i != s->staves.end(); ++i)
            constructStaveRow(&*i);

      //-----------------------------------------
      //    set bars in first staveRow
      //-----------------------------------------

      StaveRow* sr = &s->staves.front();
      for (int i = s->startBar; i < s->endBar; ++i) {
            int tick = sigmap.bar2tick(i, 0, 0);
            int x    = s->xoffset + (i-s->startBar) * s->measureWidth;
            // gemogelt:
            int h = lineHeight1* (s->staves.size()-1) + lineD*(lines-1);
            if (song->showMeasureNo())
                  sr->add(new MeasureNo(tick, QPoint(x, -3), i,
                     song->measureNoFont()));
            if ((i - s->startBar) == 0)
                  sr->add(new MeasureItem(tick, QPoint(0, 0), h));
            x += s->measureWidth;
            //
            // last bar is of double bar type
            //
            int type = (i == endBar-1) ? 1 : 0;
            sr->add(new MeasureItem(tick, QPoint(x, 0), h, type));
            }
      }

//---------------------------------------------------------
//   constructPage
//---------------------------------------------------------

void SystemLayout::constructPage()
      {
      int yoff = curPage->systems.front().staves.front().offset.y()
         - mm2dot(topMargin);

      //-----------------------------------------
      //    title page
      //-----------------------------------------

      if (curPage->pageno == 0) {
            TitleItem* t1 = new TitleItem(0, song->name(),
               song->nameFont());
            t1->setPos(mm2dot(paperWidth/2.0), yoff/2 + mm2dot(topMargin));
            t1->setAlign(QPainter::AlignCenter);
            curPage->items.add(t1);

            Comp1Item* t2 = new Comp1Item(1, song->komponist1(),
               song->komponistFont());
            t2->setPos(mm2dot(paperWidth-rightMargin),
               yoff/2 + mm2dot(topMargin));
            t2->setAlign(QPainter::AlignVCenter|QPainter::AlignRight);
            curPage->items.add(t2);

            Comp2Item* t3 = new Comp2Item(2, song->komponist2(),
               song->komponistFont());
            t3->setPos(mm2dot(paperWidth-rightMargin),
               3*(yoff/4) + mm2dot(topMargin));
            t3->setAlign(QPainter::AlignVCenter|QPainter::AlignRight);
            curPage->items.add(t3);
            }
      for (iSystem i = curPage->systems.begin(); i != curPage->systems.end(); ++i)
            constructSystem(&*i);
      }

//---------------------------------------------------------
//   getPages
//---------------------------------------------------------

int SystemLayout::getPages() const
      {
      return pages.size();
      }

//---------------------------------------------------------
//   searchItem
//---------------------------------------------------------

ScoreItem* SystemLayout::searchItem(const QPoint& p) const
      {
      ScoreItem* item = curPage->items.find(p);
      if (item)
            return item;
      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  ScoreItem* item = k->items.find(p - k->offset);
                  if (item)
                        return item;
                  }
            }
      return 0;
      }

NoteItem* SystemLayout::searchItem(const MidiEvent* ev) const
      {
      SystemList* syl = &curPage->systems;
      for (iSystem i = syl->begin(); i != syl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  ScoreList* sl = &k->items;
                  iScore s;
                  for (s = sl->begin(); s != sl->end(); ++s) {
                        NoteItem* ni = dynamic_cast<NoteItem*>(s->second);
                        if (ni == 0)
                              continue;
                        if (ni->event() == ev)
                              return ni;
                        }
                  }
            }
      return 0;
      }

bool SystemLayout::searchItem(const ScoreItem* si, iScore& s, ScoreList*&sl) const
      {
      SystemList* syl = &curPage->systems;
      for (iSystem i = syl->begin(); i != syl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  sl = &k->items;
                  for (s = sl->begin(); s != sl->end(); ++s) {
                        if (s->second == si) {
                              return true;
                              }
                        }
                  }
            }
      return false;
      }

bool SystemLayout::searchItem(const StaveRow* sr, iStaveRow& k, StaveRowList*&stl) const
      {
      SystemList* syl = &curPage->systems;
      for (iSystem i = syl->begin(); i != syl->end(); ++i) {
            System* system = &*i;
            stl = &(system->staves);
            for (k = stl->begin(); k != stl->end(); ++k) {
                  if (sr == &*k)
                        return true;
                  }
            }
      return false;
      }

bool SystemLayout::searchItem(const System* sy, iSystem& i, SystemList*&syl) const
      {
      syl = &curPage->systems;
      for (i = syl->begin(); i != syl->end(); ++i) {
            if (sy == &*i)
                  return true;
            }
      return false;
      }

//---------------------------------------------------------
//   move
//---------------------------------------------------------

void SystemLayout::move(ScoreItem*, const QPoint& p)
      {
      ScoreItem* item = curPage->items.find(p);
      if (item) {
            item->setPos(p);
            return;
            }
      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  ScoreItem* item = k->items.find(p - k->offset);
                  if (item)
                        return;
                  }
            }
      }

//---------------------------------------------------------
//   xy2tick
//---------------------------------------------------------

int SystemLayout::xy2tick(const QPoint& p, int* ry, StaveRow** sr) const
      {
      int x = p.x();
      int y = p.y();

// printf("xy2tick(%d,%d):----------\n", x, y);

      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  int yo = k->offset.y() - (lineHeight1-4*lineD)/2;
                  if ((y >= yo)  && (y < (yo + lineHeight1))) {
                        int nx = x - k->offset.x() - system->xoffset;
                        if (nx < 0) {
                              if (ry)
                                    *ry = y - k->offset.y();
                              if (sr)
                                    *sr = &*k;
                              return -1;
                              }
                        int tick = system->startTick;
                        int mw   = system->measureWidth;
                        for (int m = system->startBar; m < system->startBar+measuresSystem; ++m) {
                              int ntick = sigmap.bar2tick(m+1, 0, 0);
                              if (nx < mw) {
                                    if (ry)
                                          *ry = y - k->offset.y();
                                    if (sr)
                                          *sr = &*k;
                                    return tick + nx*(ntick-tick)/mw;
                                    }
                              tick = ntick;
                              nx -= mw;
                              }
                        if (ry)
                              *ry = y - k->offset.y();
                        if (sr)
                              *sr = &*k;
                        return -1;
                        }
                  yo += lineHeight1;
                  }
            }
      return -1;
      }

//---------------------------------------------------------
//   xy2tick
//---------------------------------------------------------

int SystemLayout::xy2tick(const QPoint& p, int* ry, StaveRow* sr) const
      {
      int x  = p.x();
      int y  = p.y();

      System* system = sr->system;
      int nx         = x - mm2dot(leftMargin) - bracketWidth - system->xoffset;
      if (nx < 0)
            return system->startTick;
//            return -1;  // system->startTick;
      *ry      = y - sr->offset.y();
      int tick = system->startTick;
      int mw   = system->measureWidth;
      for (int m = system->startBar; m < system->startBar+measuresSystem; ++m) {
            int ntick = sigmap.bar2tick(m+1, 0, 0);
            if (nx < mw)
                  return tick + nx*(ntick-tick)/mw;
            tick = ntick;
            nx  -= mw;
            }
      return tick;
      }

//---------------------------------------------------------
//   collect
//    collect all Symbols with Event e
//---------------------------------------------------------

void SystemLayout::collect(ScoreList* list, MidiEvent* e)
      {
      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  ScoreList* ll = &k->items;
                  for (iScore l = ll->begin(); l != ll->end(); ++l) {
                        MidiEvent* event = l->second->event();
                        if (event == e)
                              list->add(l->second);
                        }
                  }
            }
      }

//---------------------------------------------------------
//   setNote
//---------------------------------------------------------

int SystemLayout::setNote(int, NoteItem* item)
      {
      int x, y, acc;
      int li = tick2xy(ctick, item->event(), x, y, acc);

// printf("note %d  ticksBar %d, x:%d\n", li, ticksBar, x);

      //
      // position note head
      //
      if (item->len() < division)
            x += ((item->len()/2) * measureWidth)/ticksBar;
      else
            x += ((division/2) * measureWidth)/ticksBar;


      item->setXPos(x);

      if (item->len() >= division*4)
            item->setHead(3);
      else if (item->len() >= division*2)
            item->setHead(2);
      else
            item->setHead(1);
      if (item->len() >= division*4)
            item->setStem(0);
      else {
            int st = item->event()->stem();
            if (st == 0)
                  item->stemDown(li <= 4);      // Notenhalsrichtung
            else
                  item->stemDown(st == 2);
            }
      item->setHLine(li);

      //---------------------------------------------------
      //  set bounding box of note head
      //---------------------------------------------------

      item->setBBox(x-3, li * (lineD/2) - 3, 7, 6);
      return acc;
      }

//---------------------------------------------------------
//   y2pitch
//---------------------------------------------------------

int SystemLayout::y2pitch(int y) const
      {
      int yoff = 0;
      StaveRow* staveRow = 0;

      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            StaveRowList* stl = &(system->staves);
            for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                  int yo = k->offset.y() - (lineHeight1-4*lineD)/2;
                  if ((y >= yo)  && (y < (yo + lineHeight1))) {
                        yoff = y - k->offset.y();
                        staveRow = &*k;
                        break;
                        }
                  yo += lineHeight1;
                  }
            if (staveRow)
                  break;
            }
      if (!staveRow)
            return -1;
      yoff += (18+9);
      NKey key = staveRow->stave->key;
      yoff -= (lineD/2) * key.offset();
      if (yoff < 0)
            return -1;
      int nnline = yoff % 21;
      int okt    = (yoff / 21) - 2;

      char table[21] = {
            70,69,68, 68,67,66, 66,65,65, 64,64,63, 63,62,62, 61,61,60, 60,59,59
            };
      return (table[nnline] - okt * 12);
      }


class Draw {
      QPainter& p;
   public:
      Draw(QPainter& ptr) : p(ptr) {}
      void operator() (std::pair<const int, ScoreItem*>i) { i.second->draw(p); }
      };

//---------------------------------------------------------
//   draw
//    draw a page
//---------------------------------------------------------

void SystemLayout::draw(QPainter& p, const QRect&)
      {
      ScoreList* scl = &(curPage->items);

      Draw draw_it(p);
      for_each(scl->begin(), scl->end(), draw_it);

      for (iSystem i = curPage->systems.begin(); i != curPage->systems.end(); ++i) {
            for (iStaveRow k = i->staves.begin(); k != i->staves.end(); ++k) {
                  ScoreList* scl = &(k->items);
                  p.save();
                  p.translate(double(k->offset.x()), double(k->offset.y()));
                  for_each(scl->begin(), scl->end(), draw_it);
                  p.restore();
                  }
            }
      }

//---------------------------------------------------------
//   setPage
//---------------------------------------------------------

void SystemLayout::setPage(int page)
      {
      for (iPage i = pages.begin(); i != pages.end(); ++i) {
            if (i->pageno == page) {
                  curPage = &*i;
                  return;
                  }
            }
      }


//---------------------------------------------------------
//   tick2pos
//---------------------------------------------------------

bool SystemLayout::tick2pos(int tick, int* x, int* y, int* h)
      {
      SystemList* sl = &curPage->systems;
      for (iSystem i = sl->begin(); i != sl->end(); ++i) {
            System* system = &*i;
            if (tick < system->startTick)
                  return false;
            if (tick >= system->endTick)
                  continue;
            *x = system->staves.front().offset.x() + system->xoffset;
            *y = system->staves.front().offset.y();
            int ntick = sigmap.bar2tick(system->startBar+1, 0, 0);
            int ticksBar = ntick - system->startTick;
            *x += ((tick-system->startTick) * system->measureWidth)/ticksBar;
            // gemogelt:
            *h = lineHeight1* (system->staves.size()-1) + lineD*(lines-1);
            return true;
            }
      return false;
      }

//---------------------------------------------------------
//   layout1
//---------------------------------------------------------

void SystemLayout::layout1()
      {
      measuresSystem = barsPage;  // default measures per system
      lineWidth      = mm2dot(paperWidth - leftMargin - rightMargin)
                        - bracketWidth;
      staves.clear();
      pages.clear();

      //---------------------------------------------------
      //    construct list of Stave´s
      //---------------------------------------------------

      int firstTick = MAXINT;
      int lastTick  = 0;

      assert(!_tracks.empty());

      for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
            MidiTrack* track =  (MidiTrack*)(*t);
            PartList* pl = track->parts();
//printf("track %p parts %d\n", track, track->parts()->size());
            if (!pl->empty()) {
                  int ft = pl->begin()->second->posTick();
                  int lt = pl->rbegin()->second->endTick();
                  if (ft < firstTick)
                        firstTick = ft;
                  if (lt > lastTick)
                        lastTick = lt;
                  }
            if (track->mode == Split) {
                  staves.push_back(Stave(track, UPPER, lines));
                  staves.push_back(Stave(track, LOWER, lines));
                  }
            else {
                  staves.push_back(Stave(track, NOSPLIT, lines));
                  }
            }
//printf("layout1 %d %d-%d\n", _tracks.size(), firstTick, lastTick);

      if (firstTick == MAXINT) {
            printf("SystemLayout::layout1: no parts found\n");
            abort();
            }
      int a, b;
      sigmap.tickValues(firstTick, &startBar, &a, &b);
      sigmap.tickValues(lastTick, &endBar, &a, &b);
      if (a || b)
            ++endBar;

      lineHeight1 = lineD * (lines-1) + staveSep;
      int nstaves = staves.size();

      if (nstaves == 1)
            lineHeight   = lineD * 13;
      else
            lineHeight = (lineHeight1 * nstaves) + systemSep;

      for (iStave i = staves.begin(); i != staves.end(); ++i) {
            stave = &*i;
            stave->cnq = stave->nq;
            stave->crq = stave->rq;
            }

      //---------------------------------------------------
      //    construct list of pages
      //---------------------------------------------------

      curBar = startBar;
      for (int page = 0; curBar < endBar; ++page) {
            pages.push_back(Page());
            curPage            = &pages.back();
            curPage->pageno    = page;
            int yoff = topOffset + mm2dot(topMargin);
            if (page == 0)
                  yoff += titleHeight;
            int h = mm2dot(paperHeight - bottomMargin) - yoff;
            int n = (h + staveSep + systemSep) / lineHeight;

//            if (n <= 0) {
//                  printf("h: %d %f %f %d\n", h, paperHeight, bottomMargin, yoff);
//                  printf("%f %f %d %d\n", h, staveSep, systemSep, lineHeight);
//                  }
assert(n > 0);
            curPage->startBar = curBar;
            curPage->endBar   = curBar + n * measuresSystem;
            if (curPage->endBar > endBar)
                  curPage->endBar = endBar;

            for (int i = 0; i < n; ++i) {
                  curPage->systems.push_back(System());
                  System& s  = curPage->systems.back();
                  s.page     = curPage;
                  s.startBar = curBar;
                  s.endBar   = curBar + measuresSystem;
                  if (s.endBar > endBar)
                        s.endBar = endBar;
                  s.startTick = sigmap.bar2tick(s.startBar, 0, 0);
                  s.endTick   = sigmap.bar2tick(s.endBar, 0, 0);

                  //-----------------------------------------
                  //   calculate left margin to first tick
                  //-----------------------------------------

                  s.xoffset = 0;
                  for (iStave is = staves.begin(); is != staves.end(); ++is) {
                        Stave* stave = &*is;
                        int k = keyOffset + stave->key.width() + stave->scale.width();
                        if (k > s.xoffset)
                              s.xoffset = k;
                        }
                  if (i == 0 && curPage->pageno == 0)
                        s.xoffset += 20;  // width of time signature
                  s.measureWidth = (lineWidth - s.xoffset)/measuresSystem;
                  s.xoffset      = lineWidth - (s.measureWidth * measuresSystem);

                  xoffset = s.xoffset;    // tmp value
                  measureWidth = s.measureWidth;

                  //-----------------------------------------
                  //    construct staveRows
                  //-----------------------------------------

                  int k = 0;
                  int xoff = mm2dot(leftMargin) + bracketWidth;
                  int tmpy = yoff;
                  for (iStave i = staves.begin(); i != staves.end(); ++i, ++k) {
                        stave = &*i;
                        QPoint offset(xoff, yoff);
                        s.staves.push_back(StaveRow(&s, stave, offset, k));
                        yoff += lineHeight1;
                        }
                  yoff = tmpy + lineHeight;
                  curBar = s.endBar;
                  if (curBar == curPage->endBar)
                        break;
                  }
            curBar = curPage->endBar;
            }
//      printLayout();
      }

//---------------------------------------------------------
//   layout2
//---------------------------------------------------------

void SystemLayout::layout2()
      {
      for (iPage i = pages.begin(); i != pages.end(); ++i) {
            Page* page = &*i;
            page->items.clear();
            for (iSystem is = page->systems.begin(); is != page->systems.end(); ++is) {
                  System* s = &*is;
                  for (iStaveRow k = s->staves.begin(); k != s->staves.end(); ++k) {
                        StaveRow* sr = &*k;
                        sr->items.clear();
                        }
                  }
            }
      for (iPage ip = pages.begin(); ip != pages.end(); ++ip) {
            curPage = &*ip;
            constructPage();
            }
      curPage = &pages.front();
      }

//---------------------------------------------------------
//   printLayout
//    debug utility
//---------------------------------------------------------

void SystemLayout::printLayout() const
      {
      for (ciPage i = pages.begin(); i != pages.end(); ++i) {
            const Page* page = &*i;
            printf("Page %d, bar %d - %d\n",
               page->pageno, page->startBar, page->endBar);
            for (ciSystem is = page->systems.begin(); is != page->systems.end(); ++is) {
                  const System* s = &*is;
                  printf("  System %d(%d)-%d(%d) off %d, mw %d\n",
                        s->startBar, s->startTick, s->endBar, s->endTick,
                        s->xoffset, s->measureWidth);
                  for (ciStaveRow k = s->staves.begin(); k != s->staves.end(); ++k) {
                        const StaveRow* sr = &*k;
                        printf("    StaveRow %d, xoff %d yoff %d\n",
                           sr->no, sr->offset.x(), sr->offset.y());
                        }
                  }
            }
      }

//---------------------------------------------------------
//   constructStaveRow
//---------------------------------------------------------

void SystemLayout::constructStaveRow(StaveRow* sr)
      {
      stave            = sr->stave;
      System* system   = sr->system;
      int etick        = system->endTick;
      MidiTrack* track = stave->track;
      PartList* parts  = track->parts();

      curBar           = system->startBar;
      stick            = system->startTick;
//      ticksBar         = song->ticksMeasure(stick);
      xoffset          = system->xoffset;
      measureWidth     = system->measureWidth;

      sr->add(new KeyItem(QPoint(keyOffset, 0), stick, &(stave->key)));
      int x = keyOffset + stave->key.width();
      sr->add(new Clef(QPoint(x, 0), stick, &(stave->scale), &(stave->key)));

      sr->add(new SystemItem(stick, stave->lines,
        measureWidth * (system->endBar - system->startBar) + xoffset));

      //-----------------------------------------
      //  items on first system on page
      //-----------------------------------------

      if (system == &curPage->systems.front()) {

            //-----------------------------------------
            //  track name
            //-----------------------------------------

           if ((stave->mode != LOWER) && (song->showTrackname())) {
                  TrackNameItem* ev = new TrackNameItem(stick, track->name(),
                     song->tracknameFont());
                  ev->setPos(5, -10);
                  sr->add(ev);
                  }

            //---------------------------------------------------
            //  time signature
            //    only on first page
            //---------------------------------------------------

            if (curPage->pageno == 0) {
                  int z, n;
                  sigmap.timesig(stick, z, n);
                  int type = 2;
                  if ((z == 4) && (n == 4))
                        type = 0;
                  if ((z == 2) && (n == 2))
                        type = 1;
                  int x = xoffset - 20;      // width == 20
                  sr->add(new TimesigItem(QPoint(x, 0), stick, type, z, 0, 0, n));
                  }
            }

      //-----------------------------------------
      //  place Bracket in split system
      //-----------------------------------------

      if (stave->mode == UPPER) {
            int h = lineHeight1* (system->staves.size()-1) + lineD*(lines-1);
            sr->add(new BracketItem(stick, h));
            }

      //-----------------------------------------
      //  place Events
      //-----------------------------------------

      for (ciPart p = parts->begin(); p != parts->end(); ++p) {
            MidiPart* part = (MidiPart*)p->second;
            if (part->posTick() >= etick)
                  break;
            if ((part->posTick()+part->lenTick()) < stick)
                  continue;
            EventList* el = part->events();
            for (iEvent i = el->lower_bound(stick);  i != el->lower_bound(etick); ++i) {
                  MidiEvent* e = (MidiEvent*)i->second;
                        //
                        // symbols only appear in upper stave
                        //
                  if (e->type() == MidiEvent::Symbol && (stave->mode == NOSPLIT || stave->mode == UPPER)) {
                        QPoint p(e->dataC()+xoffset, e->dataB());
                        sr->add(new Symbol(p, e->posTick(), e, part));
                        }
                  else if (e->type() == MidiEvent::Note) {
                        if ((stave->mode == NOSPLIT)
                           || ((stave->mode == UPPER) && (e->voice() == 1))
                           || ((stave->mode == LOWER) && (e->voice() == 2))
                           || ((stave->mode == UPPER) && (e->voice() == 0) && (e->pitch() >= track->splitpoint))
                           || ((stave->mode == LOWER) && (e->voice() == 0) && (e->pitch() < track->splitpoint))) {
                              int cnq  = stave->cnq;
                              int tick = ((e->posTick() + cnq/2)/cnq)*cnq;
                              NoteItem* ev = new NoteItem(tick, e, part);
                              ev->quantize(stick, cnq, stave->crq);
                              sr->add(ev);
                              for (iAttribute a = e->beginAttr(); a != e->endAttr(); ++a) {
                                    Attribute* at = &*a;
                                    if (at->type() == A_LYRICS) {
                                          QPoint pos = at->pos();
                                          LyricsItem* ly
                                             = new LyricsItem(pos, at, ev, song->lyricsFont());
                                          sr->add(ly);
                                          }
                                    }
                              }
                        }
                  else if (e->type() == MidiEvent::Quantize) {
                        int voice = e->voice();
                        if ((stave->mode == NOSPLIT)
                           || ((voice == 1) && (stave->mode == UPPER))
                           || (voice == 2) && (stave->mode == LOWER)) {
                              int tick = e->posTick();
                              QuantItem* ev = new QuantItem(tick, e, part, e->dataA(),
                                 e->dataB());
                              stave->cnq = e->dataA();
                              stave->crq = e->dataB();
                              ev->setPos(xoffset, 50);
                              sr->add(ev);
                              }
                        }
                  }

            }

      //---------------------------------------------------
      //    place rests and notes
      //---------------------------------------------------

      sbx = 0;                  // x-position of current bar
      int bar = system->startBar;
      sbtick  = sigmap.bar2tick(bar, 0, 0);
      for (; bar < system->endBar; ++bar) {
            int ntick = sigmap.bar2tick(bar+1, 0, 0);
            ctick     = sbtick;
            ticksBar  = ntick - ctick;

            memset(tversatz, 0, sizeof(tversatz));

            iScore istart = sr->items.lower_bound(ctick);
            iScore iend   = sr->items.lower_bound(ntick);
            for (iScore i = istart; i != iend; ++i) {
                  Symbol* symbol = dynamic_cast<Symbol*>(i->second);
                  if (symbol) {
                        int rt = symbol->tick() - sbtick;
                        int dx = sbx + (rt * measureWidth)/ticksBar;
                        symbol->moveX(dx);
                        continue;
                        }
                  QuantItem* qi = dynamic_cast<QuantItem*>(i->second);
                  if (qi) {
                        int rt = qi->tick() - sbtick;
                        int dx  = sbx + (rt * measureWidth)/ticksBar;
                        qi->moveX(dx);
                        continue;
                        }
                  NoteItem* item = dynamic_cast<NoteItem*>(i->second);
                  if (item == 0)
                        continue;
                  int tick  = item->tick();

                  //---------------------------------------------
                  //  insert rests
                  //---------------------------------------------

                  int rest = tick - ctick;
                  if (rest > 0)
                        insertRest(sr, ctick, rest);
                  ctick = tick;

                  //---------------------------------------------
                  //    Note positionieren
                  //    - wenn Note zu lang, dann splitten und mit
                  //      Bindebogen verbinden
                  //---------------------------------------------

                  int len  = item->len();
                  int nlen = tick2len(tick);

                  if (nlen > len)
                       nlen = len;

                  if (nlen < len) {
                        split(item, nlen);
                        }
                  iScore ns = i;
                  ++ns;
                  NoteList chord;

                  //
                  // Rest des Taktes nach überlappenden Noten untersuchen
                  //

                  int acc = setNote(tick, item);
                  if (acc) {
                        tversatz[45 + item->hline()] = acc;
                        item->setAccidental(acc);
                        }

                  chord.push_back(item);
                  for (; ns != iend; ++ns) {
                        NoteItem* nitem = dynamic_cast<NoteItem*>(ns->second);
                        if (nitem == 0)
                              continue;
                        int ntick = nitem->tick();
                        if (ntick >= ctick + nlen)
                              break;
                        if (ntick == ctick) {
                              setNote(ntick, nitem);
                              chord.push_back(nitem);
                              i = ns;
                              }
                        else {
                              nlen = ntick-ctick;
                              split(item, nlen);
                              break;
                              }
                        }

                  //
                  //  höchste, tiefste und kürzeste Akkordnote
                  //  suchen
                  //
                  int min    = MAXINT;
                  int max    = -MAXINT;
                  int minLen = min;
                  for (iNote k = chord.begin(); k != chord.end(); ++k) {
                        int len = (*k)->len();
                        if (len < minLen)
                              minLen = len;
                        int l = (*k)->hline();
                        if (l < min)
                              min = l;
                        if (l > max)
                              max = l;
                        }
                  //
                  //  alle Akkordnoten auf minLen splitten
                  //
                  for (iNote k = chord.begin(); k != chord.end(); ++k) {
                        int len = (*k)->len();
                        if (len > minLen) {
                              split(*k, minLen);
                              }
                        }

                  bool stemDown;
                  int startVal;
                  int amax = (max - 4) < 0 ? -(max-4) : (max-4);
                  int amin = (min - 4) < 0 ? -(min-4) : (min-4);
                  if (amax > amin) {
                        stemDown = max < 4;
                        startVal = max;
                        }
                  else {
                        stemDown = min < 4;
                        startVal = min;
                        }
                  //
                  // stems & ties
                  //
                  for (iNote k = chord.begin(); k != chord.end(); ++k) {
                        if ((*k)->hline() == startVal) {
                              (*k)->setStem((max - min) * 3 + 20);
                              (*k)->stemDown(stemDown);
                              }
                        else
                              (*k)->setStem(0);
                        if ((*k)->tieTo()) {
                              (*k)->tieTo()->setUp(stemDown);
                              }
                        setNote(ctick, *k);
                        }
                  ctick += nlen;
                  }


            int rest = ntick - ctick;
            if (rest > 0)
                  insertRest(sr, ctick, rest);
            sbx += measureWidth;
            sbtick = ntick;

            //---------------------------------------------
            //    Balken
            //---------------------------------------------

            int t1 = sigmap.bar2tick(bar, 0, 0);
            int z, n;
            sigmap.timesig(t1, z, n);

            for (int i = 0; i < z; ++i) {
                  int t2 = sigmap.bar2tick(bar, i+1, 0);

                  iScore istart = sr->items.lower_bound(t1);
                  iScore iend   = sr->items.lower_bound(t2);
                  int count = 0;
                  int min   = MAXINT;     // pitch
                  int max   = -MAXINT;
                  NoteItem* firstItem = 0;
                  NoteItem* lastItem  = 0;
                  for (iScore i = istart; i != iend; ++i) {
                        NoteItem* item = dynamic_cast<NoteItem*>(i->second);
                        if (item == 0)
                              continue;
                        if (firstItem == 0)
                              firstItem = item;
                        else
                              lastItem = item;
                        int l = item->hline();
                        if (l > max)
                              max = l;
                        if (l < min)
                              min = l;
                        ++count;
                        }
                  if (count < 2 || (firstItem->tick() == lastItem->tick())) {
                        t1 = t2;
                        continue;
                        }

                  Beam* beam = new Beam(t1, min, max);
                  sr->add(beam);

                  for (iScore i = istart; i != iend; ++i) {
                        NoteItem* item = dynamic_cast<NoteItem*>(i->second);
                        if (item == 0)
                              continue;
                        beam->addNote(item);
                        item->setStem(0);
                        item->setFlags(false);
                        }
                  t1 = t2;
                  }
            }
      }

//---------------------------------------------------------
//   SystemLayout
//---------------------------------------------------------

SystemLayout::SystemLayout()
      {
      curPage      = 0;
      lines        = 5;  // default number of lines per system
      topOffset    = 50; // top distance to first stave
      lineD        = 6;  // line distance
      keyOffset    = 4;  // distance left margin to key
      bracketWidth = 10; // width of system bracket/brace
      titleHeight  = 60;
      systemSep    = lineD*(lines-1);
      staveSep     = lineD*6;
      }

//---------------------------------------------------------
//   setTracks
//    create new list of tracks from previously
//    collected track serial numbers
//---------------------------------------------------------

void SystemLayout::updateTracks(std::list<int>& tl)
      {
      _tracks.clear();
      TrackList* st = song->tracks();
      for (iTrack t = st->begin(); t != st->end(); ++t) {
            Track* track = *t;
            for (std::list<int>::const_iterator i = tl.begin(); i != tl.end(); ++i) {
                  if (*i == track->sn())
                        _tracks.push_back(track);
                  }
            }
      }

//---------------------------------------------------------
//   setSelection
//---------------------------------------------------------

void SystemLayout::updateSelection(ScoreCanvas* scoreCanvas)
      {
      for (iPage i = pages.begin(); i != pages.end(); ++i) {
            Page* page = &*i;
//            page->items.clear();
            for (iSystem is = page->systems.begin(); is != page->systems.end(); ++is) {
                  System* s = &*is;
                  for (iStaveRow k = s->staves.begin(); k != s->staves.end(); ++k) {
                        StaveRow* sr = &*k;
                        for (iScore is = sr->items.begin(); is != sr->items.end(); ++is) {
                              MidiEvent* event = is->second->event();
                              if (event && event->selected())
                                    scoreCanvas->selectItem(is->second);
                              else
                                    is->second->setState(ScoreItem::Normal);
                              }
                        }
                  }
            }
      }

