/***************************************************************************
 * trackprint.cpp: implementation of TrackPrint class
 *
 * This file is part of KGuitar, a KDE tabulature editor
 *
 * copyright (C) 2003-2004 the KGuitar development team <mrgwen@gmail.com>
 ***************************************************************************/

/***************************************************************************
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * See the file COPYING for more information.
 ***************************************************************************/

#include "trackprint.h"

#include "../tablature/tabbeateffects.h"
#include "../tablature/tabchord.h"
#include "../tablature/tabduration.h"

#include "../tablature/tabbend.h"
#include "../tablature/tabdynamic.h"

#include "../tablature/tabgracenotetransition.h"
#include "../tablature/tabgracenote.h"

#include "../tablature/tabexception.h"
#include "../tablature/tabharmonic.h"
#include "../tablature/tabslide.h"
#include "../tablature/tabtrill.h"
#include "../tablature/tabnoteeffect.h"

#include "../tablature/tabnote.h"

#include "../tablature/tabmixtablechanges.h"
#include "../tablature/tabtimes.h"
#include "../tablature/tabbarproperties.h"

#include "../tablature/tabbar.h"

#include "../tablature/trackmidiproperties.h"
#include "../tablature/timesignature.h"
#include "../tablature/trackproperties.h"

#include "../tablature/songkey.h"
#include "../tablature/tablyrics.h"
#include "../tablature/tabproperties.h"

#include "accidentals.h"
#include "global.h"
#include "settings.h"
#include "../tablature/tabtrack.h"
#include "tunings.h"

/*
	TrackPrint constructor. Load some fonts and load some pixmaps used to draw
	the effects.
*/
TrackPrint::TrackPrint(int resolution)
: tripletCount(0),
  crocheCount(0),
  dblCrocheCount(0),
  trlCrocheCount(0),
  lineSpaces(8),
  painter(0),
  dpi(resolution),
  size(0, 0, 0, 0),
  fontTitle(0)
{
	QString temp;
	
	pLnBl = QPen(Qt::black, 1);
	pLnWh = QPen(Qt::white, 1);
	
	silent = new QPixmap*[7];
	
	// Load the silents pixmaps
	//
	for (char i = 0; i < 7; i++)
		silent[i] = new QPixmap(locate("data", "ktabedit/pics/silent" + temp.setNum(i + 1) + ".png"));
	
	// Load the bend pixmap
	//
	bend = new QPixmap(locate("data", "ktabedit/pics/bend.png"));
	
	// Load the bend and release pixmap
	//
	bendRelease = new QPixmap(locate("data", "ktabedit/pics/bendrelease.png"));
	
	// Load the pre bend and release pixmap
	//
	preBendRelease = new QPixmap(locate("data", "ktabedit/pics/prebendrelease.png"));
	
	// Load some fonts
	//
	fTBar1  = new QFont(KGlobalSettings::generalFont());
	if (fTBar1->pointSize() == -1)
		fTBar1->setPixelSize((int) ((double) fTBar1->pixelSize() * 0.8));
	else
		fTBar1->setPointSizeFloat(fTBar1->pointSizeFloat() * 0.9);
	
	fTBar2  = new QFont(*fTBar1);
	if (fTBar2->pointSize() == -1)
		fTBar2->setPixelSize((int) ((double) fTBar2->pixelSize() * 0.7));
	else
		fTBar2->setPointSizeFloat(fTBar2->pointSizeFloat() * 0.9);
	
	fTSig   = new QFont(*fTBar1);
	if (fTSig->pointSize() == -1)
		fTSig->setPixelSize((int) ((double) fTSig->pixelSize() * 1.4));
	else
		fTSig->setPointSizeFloat(fTSig->pointSizeFloat() * 1.8);
	
	fTSig->setBold(true);
	
	fFeta   = new QFont(*fTBar1);
	fFetaNr = new QFont(*fTBar1);
	
	// Get the height of the font to get the spaces 
	// between the lines.
	//
	QFontMetrics fm(*fTBar1);
	lineSpaces = fm.boundingRect("8").height() + 1;
	
	// Font used for the title : the size of the font is 0.700 mm
	// or 0.2756 in and the size is converted in px
	//
	fontTitle = new QFont("Times", mmToPixel<int>(0.700));
	
	// Font used for the sub title : the size of the font is 0.350 mm
	// or 0.1378 in and the size is converted in px
	//
	fontSubTitle = new QFont("Times", mmToPixel<int>(0.350));
	
	// Font used for the text : the size of the font is 0.100 mm
	// or 0.0394 in and the size is converted in px
	//
	fontText = new QFont("Times", mmToPixel<int>(0.100));
}

TrackPrint::~TrackPrint()
{
	delete fTBar1;
	delete fTBar2;
	delete fTSig;
	delete fFeta;
	delete fFetaNr;
	
	for (char i = 0; i < 7; i++)
		delete silent[i];
	delete silent;
	
	delete bend;
	delete bendRelease;
	delete preBendRelease;
}

inline void TrackPrint::drawText(unsigned int x, unsigned int y, const QString& string)
{
	painter->setFont(*fTBar1);
	painter->drawText(x, y, string);
}

inline void TrackPrint::drawText(unsigned int x, unsigned int y, QFont& font, const QString& string)
{
	painter->setFont(font);
	painter->drawText(x, y, string);
}

// Draw a margins page
//
void TrackPrint::drawPage(uchar pageNb)
{
	painter->drawRect(size);
}

// Print the title of the song :
//   - center the title
//
void TrackPrint::printTitle(const QString& title)
{
	// Get the height of the font to get the spaces 
	// between the lines.
	//
	QFontMetrics fm(*fontTitle);
	
	// Center the text
	//
	const int x =  size.left() + ((size.width() - fm.boundingRect(title).width()) / 2);
	const int y = size.top() + mmToPixel<int>(0.500) + fm.boundingRect(title).height();
	
	// Draw the text
	//
	drawText(x, y, *fontTitle, title);
}

// Print Words ... by and Music by ...
//
void TrackPrint::printWordsAndMusic(const QString& words, const QString& music)
{
	// Get the height of the font to get the spaces 
	// between the lines.
	//
	QFontMetrics fm(*fontSubTitle);
	
	// Make the text
	//
	QString text;
	
	if (words == music)
		text = "Words and Music by " + words;
	else
		text = "Words by " + words + " and Music by " + music;
	
	// Center the text
	//
	const int x =  size.left() + ((size.width() - fm.boundingRect(text).width()) / 2);
	const int y = size.top() + mmToPixel<int>(1.500) + fm.boundingRect(text).height();
	
	// Draw the text
	//
	drawText(x, y, *fontSubTitle, text);
}

// Print tuning
//
void TrackPrint::printTuning(const TrackProperties& trackProperties)
{
	// Print the word : "Tune Down 1/2 Step:"
	//                        Up   1 Step:"
	//
	QString text;
	
	// Get the height of the font to get the spaces 
	// between the lines.
	//
	QFontMetrics fm(*fontText);
	
	for (uchar i = 0; lib_tuning[i].strings != 0; i++) {
		if (lib_tuning[i].strings == trackProperties.nbStrings()) {
			bool find;
			
			for (uchar j = 0; (find = j < trackProperties.nbStrings()); j++) {
				if (lib_tuning[i].shift[j] != trackProperties.tune(j))
					break;
			}
			
			if (find == false) {
				text = lib_tuning[i].name;
				break;
			}
		}
	}
	
	if (!text.isEmpty()) {
		// Center the text
		//
		const int x = size.left() + mmToPixel<int>(1.500);
		const int y = size.top() + mmToPixel<int>(3.000) + fm.boundingRect(text).height();
		
		// Draw the text
		//
		drawText(x, y, *fontText, text);
	}
	
	// Draw the tuning circles
	//
	int x = size.left() + mmToPixel<int>(1.500);
	int y = 0;
	
	if (text.isEmpty())
		y = size.top() + mmToPixel<int>(3.000);
	else
		y = size.top() + mmToPixel<int>(3.000) + fm.boundingRect(text).height();
	
	for (uchar i = 0; i < (trackProperties.nbStrings() / 3); i++) {
		for (uchar j = i; j < trackProperties.nbStrings(); j+=3) {
			const int numHeight = fm.boundingRect("8").height() + mmToPixel<int>(0.100);
			const int width = fm.boundingRect(Settings::noteName(trackProperties.tune(i) % 12)).width();
			
			painter->drawArc(x, y, numHeight, numHeight, 0, 16 * 360);
		}
	}
}

void TrackPrint::setPosition(const QPoint& p)
{
	position = p;
}

QPoint TrackPrint::getPosition() const
{
	return position;
}

// draw bar lines at xpos,ypos width w for all strings of track trk
//
void TrackPrint::drawBarLines(int width, const TabTrack& track)
{
	nbStrings = track.nbStrings();
	
	painter->setPen(pLnBl);
	
	painter->drawLine(position.x(), position.y(), position.x(), position.y() + (nbStrings - 1) * lineSpaces);
	painter->drawLine(position.x() + width - 1, position.y(), position.x() + width - 1, position.y() + (nbStrings - 1) * lineSpaces);
	
	// horizontal lines from position.x() to position.x()+w-1
	//
	for (int i = 0; i < nbStrings; i++) {
		painter->drawLine(position.x(), position.y() + i * lineSpaces, position.x() + width - 1, position.y() + i * lineSpaces);
	}
}

/*
	Draw the single end bar or repeat end bar.
*/
void TrackPrint::drawEndBarLines(int width)
{
}

// draw:
// - key + keysig (if fbol or changed)
// - timesig      (if first bar of track or changed)
// return xpixels used
// actually draws only when doDraw is true
//
void TrackPrint::drawKeySigTimeSig(int bn, bool drawKey, bool drawSignature, const TabTrack &tabTrack)
{
	if (drawKey)
		this->drawKey(tabTrack);
	
// 	res+=drawKeySig(tabTrack, doDraw);
	
	if (drawSignature)
		drawTimeSig(bn, tabTrack);
	
	position.setX(position.x() + 10);
}

void TrackPrint::drawRepeatStart()
{
	drawText(position.x() + 5, position.y() + nbStrings * 4, ":");
}

void TrackPrint::drawAlternateEndings(unsigned int from, unsigned int to)
{
	QString i;
	
	drawText(position.x() + 10, position.y() - 10, i.setNum(from) + " to " + i.setNum(to));
}

void TrackPrint::drawRepetitions(unsigned int x, unsigned int size)
{
	QString i;
	
	drawText(position.x() + size * 26, position.y() - 10, i.setNum(x) + 'x');
	drawText(position.x() + size * 26 - 5, position.y() + nbStrings * 4, ":");
}

void TrackPrint::drawDoubleBar(unsigned int size)
{
	painter->setPen(pLnBl);
	
	painter->drawLine(position.x() + size * 26, position.y(), position.x() + size * 26, position.y() + (nbStrings - 1) * lineSpaces);
	painter->drawLine(position.x() + size * 26 + 5 - 1, position.y(), position.x() + size * 26 + 5 - 1, position.y() + (nbStrings - 1) * lineSpaces);
}

// Draw the tempo
//
void TrackPrint::drawTempo(uint tempo)
{
	QString temp;
	
	QPixmap note(locate("data", "ktabedit/icons/hicolor/22x22/actions/note4.png"));
	
	painter->drawPixmap(position.x(), position.y() - 20, note);
	drawText(position.x() + 20, position.y() - 10, "= " + temp.setNum(tempo));
}

// draw bar bn's contents starting at xpos,ypos adding extra space es
// also update selection x coordinates for trackview
//
void TrackPrint::drawBar(int bn, const TabBar& tempBar)
{
	int i, j, tuplet, tupletCount;
	float timeCount;
	QPoint oldPos;
	QString temp;
	
	// Reset the n tuplet counter
	//
	tripletCount   = 0;
	
	// Set the eighth time counter to 0
	//
	crocheCount    = 0;
	
	// Set the sixteenth time counter to 0
	//
	dblCrocheCount = 0;
	
	// Set the thirthsecond time counter to 0
	//
	trlCrocheCount = 0;
	
	// Space between the lines
	//
	lineSpaces     = 8;
	
	time[0] = tempBar.timeSignature(0);
	time[1] = tempBar.timeSignature(1);
	
	durationCount = crocheCount = dblCrocheCount = trlCrocheCount = 0;
	
	// Save the position
	//
	oldPos = position;
	
	// Draw thebar number
	//
	drawText(position.x(), position.y() - 7, temp.setNum(bn + 1));
	
	// Draw mix changes
	//
	drawChanges(position.x(), position.y() - 7, tempBar);
	
	for (i = 0; i < tempBar.count(); i++) {
		TabTimes tempTimes = tempBar.times(i);
		
		if (!tempTimes.isRest()) {
			if (i + 1 < tempBar.count()) {
				drawDuration(tempTimes, tempBar.times(i + 1));
				drawVerticalLine(tempTimes);
				drawTimes(tempTimes);
				drawEffect(tempTimes, tempBar.times(i + 1));
			} else {
				TabTimes nextTimes = tempTimes;
				nextTimes.setDuration(Whole);
				
				drawDuration(tempTimes, nextTimes);
				drawVerticalLine(tempTimes);
				drawTimes(tempTimes);
				drawEffect(tempTimes, nextTimes);
			}
		} else {
			position.setX(position.x() + 1);
			
// 			if (i + 1 < tempBar.count()) {
// 				drawDuration(tempTimes, tempBar.times(i + 1));
// 			} else {
// 				TabTimes nextTimes = tempTimes;
// 				nextTimes.setDuration(480);
				
// 				drawDuration(tempTimes, nextTimes);
// 			}
			
			position.setX(position.x() - 1);
			
			drawRest(position.x(), position.y(), tempTimes);
	 	}
		
		drawDotted(tempTimes);
		
		position.setX(position.x() + 26);
	}
	
	tupletCount = tuplet = 0;
	timeCount = 0.0;
	
	// Restore the position
	//
	position = oldPos;
	
	for (i = 0; i < tempBar.count(); i++) {
		TabTimes tempTimes = tempBar.times(i);
		
		// Draw the old tuplet
		//
		if (tempTimes.nTuplet() != tuplet) {
			
			// Is it a tuplet ?
			//
			if (tuplet != 0) {
				if (tupletCount > tuplet) {
					drawTuplet(i - tupletCount, tupletCount, tuplet);
				} else {
					// Draw only the tuplet number
					//
					for (j = 0; j < tupletCount; j++) {
						unsigned int queueLength = nbStrings * 8 + 2;
						
						drawText(position.x() + (i - tupletCount + j) * 26, position.y() + queueLength + lineSpaces, temp.setNum(tuplet + 1));
					}
				}
			}
			
			 if ((tuplet = tempTimes.nTuplet()) != 0) {
				tupletCount = 1;
				timeCount+=tempTimes.duration();
				
				if (tempTimes.isDotted() == true)
					timeCount+=(tempTimes.duration() / 2);
			} else {
				tupletCount = 0;
				timeCount = 0.0;
			}
		} else {
			tupletCount++;
			
			// Check the number of tuplet times
			//
			timeCount+=tempTimes.duration();
			
			if (tempTimes.isDotted() == true)
				timeCount+=(tempTimes.duration() / 2);
			
			// Need at least tuplet + 1 elements for a complete tuplet
			//
			if (tuplet != 0 && tupletCount >= tuplet + 1) {
				if (timeCount == 15 * (tuplet + 1)   || timeCount == 30 * (tuplet + 1)   ||
					timeCount == 60 * (tuplet + 1)   || timeCount == 120 * (tuplet + 1)  ||
					timeCount == 240 * (tuplet + 1)  || timeCount == 480 * (tuplet + 1)  ||
					timeCount == 22.5 * (tuplet + 1) || timeCount == 45 * (tuplet + 1)   ||
					timeCount == 90 * (tuplet + 1)   || timeCount == 180 * (tuplet + 1)  ||
					timeCount == 360 * (tuplet + 1)  || timeCount == 720 * (tuplet + 1)) {
					drawTuplet(i - tupletCount + 1, tupletCount, tuplet);
					
					timeCount = 0.0;
					tupletCount = 0;
				}
			}
		}
	}
	
	// Draw the last tuplets
	//
	if (tuplet != 0) {
		if (tupletCount >= tuplet + 1) {
			drawTuplet(i - tupletCount, tupletCount, tuplet);
		} else {
			// Draw only the tuplet number
			//
			for (j = 0; j < tupletCount; j++) {
				unsigned int queueLength = nbStrings * 8 + 2;
				
				drawText(position.x() + (i - tupletCount + j) * 26, position.y() + queueLength + lineSpaces, temp.setNum(tuplet + 1));
			}
		}
	}
}

void TrackPrint::drawVerticalLine(const TabTimes& tempTimes)
{
	unsigned int queueLength = nbStrings * 8;
	int j;
	
	switch (tempTimes.duration()) {
	case HundredTwentyEighth:
	case SixtyFourth:
	case ThirthSecond:  // 1/32 Triple croche
	case Sixteenth:  // 1/16 Double croche
	case Eighth:  // 1/8 Croche
	case Quarter: // 1/4 - a long vertical line, so we need to find the highest note
		for (j = nbStrings - 1;((j >= 0) && (tempTimes.notes(j).isEmpty()) && (tempTimes.notes(j).isDeadNote() == false)); j--);
		
		// If it's an empty measure at all - draw the vertical line from bottom
		//
		if (j < 0)  j = 1;
		
		painter->drawLine(position.x(), position.y() + (j + 1) * 8, position.x(), position.y() + queueLength + 1);
		
		break;		// required to prevent print preview artefact
	case Half: // 1/2 Blanche
		painter->drawLine(position.x(), position.y() + queueLength - 5, position.x(), position.y() + queueLength);
	case Whole: // whole Ronde
	case NoneDuration:
		break;
	}
}

void TrackPrint::drawChanges(unsigned int x, unsigned int y, const TabBar& bar)
{
	if (bar.tempo() != -1) {
		drawTempo(bar.tempo());
		x += 60;
	}
	
	if (bar.balance() != -1 || bar.chorus() != -1 || bar.reverb() != -1 || bar.phaser() != -1 || bar.tremolo() != -1 || bar.instrument() != -1)
		drawText(x, y, "Mix changes");
	
}
// Draw duration
//
// TODO : count triolets !
//
void TrackPrint::drawDuration(const TabTimes &tempTimes, const TabTimes &nextTimes)
{
	bool croche = false, dblCroche = false, tplCroche = false;
	bool last = false;
	unsigned int queueLength = nbStrings * 8, smallest = 10000;
	int j;

	painter->setPen(pLnBl);

	switch (time[1]) {
	case 4:
		if (durationCount >= 120)
			durationCount = 0;
		break;
	case 8:
		break;
	}

	if (durationCount == 0) {
		crocheCount = dblCrocheCount = trlCrocheCount = 0;
	}
	
	switch (tempTimes.duration()) {
	case HundredTwentyEighth:
	case SixtyFourth:
		break;
	case ThirthSecond:  // 1/32 triple croche
		durationCount+=15;

		trlCrocheCount++;

		switch (time[1]) {
		case 4:
			if (durationCount + nextTimes.duration() >= 120) {
				if ((dblCrocheCount == 0) && (crocheCount == 0))
					last = false;
				else
					last = true;
			}
			break;
		case 8:
			break;
		}

		// Dessine les liens
		//
		if (dblCrocheCount) {
			painter->drawLine(position.x() - 25, position.y() + queueLength - 2, position.x(), position.y() + queueLength - 2);
			painter->drawLine(position.x() - 25, position.y() + queueLength, position.x(), position.y() + queueLength);
		} else if (crocheCount) {
			painter->drawLine(position.x() - 25, position.y() + queueLength, position.x(), position.y() + queueLength);
		}
		
		crocheCount = dblCrocheCount = 0;

		if (trlCrocheCount == 1)
			tplCroche = true;
		break;
	case Sixteenth:  // 1/16 double croche
		durationCount+=30;

		dblCrocheCount++;

		switch (time[1]) {
		case 4:
			if (durationCount + nextTimes.duration() >= 120) {
				if ((trlCrocheCount == 0) && (crocheCount == 0))
					last = false;
				else
					last = true;
			}
			break;
		case 8:
			break;
		}

		// Dessine les liens
		//
		if (trlCrocheCount) {
			painter->drawLine(position.x() - 25, position.y() + queueLength - 2, position.x(), position.y() + queueLength - 2);
			painter->drawLine(position.x() - 25, position.y() + queueLength, position.x(), position.y() + queueLength);
		} else if (crocheCount) {
			painter->drawLine(position.x() - 25, position.y() + queueLength, position.x(), position.y() + queueLength);
		}

		crocheCount = trlCrocheCount = 0;

		if (dblCrocheCount == 1) {
			if ((durationCount + nextTimes.duration() <= 120) && (nextTimes.duration() == 30)) {
				last = false;
			} else {
				dblCroche = true;
			}
		}

		if (dblCrocheCount == 1)
			dblCroche = true;
		break;
	case Eighth:  // 1/8 croche
		durationCount+=60;

		crocheCount++;

		switch (time[1]) {
		case 4:
			if (durationCount + nextTimes.duration() >= 120) {
				if ((trlCrocheCount == 0) && (dblCrocheCount == 0))
					last = false;
				else
					last = true;
			}
			break;
		case 8:
			break;
		}

		// Dessine les liens
		//
		if ((trlCrocheCount) || (dblCrocheCount)) {
			painter->drawLine(position.x() - 25, position.y() + queueLength, position.x(), position.y() + queueLength);
		}

		dblCrocheCount = trlCrocheCount = 0;

		if (crocheCount == 1)
			croche = true;
		break;
	case Quarter: // 1/4 noire
		durationCount+=120;

		trlCrocheCount = crocheCount = dblCrocheCount = 0;
		break;
	case Half: // 1/2
		durationCount+=240;

		trlCrocheCount = crocheCount = dblCrocheCount = 0;
		break;
	case Whole: // whole
		durationCount+=240;

		trlCrocheCount = crocheCount = dblCrocheCount = 0;
		break;
	case NoneDuration:
		break;
	}

	switch (tempTimes.duration()) {
	case HundredTwentyEighth:
		break;
	case SixtyFourth:
			painter->drawLine(position.x(), position.y() + queueLength - 6, position.x() + 5, position.y() + queueLength - 6);
			painter->drawLine(position.x(), position.y() + queueLength - 4, position.x() + 5, position.y() + queueLength - 4);
			painter->drawLine(position.x(), position.y() + queueLength - 2, position.x() + 5, position.y() + queueLength - 2);
			painter->drawLine(position.x(), position.y() + queueLength, position.x() + 5, position.y() + queueLength);
		break;
	case ThirthSecond:  // 1/32 Triple croche
		if ((last == false) && (tplCroche)) {
			painter->drawLine(position.x(), position.y() + queueLength - 4, position.x() + 5, position.y() + queueLength - 4);
			painter->drawLine(position.x(), position.y() + queueLength - 2, position.x() + 5, position.y() + queueLength - 2);
			painter->drawLine(position.x(), position.y() + queueLength, position.x() + 5, position.y() + queueLength);
		} if (last == true) {
			painter->drawLine(position.x(), position.y() + queueLength - 4, position.x() - 5, position.y() + queueLength - 4);
			painter->drawLine(position.x(), position.y() + queueLength - 2, position.x() - 5, position.y() + queueLength - 2);
			painter->drawLine(position.x(), position.y() + queueLength, position.x() - 5, position.y() + queueLength);
		}else {
			painter->drawLine(position.x() - (26 * (trlCrocheCount - 1)), position.y() + queueLength - 4, position.x(), position.y() + queueLength - 4);
			painter->drawLine(position.x() - (26 * (trlCrocheCount - 1)), position.y() + queueLength - 2, position.x(), position.y() + queueLength - 2);
			painter->drawLine(position.x() - (26 * (trlCrocheCount - 1)), position.y() + queueLength, position.x(), position.y() + queueLength);
		}
		
		break;
	case Sixteenth:  // 1/16 Double croche
		if ((last == false) && (dblCroche)) {
			painter->drawLine(position.x(), position.y() + queueLength - 2, position.x() + 5, position.y() + queueLength - 2);
			painter->drawLine(position.x(), position.y() + queueLength, position.x() + 5, position.y() + queueLength);
		} else if (last == true){
			painter->drawLine(position.x(), position.y() + queueLength - 2, position.x() - 5, position.y() + queueLength - 2);
			painter->drawLine(position.x(), position.y() + queueLength, position.x() - 5, position.y() + queueLength);
		} else {
			painter->drawLine(position.x() - (26 * (dblCrocheCount - 1)), position.y() + queueLength - 2, position.x(), position.y() + queueLength - 2);
			painter->drawLine(position.x() - (26 * (dblCrocheCount - 1)), position.y() + queueLength, position.x(), position.y() + queueLength);
		}
		
		break;
	case Eighth:  // 1/8 Croche
		if ((last == false) && (croche)) {
			painter->drawLine(position.x(), position.y() + queueLength, position.x() + 5, position.y() + queueLength);
		} else if (last) {
			painter->drawLine(position.x() - (26 * (crocheCount - 1)), position.y() + queueLength, position.x(), position.y() + queueLength);
		} else {
			painter->drawLine(position.x() - (26 * (crocheCount - 1)), position.y() + queueLength, position.x(), position.y() + queueLength);
		}
		
	case Quarter: // 1/4 - a long vertical line, so we need to find the highest note
		break;		// required to prevent print preview artefact
	case Half: // 1/2 Blanche
		painter->drawLine(position.x(), position.y() + queueLength - 5, position.x(), position.y() + queueLength);
	case Whole: // whole Ronde
		break;
	case NoneDuration:
		break; 
	}
}

// Draw the tuplet line and the tuplet number
//
void TrackPrint::drawTuplet(unsigned int barPos, unsigned int ntupletLength ,unsigned int ntuplet)
{
	unsigned int queueLength = (nbStrings * 8) + 2;
	QString temp;
	
	painter->drawLine(position.x() + (barPos * 26) - 5, position.y() + queueLength, position.x() + ((barPos + ntupletLength) * 26) - 13, position.y() + queueLength);
	
	drawText(position.x() + (barPos * 26) - 5 + (ntupletLength * 26 / 2) - 8, position.y() + queueLength + lineSpaces, temp.setNum(ntuplet + 1));
}

// Draw the note number
//
void TrackPrint::drawTimes(const TabTimes &tempTimes)
{
	unsigned int yPos = position.y();
	painter->setFont(*fTBar1);
	QFontMetrics fm = painter->fontMetrics();
	const int yOffs = fm.boundingRect("8").height() / 2;
	QString string;
	
	for (unsigned int j = 0; j < nbStrings; j++) {
		if (((!tempTimes.notes(j).isEmpty()) || (tempTimes.notes(j).isDeadNote() == true)) && (tempTimes.notes(j).isTieNote() == false)) {
			if (tempTimes.notes(j).isDeadNote() == true)
				string = "X";
			else if (tempTimes.notes(j).isGhostNote())
				string = '(' + string.setNum(tempTimes.notes(j).fret()) + ')';
			else
				string = string.setNum(tempTimes.notes(j).fret());
			
			const int xOffs = fm.boundingRect(string).width() / 2;
			
			painter->eraseRect(position.x() - xOffs + 2, yPos + (j * lineSpaces + 4) - (2 * yOffs), 4 * xOffs - 2, 2 * yOffs);
			
			painter->setPen(pLnBl);
			painter->drawText(position.x(), yPos + j * lineSpaces + 4, string);
		}
	}
}

// Draw the effects
//
void TrackPrint::drawEffect(const TabTimes &tempTimes, const TabTimes &nextTimes)
{
	if (tempTimes.isVibrato()) {
		int x = position.x() + 3;
// 				int y = position.y() + ((j - 1) * 7);
		
		drawVibrato(x, position.y() - 7);
	}
	
	if (tempTimes.isWideVibrato()) {
		int x = position.x() + 3;
// 				int y = position.y() + ((j - 1) * 7);
		
		drawWideVibrato(x, position.y() - 7);
	}
	
	if (tempTimes.tremolo()) {
		int x = position.x() + 3;
// 				int y = position.y() + ((j - 1) * 7);
		
		drawTremoloBar(x, position.y() - 7);
	}
	
	if (tempTimes.isNaturalHarmonic()) {
		// leftmost point of diamond
		//
		int x = position.x() - 3;
// 				int y = position.y() + 42;
		
		drawNaturalHarmonic(x, position.y() - 15);
	}
	
	if (tempTimes.isArtificialHarmonic()) {
		// leftmost point of diamond
		//
		int x = position.x() - 3;
// 				int y = position.y() + 42;
		
		drawArtificialHarmonic(x, position.y() - 15);
	}
	
	if (tempTimes.isTapping()) {
		int x = position.x() - 3;
		
		drawTapping(x, position.y() - 15);
	}
	
	if (tempTimes.isSlapping()) {
		int x = position.x() - 3;
		
		drawSlapping(x, position.y() - 15);
	}
	
	if (tempTimes.isPopping()) {
		int x = position.x() - 3;
		
		drawPopping(x, position.y() - 15);
	}
	
	if (tempTimes.isFadeIn()) {
		int x = position.x() - 3;
		
		drawFadeIn(x, position.y() - 15);
	}
	
	if (tempTimes.isStroke()) {
		int x = position.x() + 10;
		
		if (tempTimes.upStroke())
			drawUpStroke(x, position.y() - 15, tempTimes.isRasgueado());
		else
			drawDownStroke(x, position.y() - 15, tempTimes.isRasgueado());
	}
	
	switch (tempTimes.bendType()) {
		case TabBend::NoneBend:
			break;
		case TabBend::Bend:
		case TabBend::BendRelease:
		case TabBend::BendReleaseBend:
		case TabBend::PreBend:
		case TabBend::PreBendRelease:
			kdDebug() << "Warning you can not have a bend in a TabTime class !\n";
			break;
		case TabBend::Dip: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawDip(x, y, tempTimes.bendValueToString());
			break;
		}
		case TabBend::Dive: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawDive(x, y, tempTimes.bendValueToString());
			break;
		}
		case TabBend::ReleaseUp: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawReleaseUp(x, y, tempTimes.bendValueToString());
			break;
		}
		case TabBend::InvertedDip: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawInvertedDip(x, y, tempTimes.bendValueToString());
			break;
		}
		case TabBend::Return: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawReturn(x, y, tempTimes.bendValueToString());
			break;
		}
		case TabBend::ReleaseDown: {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawReleaseDown(x, y, tempTimes.bendValueToString());
			break;
		}
		default:
			kdDebug() << "bend : " << (int)tempTimes.bendType() << " there is a problem :p" << endl;
			break;
	}
	
	for (unsigned int j = 0; j < nbStrings; j++) {
		TabNote note = tempTimes.notes(j);
		
		// Check if there is a bend effect
		//
		switch (note.bendType()) {
			case TabBend::NoneBend:
				break;
			case TabBend::Bend: {
				int x = position.x() + 3;
				int y = position.y() + ((j - 1) * 7);
				
				drawBend(x, y, note.bendValueToString());
			} break;
			case TabBend::BendRelease: {
				int x = position.x() + 3;
				int y = position.y() + ((j - 1) * 7);
				
				drawBendAndRelease(x, y, note.bendValueToString());
			} break;
			case TabBend::BendReleaseBend: {
				int x = position.x() + 3;
				int y = position.y() + ((j - 1) * 7);
				
				drawBendAndRelease(x, y, "BendReleaseBend");
			}
			case TabBend::PreBend: {
				int x = position.x() + 3;
				int y = position.y() + ((j - 1) * 7);
				
				drawPreBend( x, y, note.bendValueToString());
			} break;
			case TabBend::PreBendRelease: {
				int x = position.x() + 3;
				int y = position.y() + ((j - 1) * 7);
				
				drawPreBendAndRelease( x, y, note.bendValueToString());
			} break;
			default:
				kdDebug() << "bend : " << (int)note.bendType() << " there is a problem :p" << endl;
				break;
		}
		
		switch (note.harmonic()) {
			case NoneHarmonic:
				break;
			case Natural: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawNaturalHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "N.H.");
			} break;
			case Tapped: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "T.H.");
			} break;
			case Pitch: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "P.H.");
			} break;
			case Semi: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "S.H.");
			} break;
			case Artificial5: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "A.H. 5");
			} break;
			case Artificial7: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "A.H. 7");
			} break;
			case Artificial12: {
				// leftmost point of diamond
				//
				int x = position.x() - 3;
				
				drawArtificialHarmonic(x, position.y() - 15);
				drawText(x + 3, position.y() - 20, "A.H. 12");
			} break;
			default:
				kdDebug() << "harmonic : " << (int)note.harmonic() << " there is a problem :p" << endl;
				break;
		}
		
		switch (note.slide()) {
		case NoneSlide:
			break;
		case ShiftSlide: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawSlide(x, y);
		} break;
		case LegatoSlide: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawLegatoSlide(x, y);
		} break;
		case OutDownwardSlide: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawOutDownwardSlide(x, y);
		} break;
		case OutUpwardSlide: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawOutUpwardSlide(x, y);
		} break;
		case SlideInBelow: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawSlideInBelow(x, y);
		} break;
		case SlideInAbove: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawSlideInAbove(x, y);
		} break;
		default:
			kdDebug() << "slide : " << (int)note.slide() << " there is a problem :p" << endl;
			break;
		}
		
		switch (note.tremoloPicking()) {
		case 0:
			break;
		case 1: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawTremoloPicking(x, y, 1);
		} break;
		case 2: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawTremoloPicking(x, y, 2);
		} break;
		case 3: {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawTremoloPicking(x, y, 3);
		} break;
		default:
			kdDebug() << "tremolo picking : " << (int)note.tremoloPicking() << " there is a problem :p" << endl;
			break;
		}
		
		if (note.isSlide() && note.slide() == NoneSlide) {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawSlide(x, y);
		}
		
		if (note.isHammerOnPullOff()) {
			// Hammer on, Pull off
			//
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawLegato(x, y, tempTimes, nextTimes, j);
		}
			
		if (note.isPalmMute()) {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawPalmMute(x, y);
		}
		
		if (note.isTieNote()) {
			// Hammer on, Pull off
			//
			int x = position.x() - 26 + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawTieNote(x, y);
		}
		
		if (note.isStaccato()) {
			int x = position.x() + 3;
			int y = position.y() + ((j - 1) * 7);
			
			drawStaccato(x, y);
		}
		
		if (note.isAccentuated()) {
			int x = position.x() + 3;
			int y = position.y() - 10;
			
			drawAccentuated(x, y);
		}
		
		if (note.graceNoteDuration()) {
			int x = position.x() - 6;
			int y = position.y() + ((j) * 7);
			
			drawGraceNote(x, y, note.graceNoteFret());
		}
		
		if (note.fretTrill()) {
			int x = position.x() - 6;
			int y = position.y() + ((j) * 7);
			
			drawTrillNote(x, y, note.fretTrill());
		}
	}
}

void TrackPrint::drawDip(unsigned int x, unsigned int y, const QString& s)
{
	painter->drawLine(x, y, x + 5, y + 5);
	painter->drawLine(x + 5, y + 5, x + 10, y);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawDive(unsigned int x, unsigned int y, const QString& s)
{
	painter->drawLine(x, y, x + 10, y + 5);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawReleaseUp(unsigned int x, unsigned int y, const QString& s)
{
	const QPen oldPen = painter->pen();
	
	QPen pen(Qt::DotLine);
	painter->setPen(pen);
	
	painter->drawLine(x, y, x + 10, y);
	painter->drawLine(x + 10, y, x + 10, y - 5);
	
	drawText(x + 3, position.y() - 12, s);
	
	painter->setPen(oldPen);
}

void TrackPrint::drawInvertedDip(unsigned int x, unsigned int y, const QString& s)
{
	painter->drawLine(x, y, x + 5, y - 5);
	painter->drawLine(x + 5, y - 5, x + 10, y);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawReturn(unsigned int x, unsigned int y, const QString& s)
{
	painter->drawLine(x, y, x + 10, y - 5);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawReleaseDown(unsigned int x, unsigned int y, const QString& s)
{
	const QPen oldPen = painter->pen();
	
	QPen pen(Qt::DotLine);
	painter->setPen(pen);
	
	painter->drawLine(x, y - 5, x + 10, y - 5);
	painter->drawLine(x + 10, y - 5, x + 10, y);
	
	drawText(x + 3, position.y() - 12, s);
	
	painter->setPen(oldPen);
}

void TrackPrint::drawPalmMute(unsigned int x, unsigned int y)
{
	drawText(x + 3, position.y() - 12, "P.M.");
}

void TrackPrint::drawStaccato(unsigned int x, unsigned int y)
{
	drawText(x + 3, position.y() - 12, ".");
}

void TrackPrint::drawTapping(unsigned int x, unsigned int y)
{
	drawText(x + 3, position.y() - 12, "T");
}

void TrackPrint::drawSlapping(unsigned int x, unsigned int y)
{
	drawText(x + 3, position.y() - 12, "S");
}

void TrackPrint::drawPopping(unsigned int x, unsigned int y)
{
	drawText(x + 3, position.y() - 12, "P");
}

void TrackPrint::drawFadeIn(unsigned int x, unsigned int y)
{
	painter->drawLine(x, y, x + 10, y - 5);
	painter->drawLine(x, y, x + 10, y + 5);
}

void TrackPrint::drawAccentuated(unsigned int x, unsigned int y)
{
	painter->drawLine(x, y - 5, x + 10, y);
	painter->drawLine(x, y + 5, x + 10, y);
}

void TrackPrint::drawGraceNote(unsigned int x, unsigned int y, unsigned int note)
{
	QString string;
	QFont smallFont(KGlobalSettings::generalFont());
	
	smallFont.setPointSizeFloat(smallFont.pointSizeFloat() * 0.75);
	
	if (note != 255)
		string = string.setNum(note);
	else
		string = "x";
	
	
	painter->setFont(smallFont);
	painter->drawText(x, y, string);
}

void TrackPrint::drawUpStroke(unsigned int x, unsigned int y, bool ras)
{
	painter->drawLine(x, y, x, y + 10);
	
	painter->drawLine(x, y, x - 5, y + 5);
	painter->drawLine(x, y, x + 5, y + 5);
	
	if (ras) {
		QFont smallFont(KGlobalSettings::generalFont());
		smallFont.setPointSizeFloat(smallFont.pointSizeFloat() * 0.75);
		
		painter->setFont(smallFont);
		painter->drawText(x + 15, y, "r");
	}
	
}

void TrackPrint::drawDownStroke(unsigned int x, unsigned int y, bool ras)
{
	painter->drawLine(x, y, x, y + 10);
	
	painter->drawLine(x, y + 10, x - 5, y + 5);
	painter->drawLine(x, y + 10, x + 5, y + 5);
	
	if (ras) {
		QFont smallFont(KGlobalSettings::generalFont());
		smallFont.setPointSizeFloat(smallFont.pointSizeFloat() * 0.75);
		
		painter->setFont(smallFont);
		painter->drawText(x + 15, y, "r");
	}
	
}

void TrackPrint::drawSlide(unsigned int x, unsigned int y)
{
	painter->drawLine(x + 5, y + 10, x + 15, y + 5);
}

void TrackPrint::drawLegatoSlide(unsigned int x, unsigned int y)
{
	painter->drawLine(x + 5, y + 10, x + 15, y + 5);
	
	painter->drawArc(x + 4, y - 5, 15, 10, 0, 180 * 16);
}

void TrackPrint::drawOutDownwardSlide(unsigned int x, unsigned int y)
{
	painter->drawLine(x + 5, y + 10, x + 15, y + 5);
}

void TrackPrint::drawOutUpwardSlide(unsigned int x, unsigned int y)
{
	painter->drawLine(x + 5, y + 5, x + 15, y + 10);
}

void TrackPrint::drawSlideInBelow(unsigned int x, unsigned int y)
{
	painter->drawLine(x - 5, y + 5, x - 15, y + 10);
}

void TrackPrint::drawSlideInAbove(unsigned int x, unsigned int y)
{
	painter->drawLine(x - 5, y + 10, x - 15, y + 5);
}

void TrackPrint::drawTrillNote(unsigned int x, unsigned int y, unsigned int note)
{
	QString string;
	QFont smallFont(KGlobalSettings::generalFont());
	
	string = string.setNum(note);
	
	drawText(x + 3, position.y() - 12, "TR");
	
	smallFont.setPointSizeFloat(smallFont.pointSizeFloat() * 0.75);
	
	painter->setFont(smallFont);
	painter->drawText(x + 15, y, '(' + string + ')');
}

void TrackPrint::drawTremoloPicking(unsigned int x, unsigned int y, unsigned int nbTremolo)
{
	y+=nbStrings * 8;
	
	for (uchar i = 0; i < nbTremolo; i++) {
		painter->drawLine(x - 5, y + 10, x - 15, y + 5);
		y+=4;
	}
}

// Draw the end bar
//
void TrackPrint::drawEndBar()
{
	// draw vertical line
	painter->drawLine(position.x(), position.y(), position.x(), position.y() + 38);
}

void TrackPrint::drawDotted(const TabTimes& tempTimes)
{
	unsigned int queueLength = nbStrings * 8;
	
	if (tempTimes.isDotted()) {
		QPen oldPen = painter->pen();
		QPen pen(Qt::black, 2);
		
		painter->setPen(pen);
		
		if (tempTimes.isRest() == false)
			painter->drawPoint(position.x() + 2, position.y() + queueLength - 6);
		else
			painter->drawPoint(position.x() + 10, position.y() + (nbStrings * 4) - 2);
		
		painter->setPen(oldPen);
	}
}

// draw rest of type t centered at x on staff line y
// note: lowest = 0, highest = 8
// uses position.y()st but ignores xpos
//
void TrackPrint::drawRest(int x, int y, const TabTimes &tempTimes) throw (const char*)
{
	switch (tempTimes.duration()) {
	case NoneDuration:
		break;
	case HundredTwentyEighth:
		painter->drawPixmap(x - 10, y, *silent[7]);
		break;
	case SixtyFourth:
		painter->drawPixmap(x - 10, y, *silent[6]);
		break;
	case ThirthSecond:  // 1/32
		painter->drawPixmap(x - 10, y, *silent[5]);
		break;
	case Sixteenth:  // 1/16
		painter->drawPixmap(x - 10, y, *silent[4]);
		break;
	case Eighth:  // 1/8
		painter->drawPixmap(x - 10, y, *silent[3]);
		break;
	case Quarter: // 1/4
		painter->drawPixmap(x - 10, y, *silent[2]);
		break;
	case Half: // 1/2
		painter->drawPixmap(x - 10, y, *silent[1]);
		break;
	case Whole: // whole
		painter->drawPixmap(x - 10, y, *silent[0]);
		break;
	default:
		kdDebug() << "TrackPrint::drawRest : error unknow duration " << tempTimes.duration() << "\n";
		throw "TrackPrint::drawRest unknow duration\n";
		return;
	}
}

// draw rest of type t centered at x on staff line y
// note: lowest = 0, highest = 8
// uses position.y()st but ignores xpos
//
void TrackPrint::drawRest(int x, int y, const TabTimes& tempTimes, const TabTimes& previousTimes) throw (const char*)
{
	bool smaller = false;
	unsigned int queueHeight = nbStrings * 8;
	unsigned int queueLength = position.x() - 5;
	
	if (tempTimes.duration() == previousTimes.duration()) {
		queueLength = position.x() - 20;
	} 
	
	switch (tempTimes.duration()) {
	case NoneDuration:
		break;
	case HundredTwentyEighth:
		break;
	case SixtyFourth:
		break;
	case ThirthSecond:  // 1/32
		painter->drawLine(position.x(), position.y() + queueHeight - 4, queueLength, position.y() + queueHeight - 4);
		painter->drawLine(position.x(), position.y() + queueHeight - 2, queueLength, position.y() + queueHeight - 2);
		painter->drawLine(position.x(), position.y() + queueHeight, queueLength, position.y() + queueHeight);
		break;
	case Sixteenth:  // 1/16
		painter->drawLine(position.x(), position.y() + queueHeight - 2, queueLength, position.y() + queueHeight - 2);
		painter->drawLine(position.x(), position.y() + queueHeight, queueLength, position.y() + queueHeight);
		break;
	case Eighth:  // 1/8 croche
		painter->drawLine(position.x(), position.y() + queueHeight, queueLength, position.y() + queueHeight);
		break;
	case Quarter: // 1/4 noire
		// Draw nothing
		//
		break;
	case Half: // 1/2 blanche
		// Draw nothing
		//
		break;
	case Whole: // whole
		// Draw nothing
		//
		break;
	default:
		kdDebug() << "TrackPrint::drawRest : error unknow duration " << tempTimes.duration() << "\n";
		throw "TrackPrint::drawRest unknow duration\n";
		return;
	}
	
	if ((tempTimes.duration() < 120) && (previousTimes.duration() < 120))
		smaller = (tempTimes.duration() < previousTimes.duration());
	
	if (smaller) {
		queueLength = position.x() - 20;
		
		switch(previousTimes.duration()) {
		case NoneDuration:
			break;
		case HundredTwentyEighth:
			break;
		case SixtyFourth:
			break;
		case ThirthSecond:  // 1/32
			// Draw nothing
			//
			break;
		case Sixteenth:  // 1/16
			painter->drawLine(position.x(), position.y() + queueHeight - 2, queueLength, position.y() + queueHeight - 2);
			painter->drawLine(position.x(), position.y() + queueHeight, queueLength, position.y() + queueHeight);
			break;
		case Eighth:  // 1/8 croche
			painter->drawLine(position.x(), position.y() + queueHeight, queueLength, position.y() + queueHeight);
			break;
		case Quarter: // 1/4 noire
			// Draw nothing
			//
			break;
		case Half: // 1/2 blanche
			// Draw nothing
			//
			break;
		case Whole: // whole
			// Draw nothing
			//
			break;
		default:
			kdDebug() << "TrackPrint::drawRest : error unknow duration " << previousTimes.duration() << "\n";
			throw "TrackPrint::drawRest unknow duration\n";
			return;
		}
	}
	
	drawRest(x, y, tempTimes);
}

void TrackPrint::drawBend(unsigned int x, unsigned  int y, const QString &s)
{
	painter->drawPixmap(x + 3, y - 4, *bend);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawBendAndRelease(unsigned int x, unsigned  int y, const QString &s)
{
	painter->drawPixmap(x + 3, y - 4, *bendRelease);
	
	drawText(x + 3, position.y() - 12, s);
}

void TrackPrint::drawPreBend(unsigned int x, unsigned  int y, const QString &s)
{
	painter->drawLine(x + 7, y, x + 7, y + 8);

	drawText(x + 3, position.y() - 10, s);
}

void TrackPrint::drawPreBendAndRelease(unsigned int x, unsigned  int y, const QString &s)
{
	painter->drawPixmap(x + 3, y - 4, *preBendRelease);
	
	drawText(x + 3, position.y() - 10, s);
}

void TrackPrint::drawUnisonBend(unsigned int x, unsigned int y)
{
	drawBend(x, y, "Full");
}

void TrackPrint::drawVibrato(unsigned int x, unsigned int y)
{
	QPointArray points(6);

	for (unsigned int i = 0; i < 6; i++) {
		x+=3;
		
		if (i % 2) y-=3;
		else y+=3;

		points.setPoint(i, x, y);
	}

	painter->drawPolyline(points);
}

void TrackPrint::drawWideVibrato(unsigned int x, unsigned int y)
{
	QPointArray points(6);
	QPen oldPen = painter->pen();
	QPen pen(Qt::black, 2);
	
	for (unsigned int i = 0; i < 6; i++) {
		x+=3;
		
		if (i % 2) y-=3;
		else y+=3;

		points.setPoint(i, x, y);
	}
	
	painter->setPen(pen);
	painter->drawPolyline(points);
	painter->setPen(oldPen);
}

void TrackPrint::drawLegato(unsigned int x, unsigned int y, const TabTimes &currentTimes, const TabTimes &nextTimes, unsigned int i)
{
	painter->drawArc(x + 4, y, 15, 10, 0, 180 * 16);
	QString string = "";

	if (currentTimes.notes(i).fret() < nextTimes.notes(i).fret()) {
		string = "HO";
	} else {
	 	string = "PO";
	}

	painter->setFont(*fTBar2);
	painter->drawText(x, y, string);
	painter->setFont(*fTBar1);
}

void TrackPrint::drawTieNote(unsigned int x, unsigned int y)
{
	painter->drawArc(x + 4, y, 15, 10, 0, 180 * 16);
}

void TrackPrint::drawTrill(unsigned int x, unsigned int y, unsigned int trillNote)
{
	QString temp;
	drawVibrato(x, y);
	
	drawText(x, y, '(' + temp.setNum(trillNote) + ')');
}

void TrackPrint::drawNaturalHarmonic(unsigned int x, unsigned int y)
{
	QPointArray a(4);
	
	// initialize diamond shape
	//
	a.setPoint(0, x, y);
	a.setPoint(1, x + 5, y + 5);
	a.setPoint(2, x + 2 * 5, y);
	a.setPoint(3, x + 5, y - 5);
	
	// erase tab line
	//
	painter->setPen(pLnWh);
	painter->drawLine(x, y, x + 2 * 5, y);
	painter->setPen(pLnBl);
	
	// draw (empty) diamond
	//
	painter->drawPolygon(a);
}

void TrackPrint::drawArtificialHarmonic(unsigned int x, unsigned int y)
{
	QPointArray a(4);
	
	// initialize diamond shape
	//
	a.setPoint(0, x, y);
	a.setPoint(1, x + 5, y + 5);
	a.setPoint(2, x + 2 * 5, y);
	a.setPoint(3, x + 5, y - 5);
	
	// draw filled diamond
	//
	QBrush blbr(Qt::black);
	painter->setBrush(blbr);
	painter->drawPolygon(a);
	painter->setBrush(Qt::NoBrush);
}

void TrackPrint::drawTremoloBar(unsigned int x, unsigned int y)
{
}

void TrackPrint::drawMuffledStrings(unsigned int x, unsigned int y)
{
}

void TrackPrint::drawPickSlide(unsigned int x, unsigned int y)
{
}

void TrackPrint::drawTremoloPicking(unsigned int x, unsigned int y)
{
}

void TrackPrint::drawLetRing(unsigned int, unsigned int)
{
}

void TrackPrint::drawStopRing(unsigned int, unsigned int)
{
}


// draw clef at position.x(),position.y()st
// draw key at position.x(),position.y() for all strings of track trk
// at the first line (l == 0), string names are printed
// at all other lines the text "TAB"
// note: print drum names instead in case of drumtrack
//
int TrackPrint::drawKey(const TabTrack& tabTrack)
{
	const int lstStr = tabTrack.nbStrings() - 1;
	int res = 0;
	
	position.setY(45);
	painter->setFont(*fTBar1);
	
	for (int i = 0; i < lstStr + 1; i++) {
		if (tabTrack.mode() == DrumTab)
				painter->drawText(position.x() + 5, position.y() + (8 * (lstStr - i)), drum_abbr[tabTrack.tune(i)]);
		else
				painter->drawText(position.x() + 5, position.y() + (8 * (lstStr - i)), Settings::noteName(tabTrack.tune(i) % 12));
	}
	
	if (tabTrack.mode() == DrumTab)
		position.setX(position.x() + 25);
	else
		position.setX(position.x() + 15);
	
	position.setY(40);
	
	return res;
}

// draw key signature at position.x(),position.y()st
//
int TrackPrint::drawKeySig(TabTrack &tabTrack, bool doDraw)
{
	// Key signature accidental placement table
	// if keySig > 0, start at F and work to the right, notes are sharpened
	// if keySig < 0, start at B and work to the left, notes are flattened
	//                               F   C   G   D   A   E   B
// 	const int accPosSharpTab[7] = { 3,  0,  4,  1, -2,  2, -1};
// 	const int accPosFlatTab[7]  = {-4,  0, -3,  1, -2,  2, -1};
	int res = 0;
// 	QString s;

/*	if (stNts) {
		if (doDraw) {
			painter->setFont(*fFeta);
		}

		int position.y();
		int sig = 0;

		if ((sig <= -8) || (8 <= sig)) {
			sig = 0;
		}

		if (sig != 0) {
			if (doDraw) {
				position.x() += wNote;
			}
			res += wNote;
		}

		if (sig > 0) {
			s = QChar(0x201c);
			for (int i = 0; i < sig; i++) {
				position.y() = accPosSharpTab[i];
				if (doDraw) {
					painter->drawText(position.x(), position.y()st - (position.y() + 5) * ystepst / 2, s);
					position.x() += (int) (0.8 * wNote);
				}
				res += (int) (0.8 * wNote);
			}
		} else if (sig < 0) {
			s = QChar(0x201e);
			for (int i = 0; i > sig; i--) {
				position.y() = accPosFlatTab[i + 6];
				if (doDraw) {
					painter->drawText(position.x(), position.y()st - (position.y() + 5) * ystepst / 2, s);
					position.x() += (int) (0.7 * wNote);
				}
				res += (int) (0.7 * wNote);
			}
		}
	}
*/
	return res;
}

// print timesig if necessary
//
int TrackPrint::drawTimeSig(int bn, const TabTrack& tabTrack)
{
	int res = 0;
	int brth;
	QFontMetrics fm = painter->fontMetrics();
	QString time;
	int y;
	
	// tab bar
	//
	painter->setFont(*fTSig);
	fm = painter->fontMetrics();
	
	// calculate vertical position:
	// exactly halfway between top and bottom string
	//
	y = position.y() + 10 * (tabTrack.nbStrings() - 1) / 2;
	
	// center the timesig at this height
	// use spacing of 0.2 * char height
	//
	time.setNum(tabTrack.bar(bn).timeSignature(0));
	
	brth = fm.boundingRect(time).height();
	y -= (int) (0.1 * brth);
	painter->drawText(position.x(), y, time);
	
	time.setNum(tabTrack.bar(bn).timeSignature(1));
	
	y += (int) (1.2 * brth);
	painter->drawText(position.x(), y, time);
	painter->setFont(*fTBar1);
	
	position.setX(position.x() + 10);
	
	return res;
}
