void ofxTimeMeasurements::draw(int x, int y) {

	if (!enabled){
		//drawString(ofToString(fr, msPrecision), 10, fontSize);
		return;
	}
	
	float fr = ofGetFrameRate();
	uint64_t timeNow;
	if(internalBenchmark){
		timeNow = TM_GET_MICROS();
	}
	currentFrameNum = ofGetFrameNum();

	drawLines.clear();
	float percentTotal = 0.0f;
	float timePerFrame = 1000.0f / desiredFrameRate;

	mutex.lock();

	vector<TimeMeasurement*> toResetUpdatedLastFrameFlag;

	//update time stuff, build draw lists
	for( unordered_map<string,TimeMeasurement*>::iterator ii = times.begin(); ii != times.end(); ++ii ){
		TimeMeasurement* t = ii->second;
		string key = ii->first;
		if(!t->measuring){
			if (t->life > 0.01){
				t->life *= idleTimeColorDecay; //decrease life
			}else{ //life decays very slow when very low
				t->life *= deadThreadExtendedLifeDecSpeed; //decrease life very slowly
			}
		}
//		if (!t->updatedLastFrame && averaging){ // if we didnt update that time, make it tend to zero slowly
//			t->avgDuration = (1.0f - timeAveragePercent) * t->avgDuration;
//		}
		toResetUpdatedLastFrameFlag.push_back(t);
	}

	unordered_map<ThreadId, ThreadInfo>::iterator ii;
	vector<ThreadId> expiredThreads;

	//lets make sure the Main Thread is always on top
	vector< ThreadContainer > sortedThreadList;

	for( ii = threadInfo.begin(); ii != threadInfo.end(); ++ii ){ //walk all thread trees
		ThreadContainer cont;
		cont.id = ii->first;
		cont.info = &ii->second;
		if (isMainThread(ii->first)){ //is main thread
			sortedThreadList.insert(sortedThreadList.begin(), cont);
		}else{
			sortedThreadList.push_back(cont);
		}
	}
	std::sort(sortedThreadList.begin(), sortedThreadList.end(), compareThreadPairs);

	#if defined(USE_OFX_HISTORYPLOT)
	vector<ofxHistoryPlot*> plotsToDraw;
	#endif

	for( int k = 0; k < sortedThreadList.size(); k++ ){ //walk all thread trees

		ThreadId thread = sortedThreadList[k].id;
		core::tree<string> &tr = sortedThreadList[k].info->tree;

		ThreadInfo & tinfo = threadInfo[thread];
		PrintedLine header;
		header.formattedKey = "+ " + *tr;
		header.color = tinfo.color;
		header.lineBgColor = ofColor(header.color, dimColorA * 2); //header twice as alpha
		header.key = *tr; //key for selection, is thread name
		drawLines.push_back(header); //add header to drawLines

		int numAlive = 0;
		int numAdded = 0;

		core::tree<string>::iterator wholeTreeWalker = tr.in();
		bool finishedWalking = false;
		float winW = ofGetWidth();

		while( !finishedWalking ){

			string key = *wholeTreeWalker;
			TimeMeasurement * t = times[*wholeTreeWalker];
			if(t->thread == thread){

				#if defined(USE_OFX_HISTORYPLOT)
				bool plotActive = false;
				ofxHistoryPlot* plot = plots[key];
				if(plot){
					if(t->settings.plotting){
						if(t->updatedLastFrame){
							//update plot res every now and then
							if(currentFrameNum%120 == 1) plot->setMaxHistory(MIN(maxPlotSamples, winW * plotResolution));
							if (!freeze) {
								if (t->accumulating) {
									plot->update(t->microsecondsAccum / 1000.0f);
								}
								else {
									plot->update(t->avgDuration / 1000.0f);
								}
							}
						}
						plotsToDraw.push_back(plot);
						plotActive = true;
					}
				}
				#endif

				bool visible = t->settings.visible;
				bool alive = t->life > 0.0001;
				if(alive){
					numAlive++;
				}

				if (visible && (removeExpiredTimings ? alive : true)){
					PrintedLine l;
					l.key = key;
					l.tm = t;
					l.lineBgColor = ofColor(tinfo.color, dimColorA);

					int depth = wholeTreeWalker.level();
					for(int i = 0; i < depth; ++i) l.formattedKey += " ";

					if (wholeTreeWalker.size() == 0){
						l.formattedKey += "-";
					}else{
						l.formattedKey += "+";
					}
					l.formattedKey += key + string(t->accumulating ? "[" + ofToString(t->numAccumulations)+ "]" : "" );
					l.isAccum = t->accumulating;
					l.time = getTimeStringForTM(t);
					if(drawPercentageAsGraph){
						l.percentGraph = getPctForTM(t);
					}

					l.color = tinfo.color * ((1.0 - idleTimeColorFadePercent) + idleTimeColorFadePercent * t->life);
					if (!t->settings.enabled){
						if(t->ifClause){
							l.color = disabledTextColor;
						}else{
							l.color = disabledTextColor.getInverted();
						}
					}

					#if defined(USE_OFX_HISTORYPLOT)
					if(plotActive){
						l.plotColor = ofColor(plots[key]->getColor(), 200);
					}
					#endif

					if (menuActive && t->key == selection){
						if(currentFrameNum%5 < 4){
							l.color.invert();
							l.lineBgColor = ofColor(tinfo.color, dimColorA * 1.5);
						}
					}

					drawLines.push_back(l);
					numAdded++;
				}

				//only update() and draw() count to the final %
				if(key == TIME_MEASUREMENTS_DRAW_KEY || key == TIME_MEASUREMENTS_UPDATE_KEY){
					percentTotal += (t->avgDuration * 0.1f) / timePerFrame;
				}
				//reset accumulator
				t->accumulating = false;
				t->numAccumulations = 0;
				t->microsecondsAccum = 0;
			}

			//control the iterator to walk the tree "recursively" without doing so.
			if(wholeTreeWalker.size()){
				wholeTreeWalker = wholeTreeWalker.in();
			}else{
				if ( wholeTreeWalker.next() == wholeTreeWalker.end() ){
					wholeTreeWalker = wholeTreeWalker.out();
					while( wholeTreeWalker.next() == wholeTreeWalker.end() && wholeTreeWalker != tr){
						wholeTreeWalker = wholeTreeWalker.out();
					}
					if(wholeTreeWalker == tr){
						finishedWalking = true;
					}else{
						wholeTreeWalker++;
					}
				}else{
					++wholeTreeWalker;
				}
			}
		}

		#if defined(USE_OFX_HISTORYPLOT)
		numActivePlots = plotsToDraw.size();
		#endif

		if (numAlive == 0 && removeExpiredThreads){
			//drop that whole section if all entries in it are not alive
			for(int i = 0; i < numAdded + 1; i++){
				if(drawLines.size() > 0){
					int delID = drawLines.size() - 1;
					//clear selection if needed
					if (selection == drawLines[delID].key){
						selection = TIME_MEASUREMENTS_UPDATE_KEY;
					}
					drawLines.erase(drawLines.begin() + delID);
				}
			}
			expiredThreads.push_back(thread);
		}
	}

	//delete expired threads
	for(int i = 0; i < expiredThreads.size(); i++){
		unordered_map<ThreadId, ThreadInfo>::iterator treeIt = threadInfo.find(expiredThreads[i]);
		if (treeIt != threadInfo.end()) threadInfo.erase(treeIt);
	}

	mutex.unlock();

	updateLongestLabel();

	//find headers
	int tempMaxW = -1;
	vector<int> headerLocations;
	for( int i = 0; i < drawLines.size(); i++ ){
		if (drawLines[i].tm){ //its a measurement
			//add padding to draw in columns
			for(int j = drawLines[i].formattedKey.length(); j < longestLabel; j++){
				drawLines[i].formattedKey += " ";
			}
			if (!drawLines[i].tm->error){
				drawLines[i].shouldDrawPctGraph = true;
				drawLines[i].fullLine = drawLines[i].formattedKey + " " + drawLines[i].time;
			}else{
				drawLines[i].shouldDrawPctGraph = true;
				drawLines[i].fullLine = drawLines[i].formattedKey + "    Error!" ;
			}
			int len = drawLines[i].fullLine.length();
			if(len > tempMaxW) tempMaxW = len;
			if(drawLines[i].tm->measuring) drawLines[i].shouldDrawPctGraph = false;
		}else{ //its a header
			drawLines[i].fullLine = drawLines[i].formattedKey;
			drawLines[i].shouldDrawPctGraph = false;
			headerLocations.push_back(i);
		}
	}

	int numInstructionLines = 0;
	if(menuActive){ //add instructions line if menu active
		PrintedLine l;
		//title line
		l.color = hilightColor;
		l.lineBgColor = ofColor(hilightColor, dimColorA * 2);
		l.fullLine = " KEYBOARD COMMANDS "; //len = 23
		int numPad = 2 + ceil((getWidth() - charW * (23)) / charW);
		for(int i = 0; i < floor(numPad/2.0); i++ ) l.fullLine = "#" + l.fullLine;
		for(int i = 0; i < ceil(numPad/2.0); i++ ) l.fullLine += "#";
		l.fullLine = " " + l.fullLine;
		drawLines.push_back(l); numInstructionLines++;
		//key command lines
		l.lineBgColor = ofColor(hilightColor, dimColorA);
		l.fullLine = " 'UP/DOWN' select measur."; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'LFT/RGHT' expand/collaps"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'RET' toggle code section"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'A' average measurements"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'F' freeze measurements"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'L' cycle widget location"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'PG_DWN' en/disable addon"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'B' internal benchmark"; drawLines.push_back(l); numInstructionLines++;
		l.fullLine = " 'V' expand all"; drawLines.push_back(l); numInstructionLines++;
		#if defined USE_OFX_HISTORYPLOT
		l.fullLine = " 'P' plot selectd measur."; drawLines.push_back(l); numInstructionLines++;
		#endif
	}

	maxW = tempMaxW;

	ofSetupScreen(); //mmmm----
	ofPushStyle();
	ofSetRectMode(OF_RECTMODE_CORNER);
	ofSetDrawBitmapMode(OF_BITMAPMODE_SIMPLE);
	ofEnableAlphaBlending();

	ofPushMatrix();
	ofScale(uiScale,uiScale);

	ofFill();

	//draw all plots
	#if defined(USE_OFX_HISTORYPLOT)
	//int numCols = plotsToDraw.size()

	float highest = FLT_MIN;
	for(auto plot : plotsToDraw){
		if(allPlotsTogether){ //lets find the range that covers all the plots
			float high = plot->getHigerRange();
			if (high > highest) highest = high;
			plot->setDrawTitle(false);
			plot->setDrawBackground(false);
			plot->setShowSmoothedCurve(false);
		}else{
			plot->setDrawTitle(true);
			plot->setDrawBackground(true);
			plot->setLowerRange(0);
			plot->setShowSmoothedCurve(true);
		}
	}

	float canvasW = ofGetWidth();
	float canvasH = ofGetHeight();

	if(allPlotsTogether && plotsToDraw.size()){
		ofSetColor(0, 230);
		ofDrawRectangle(0, canvasH - plotHeight, canvasW / uiScale, plotHeight);
	}

	for(int i = 0; i < plotsToDraw.size(); i++){
		int y = (plotBaseY == 0 ? canvasH : plotBaseY) / uiScale - plotHeight * (i + 1);
		if(allPlotsTogether){
			plotsToDraw[i]->setRange(0, highest);
			y = ((plotBaseY == 0 ? canvasH : plotBaseY) - plotHeight) / uiScale;
		}
		plotsToDraw[i]->draw(0, y, canvasW / uiScale, plotHeight);
		if(!allPlotsTogether){
			ofSetColor(99);
			if(i != plotsToDraw.size() -1){
				ofLine(0, y, canvasW / uiScale, y );
			}
		}

		if(allPlotsTogether){
			ofSetColor(plotsToDraw[i]->getColor());
			deque<float>& vals = plotsToDraw[i]->getValues();
			float val = 0.0f;
			if(!vals.empty()) val = vals.back();
			string msg = plotsToDraw[i]->getVariableName() + " " + ofToString(val, 2);
			drawString(msg, canvasW - charW * msg.size() - 2, ofGetHeight() - plotHeight - 4 - charH * (plotsToDraw.size() -1 - i));
		}
	}
	#endif

	float totalW = getWidth();
	float totalH = getHeight();

	//draw bg rect
	ofSetColor(bgColor);
	ofDrawRectangle(x, y + 1, totalW, totalH);

	//draw all lines
	for(int i = 0; i < drawLines.size(); i++){
		ofSetColor(drawLines[i].lineBgColor);
		ofRectangle lineRect = ofRectangle(x, y + 2 + i * charH, totalW, charH + (drawLines[i].tm ? 0 : 1));
		ofDrawRectangle(lineRect);
		if(drawLines[i].isAccum && drawLines[i].tm != NULL){
			ofSetColor(drawLines[i].color, 128);
			ofDrawRectangle(x + totalW,
							y + 3 + i * charH,
							-5,
							charH - 1 );
		}
		ofSetColor(drawLines[i].color);
		drawString(drawLines[i].fullLine, x , y + (i + 1) * charH);
		if(drawLines[i].plotColor.a > 0){ //plot highlight on the sides
			ofSetColor(drawLines[i].plotColor);
			float y1 = y + 2.4f + i * charH;
			ofDrawTriangle(	x, y1, 
							x, y1 + charH, 
							x + charW * 0.7f, y1 + charH * 0.5f);
		}

		if(drawPercentageAsGraph && drawLines[i].shouldDrawPctGraph && drawLines[i].percentGraph > 0.02f){
			float ww = charW * 5.5;
			float xx = lineRect.x + lineRect.width - charW * 7;
			float pct = MIN(drawLines[i].percentGraph, 1.0);
			unsigned char a = 64;
			ofColor gC;
			if(drawLines[i].percentGraph > 1.0){
				gC = ofColor(255,0,0, (currentFrameNum%4 > 2) ? 1.5 * a : a);
			}else{
				gC = ofColor(drawLines[i].lineBgColor, a) * (1.0f - pct) + ofColor(255,0,0,a) * pct;
			}

			ofSetColor(gC);
			ofDrawRectangle( xx,
							lineRect.y + 0.2 * lineRect.height ,
							ww * pct,
							lineRect.height * 0.65
							);
		}
	}

	if(internalBenchmark){
		float offset = 0;
		if(drawLocation == TIME_MEASUREMENTS_TOP_LEFT ||
		   drawLocation == TIME_MEASUREMENTS_TOP_RIGHT ||
		   drawLocation == TIME_MEASUREMENTS_CUSTOM_LOCATION ){
			offset = (drawLines.size() + 2.5) * charH;
		}
		ofSetColor(0);
		ofDrawRectangle(x, offset + y - charH, totalW, charH);
		ofSetColor(currentFrameNum%3 ? 255 : 64);
		drawString(" Meas: " + ofToString(wastedTimeAvg / 1000.f, 2) + "ms " +
				   " Draw: " + ofToString(wastedTimeDrawingAvg / 1000.f, 2) + "ms ",
				   x, offset + y - charH * 0.12);
	}

	if (freeze) {
		if(currentFrameNum%5 < 4) ofSetColor(frozenColor);
		else ofSetColor(ofColor::white);
		drawString("Frozen! 'F'", x + totalW - 13 * charW, y + charH );
	}

	{//lines
		ofSetColor(hilightColor);
		ofMesh lines;
		ofSetLineWidth(0.1);
		lines.setMode(OF_PRIMITIVE_LINES);
		float fuzzyFix = 0.5;
		float yy = y+1 + fuzzyFix;
		lines.addVertex(ofVec2f(x, yy));
		lines.addVertex(ofVec2f(x + totalW, yy));
		yy = y + totalH - charH - 3 + fuzzyFix;
		lines.addVertex(ofVec2f(x, yy));
		lines.addVertex(ofVec2f(x + totalW, yy));
		yy = y + totalH + fuzzyFix;
		lines.addVertex(ofVec2f(x, yy));
		lines.addVertex(ofVec2f(x + totalW, yy));
		if(menuActive){
			yy = y + totalH + fuzzyFix - (numInstructionLines + 1) * charH - 3;
			lines.addVertex(ofVec2f(x, yy));
			lines.addVertex(ofVec2f(x + totalW, yy));
			yy = y + totalH + fuzzyFix - (numInstructionLines) * charH - 3;
			lines.addVertex(ofVec2f(x, yy));
			lines.addVertex(ofVec2f(x + totalW, yy));
		}
		lines.draw();
	}//lines

	//print bottom line, fps and stuff
	bool missingFrames = ( fr < desiredFrameRate - 1.0 ); // tolerance of 1 fps TODO!
	static char msg[128];

	sprintf(msg, "%2.1f fps % 5.1f%%", fr, percentTotal );
	if(missingFrames){
		ofSetColor(170,33,33); //reddish fps below desired fps
	}else{
		ofSetColor(hilightColor);
	}
	int len = strlen(msg);
	string pad = " ";
	int diff = (maxW - len) - 1;
	for(int i = 0; i < diff; i++) pad += " ";
	int lastLine = ( drawLines.size() + 1 ) * charH + 2;
	drawString( pad + msg, x, y + lastLine );
	
	//show activate menu key
	if(menuActive ) ofSetColor(hilightColor.getInverted());
	else ofSetColor(hilightColor);
	drawString(" '" + ofToString(char(activateKey)) + "'", x, y + lastLine);

	//show averaging warning
	if (averaging) {
		if (currentFrameNum % 5 < 2) ofSetColor(hilightColor);
		else ofSetColor(ofColor::limeGreen);
		drawString(" avg!", x + charW * 3.5, y + lastLine);
	}

	for(int i = 0; i < toResetUpdatedLastFrameFlag.size(); i++){
		toResetUpdatedLastFrameFlag[i]->updatedLastFrame = false;
	}
	ofPopMatrix();
	ofPopStyle();

	if(internalBenchmark){
		wastedTimeDrawingThisFrame += TM_GET_MICROS() - timeNow;
		wastedTimeAvg = wastedTimeThisFrame * 0.025f + 0.975f * wastedTimeAvg;
		wastedTimeDrawingAvg = wastedTimeDrawingThisFrame * 0.025f + 0.975f * wastedTimeDrawingAvg;
		wastedTimeThisFrame = wastedTimeDrawingThisFrame = 0;
	}
}
void ofxTimeMeasurements::draw(float x, float y) {

	if (!enabled) return;
	
	drawLines.clear();
	float percentTotal = 0.0f;
	float timePerFrame = 1000.0f / desiredFrameRate;

	mutex.lock();

	vector<TimeMeasurement*> toResetUpdatedLastFrameFlag;

	//update time stuff, build draw lists
	for( unordered_map<string,TimeMeasurement*>::iterator ii = times.begin(); ii != times.end(); ++ii ){
		TimeMeasurement* t = ii->second;
		string key = ii->first;
		if(!t->measuring){
			if (t->life > 0.01){
				t->life *= idleTimeColorDecay; //decrease life
			}else{ //life decays very slow when very low
				t->life *= deadThreadExtendedLifeDecSpeed; //decrease life very slowly
			}
		}
		if (!t->updatedLastFrame && timeAveragePercent < 1.0f){ // if we didnt update that time, make it tend to zero slowly
			t->avgDuration = (1.0f - timeAveragePercent) * t->avgDuration;
		}
		toResetUpdatedLastFrameFlag.push_back(t);
	}

	unordered_map<Poco::Thread*, ThreadInfo>::iterator ii;
	vector<Poco::Thread*> expiredThreads;

	//lets make sure the Main Thread is always on top
	vector< pair<Poco::Thread*, ThreadInfo> > sortedThreadList;

	for( ii = threadInfo.begin(); ii != threadInfo.end(); ++ii ){ //walk all thread trees
		if (ii->first == NULL){ //main thread is NULL!
			sortedThreadList.insert(sortedThreadList.begin(), *ii);
		}else{
			sortedThreadList.push_back(*ii);
		}
	}
	std::sort(sortedThreadList.begin(), sortedThreadList.end(), compareThreadPairs);

	#if defined(USE_OFX_HISTORYPLOT)
	vector<ofxHistoryPlot*> plotsToDraw;
	#endif

	for( int k = 0; k < sortedThreadList.size(); k++ ){ //walk all thread trees

		Poco::Thread* thread = sortedThreadList[k].first;
		core::tree<string> &tr = sortedThreadList[k].second.tree;

		PrintedLine header;
		header.formattedKey = "+" + *tr;
		header.color = threadInfo[thread].color;
		header.key = *tr; //key for selection, is thread name
		drawLines.push_back(header); //add header to drawLines

		int numAlive = 0;
		int numAdded = 0;

		core::tree<string>::iterator wholeTreeWalker = tr.in();
		bool finishedWalking = false;

		while( !finishedWalking ){

			string key = *wholeTreeWalker;
			TimeMeasurement * t = times[*wholeTreeWalker];

			#if defined(USE_OFX_HISTORYPLOT)
			bool plotActive = false;
			if(plots[key]){
				if(t->settings.plotting){
					if(t->updatedLastFrame){
						if (t->accumulating){
							plots[key]->update(t->microsecondsAccum / 1000.0f);
						}else{
							plots[key]->update(t->avgDuration / 1000.0f);
						}
					}
					plotsToDraw.push_back(plots[key]);
					plotActive = true;
				}
			}
			#endif

			bool visible = t->settings.visible;
			bool alive = t->life > 0.0001;
			if(alive){
				numAlive++;
			}

			if (visible){
				PrintedLine l;
				l.key = key;
				l.tm = t;

				int depth = wholeTreeWalker.level();
				for(int i = 0; i < depth; ++i) l.formattedKey += " ";

				if (wholeTreeWalker.size() == 0){
					l.formattedKey += "-";
				}else{
					l.formattedKey += "+";
				}
				l.formattedKey += key;
				#if defined(USE_OFX_HISTORYPLOT)
				if(plotActive) l.formattedKey += " [p]";
				#endif

				l.time = getTimeStringForTM(t);

				l.color = threadInfo[thread].color * ((1.0 - idleTimeColorFadePercent) + idleTimeColorFadePercent * t->life);
				if (!t->settings.enabled){
					l.color = disabledTextColor;
				}
				if (t->key == selection && menuActive){
					if(ofGetFrameNum()%5 < 4){
						l.color.invert();
					}
				}
				drawLines.push_back(l);
				numAdded++;
			}

			//only update() and draw() count to the final %
			if(key == TIME_MEASUREMENTS_DRAW_KEY || key == TIME_MEASUREMENTS_UPDATE_KEY){
				percentTotal += (t->avgDuration * 0.1f) / timePerFrame;
			}
			t->accumulating = false;
			t->microsecondsAccum = 0;

			//control the iterator to walk the tree "recursivelly" without doing so.
			if(wholeTreeWalker.size()){
				wholeTreeWalker = wholeTreeWalker.in();
			}else{
				if ( wholeTreeWalker.next() == wholeTreeWalker.end() ){
					wholeTreeWalker = wholeTreeWalker.out();
					while( wholeTreeWalker.next() == wholeTreeWalker.end() && wholeTreeWalker != tr){
						wholeTreeWalker = wholeTreeWalker.out();
					}
					if(wholeTreeWalker == tr){
						finishedWalking = true;
					}else{
						wholeTreeWalker++;
					}
				}else{
					++wholeTreeWalker;
				}
			}
		}

		if (numAlive == 0 && removeExpiredThreads){
			//drop that whole section if all entries in it are not alive
			for(int i = 0; i < numAdded + 1; i++){
				if(drawLines.size() > 0){
					int delID = drawLines.size() - 1;
					//clear selection if needed
					if (selection == drawLines[delID].key){
						selection = TIME_MEASUREMENTS_UPDATE_KEY;
					}
					drawLines.erase(drawLines.begin() + delID);
				}
			}
			expiredThreads.push_back(thread);
		}
	}

	//delete expired threads
	for(int i = 0; i < expiredThreads.size(); i++){
		unordered_map<Poco::Thread*, ThreadInfo>::iterator treeIt = threadInfo.find(expiredThreads[i]);
		if (treeIt != threadInfo.end()) threadInfo.erase(treeIt);
	}

	mutex.unlock();

	updateLongestLabel();

	//update max width, find headers
	int tempMaxW = -1;
	vector<int> headerLocations;
	for( int i = 0; i < drawLines.size(); i++ ){
		if (drawLines[i].tm){ //its a measurement
			//add padding to draw in columns
			for(int j = drawLines[i].formattedKey.length(); j < longestLabel; j++){
				drawLines[i].formattedKey += " ";
			}
			if (!drawLines[i].tm->error){
				drawLines[i].fullLine = drawLines[i].formattedKey + " " + drawLines[i].time;
			}else{
				drawLines[i].fullLine = drawLines[i].formattedKey + "    Error!" ;
			}
			int len = drawLines[i].fullLine.length();
			if(len > tempMaxW) tempMaxW = len;
		}else{ //its a header
			drawLines[i].fullLine = drawLines[i].formattedKey;
			headerLocations.push_back(i);
		}
	}
	maxW = tempMaxW;

	ofSetupScreen(); //mmmm----
	ofPushStyle();
	ofPushMatrix();
	ofScale(uiScale,uiScale);
	ofSetDrawBitmapMode(OF_BITMAPMODE_SIMPLE);

	ofFill();
	ofEnableAlphaBlending();

	#if defined(USE_OFX_HISTORYPLOT)
	//int numCols = plotsToDraw.size()
	for(int i = 0; i < plotsToDraw.size(); i++){
		int y = ofGetHeight() / uiScale - plotHeight * (i + 1);
		plotsToDraw[i]->draw(0, y, ofGetWidth() / uiScale, plotHeight);
		ofSetColor(99);
		if(i != plotsToDraw.size() -1){
			ofLine(0, y, ofGetWidth() / uiScale, y );
		}
	}
	#endif

	float totalW = getWidth();
	float totalH = getHeight();

	ofSetColor(bgColor, 245);
	int barH = 1;
	ofRect(x, y + 1, totalW, totalH);

	//thread header bg highlight
	for(int i = 0; i < headerLocations.size(); i++){
		int loc = headerLocations[i];
		//whole section
		ofSetColor(drawLines[loc].color, 40);
		int h = charH * ((i < headerLocations.size() - 1) ? headerLocations[i+1] - headerLocations[i] : drawLines.size() - loc );
		ofRect(x, y + 2 + loc * charH, totalW, h);
		//thread header
		ofSetColor(drawLines[loc].color, 40);
		ofRect(x, y + 2 + loc * charH, totalW, charH + 1);
	}

	ofSetColor(hilightColor);
	ofRect(x, y + 1, totalW, barH);
	ofRect(x, y + totalH - charH - 4 , totalW, barH);
	ofRect(x, y + totalH, totalW - barH, barH);

	for(int i = 0; i < drawLines.size(); i++){
		ofSetColor(drawLines[i].color);
		drawString(drawLines[i].fullLine, x , y + (i + 1) * charH);
	}

	//print bottom line, fps and stuff
	bool missingFrames = ( ofGetFrameRate() < desiredFrameRate - 1.0 ); // tolerance of 1 fps TODO!
	static char msg[128];

	sprintf(msg, "%2.1f fps % 5.1f%%", ofGetFrameRate(), percentTotal );
	if(missingFrames){
		ofSetColor(170,33,33);
	}else{
		ofSetColor(hilightColor);
	}
	int len = strlen(msg);
	string pad = " ";
	int diff = (maxW - len) - 1;
	for(int i = 0; i < diff; i++) pad += " ";
	int lastLine = ( drawLines.size() + 1 ) * charH + 2;
	drawString( pad + msg, x, y + lastLine );
	ofSetColor(hilightColor);
	drawString( " '" + ofToString(char(activateKey)) + "'" + string(timeAveragePercent < 1.0 ? " avgd!" : ""),
					   x, y + lastLine );

	for(int i = 0; i < toResetUpdatedLastFrameFlag.size(); i++){
		toResetUpdatedLastFrameFlag[i]->updatedLastFrame = false;
	}
	ofPopMatrix();
	ofPopStyle();
}