#include "graphic.h"
#include "qad_types.h"
#include "uglwidget.h"
#include "ui_graphic.h"
#include "ui_graphic_conf.h"
#include <QMetaObject>
#include <QMessageBox>
#if QT_VERSION < 0x050000
#  include <QTapAndHoldGesture>
#  include <QPanGesture>
#  include <QPinchGesture>
#endif
#ifndef Q_OS_ANDROID
#  define HAS_GL
#endif
#ifdef HAS_GL
#  ifndef GL_MULTISAMPLE
#    define GL_MULTISAMPLE 0x809D
#  endif
#endif


__GraphicRegistrator__ __graphic_registrator__;


Graphic::Graphic(QWidget * parent): QFrame(parent), canvas(0), line_x_min(this), line_x_max(this), line_y_min(this), line_y_max(this) {
	canvas_gl = 0;
	QTranslator * trans = new QTranslator();
	trans->load(":/lang/qad_graphic_" + QLocale::system().name().left(2));
	if (trans->isEmpty())
		trans->load("lang/qad_graphic_" + QLocale::system().name().left(2));
#if QT_VERSION >= 0x050000
	if (!qApp->installTranslator(trans))
		delete trans;
#else
	qApp->installTranslator(trans);
#endif
	fullscr_dialog = 0;
	gesture_angle = 45.;
	leg_update = true;
	visible_update = fullscr = need_mouse_pan = false;
	gestures =
#ifdef Q_OS_ANDROID
				true;
#else
				false;
#endif
	ui = new Ui::Graphic();
	ui->setupUi(this);
	/*line_x_min.resize(70, 22);
	line_x_max.resize(70, 22);
	line_y_min.resize(70, 22);
	line_y_max.resize(70, 22);*/
	line_x_min.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
	line_x_max.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
	((QBoxLayout * )ui->widgetLY->layout())->insertWidget(0, &line_y_min);
	((QBoxLayout * )ui->widgetLY->layout())->addWidget(&line_y_max);
	((QBoxLayout * )ui->widgetLX->layout())->insertWidget(0, &line_x_min);
	((QBoxLayout * )ui->widgetLX->layout())->addWidget(&line_x_max);
	tm.restart();
	grid_numbers_x = grid_numbers_y = 1;
	LN2 = qLn(2.);
	LN5 = qLn(5.);
	LN10 = qLn(10.);
	line_x_min.setClearButtonVisible(true);
	line_x_max.setClearButtonVisible(true);
	line_y_min.setClearButtonVisible(true);
	line_y_max.setClearButtonVisible(true);
	connect(&line_x_min, SIGNAL(valueChanged(double)), this, SLOT(lineXMinChanged(double)));
	connect(&line_x_max, SIGNAL(valueChanged(double)), this, SLOT(lineXMaxChanged(double)));
	connect(&line_y_min, SIGNAL(valueChanged(double)), this, SLOT(lineYMinChanged(double)));
	connect(&line_y_max, SIGNAL(valueChanged(double)), this, SLOT(lineYMaxChanged(double)));
	connect(ui->canvas_raster, SIGNAL(paintEvent(QPaintEvent * )), this, SLOT(canvasPaintEvent()));
	prepareCanvas(ui->canvas_raster);
#ifdef HAS_GL
	canvas_gl = new UGLWidget();
	ui->layoutCanvas->addWidget(canvas_gl);
	connect(canvas_gl, SIGNAL(paintSignal()), this, SLOT(canvasPaintEvent()));
	prepareCanvas(canvas_gl);
#endif
	icon_exp_x  = QIcon(":/icons/expand_x.png");
	icon_exp_y  = QIcon(":/icons/expand_y.png");
	icon_exp_sx = QIcon(":/icons/expand_s_x.png");
	icon_exp_sy = QIcon(":/icons/expand_s_y.png");
	icon_pause_b = QImage(":/icons/pause-back.png");
	icon_pause_f = QImage(":/icons/pause-front.png");
	aupdate = grid = isFit = isEmpty = navigation = true;
	aalias = mupdate = bufferActive = isOGL = cancel = isPrinting = guides = hasLblX = hasLblY = isHover = false;
	pause_ = only_expand_x = only_expand_y = false;
	//qDebug() << -DBL_MAX/2. << DBL_MAX/2. << DBL_MIN;
	limit_.setCoords(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX);
	eminx = eminy = DBL_MAX;
	emaxx = emaxy = DBL_MIN;
	grad_x = grad_y = Auto;
	axis_type_x = Numeric;
	min_repaint_int = 25;
	inc_x = 1.;
	legy = 0;
	buffer = 0;
	gridx = gridy = 1.;
	history = 5.;
	min_int = 1;
	max_int = 200;
	mdm = 10.;
	visible_time = -1.;
	thick = lineThickness();
	pause_phase = 0.;
	def_rect.setRect(0., 0., 1., 1.);
	selrect = def_rect;
	margins_.setRect(4, 4, 4, 4);
	curaction = gaMove;
	selbrush.setStyle(Qt::SolidPattern);
	selbrush.setColor(QColor(60, 175, 255, 100));
	text_color = palette().color(QPalette::WindowText);
	grid_pen = QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 0., Qt::DotLine);
	//graph_pen = QPen(Qt::red);
	//graph_pen.setCosmetic(true);
	graphics.append(GraphicType());
	curGraphic = 0;
	selpen = palette().color(QPalette::WindowText);
	selpen.setStyle(Qt::DashLine);
	back_color = palette().color(QPalette::Base);
	buttons_ = AllButtons;
	setOpenGL(false);
	setButtonsPosition(Graphic::Left);
	setAntialiasing(false);
	setCaption("");
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	ui->layoutButtons->update();
	updateLegend();
	setRectToLines();
	conf = new GraphicConf(graphics, this);
}


Graphic::~Graphic() {
	delete conf;
	if (buffer != 0) delete buffer;
}


void Graphic::changeEvent(QEvent * e) {
	QFrame::changeEvent(e);
	if (e->type() == QEvent::LanguageChange) {
		ui->retranslateUi(this);
		return;
	}
}


void Graphic::resizeEvent(QResizeEvent *) {
	if (leg_update) updateLegend(false);
}


void Graphic::timerEvent(QTimerEvent * ) {
	pause_phase += 0.02;
	if (pause_phase > 1.)
		pause_phase -= 1.;
	update();
}


bool Graphic::eventFilter(QObject * o, QEvent * e) {
	//qDebug() << "event" << o << e;
	if (o == canvas) {
		switch (e->type()) {
		case QEvent::Gesture:
			if (!navigation || !gestures) break;
			foreach (QGesture * g, ((QGestureEvent*)e)->gestures())
				procGesture(g);
			break;
		case QEvent::KeyPress: {
			int k = ((QKeyEvent*)e)->key();
			if ((k == Qt::Key_Back || k == Qt::Key_Escape) && fullscr) {
				leaveFullscreen();
				return true;
			}
			} break;
		case QEvent::TouchBegin:
			if (!navigation || !gestures) break;
			need_mouse_pan = true;
			break;
		case QEvent::TouchUpdate: {
			if (!navigation || !gestures) break;
			QList<QTouchEvent::TouchPoint> tpl = ((QTouchEvent*)e)->touchPoints();
			if (tpl.size() == 2) {
				need_mouse_pan = false;
				QPointF dp = tpl[0].scenePos() - tpl[1].scenePos();
				gesture_angle = rad2deg_qpie * qAtan2(qAbs(dp.y()), qAbs(dp.x()));
			}
			} break;
		default: break;
		}
	} /*else {
		if (fullscr) {
			switch (e->type()) {
			case QEvent::KeyPress:
				if ((((QKeyEvent*)e)->key() != Qt::Key_Back) || !fullscr)
					break;
			case QEvent::Close:
				leaveFullscreen();
				return true;
			case QEvent::OrientationChange:
			case QEvent::Resize: {
				QWidget * rw = canvas->parentWidget();
				if (rw)
					canvas->setGeometry(0, 0, rw->width(), rw->height());
				} break;
			default: break;
			}
		}
	}*/
	return QFrame::eventFilter(o, e);
}


void Graphic::prepareCanvas(QWidget * w) {
	connect(w, SIGNAL(mouseMoveEvent(QMouseEvent * )), this, SLOT(canvasMouseMoveEvent(QMouseEvent * )));
	connect(w, SIGNAL(mousePressEvent(QMouseEvent * )), this, SLOT(canvasMousePressEvent(QMouseEvent * )));
	connect(w, SIGNAL(mouseReleaseEvent(QMouseEvent * )), this, SLOT(canvasMouseReleaseEvent(QMouseEvent * )));
	connect(w, SIGNAL(mouseDoubleClickEvent(QMouseEvent*)), this, SLOT(canvasMouseDoubleClickEvent(QMouseEvent * )));
	connect(w, SIGNAL(wheelEvent(QWheelEvent * )), this, SLOT(canvasWheelEvent(QWheelEvent * )));
	connect(w, SIGNAL(leaveEvent(QEvent * )), this, SLOT(canvasLeaveEvent(QEvent * )));
	connect(w, SIGNAL(keyPressEvent(QKeyEvent * )), this, SLOT(canvasKeyPressEvent(QKeyEvent * )));
	//w->grabGesture(Qt::TapGesture);
	w->grabGesture(Qt::TapAndHoldGesture);
	w->grabGesture(Qt::PanGesture);
	w->grabGesture(Qt::PinchGesture);
	w->setMouseTracking(true);
	w->installEventFilter(this);
}


void Graphic::procGesture(QGesture * g) {
	if (!g) return;
	switch (g->gestureType()) {
	case Qt::PanGesture: {
		if (need_mouse_pan) break;
		QPanGesture * pg = (QPanGesture*)g;
		QPointF dp = -pg->delta();
		dp.rx() /= getScaleX();
		dp.ry() /= getScaleY();
		selrect.translate(dp);
		totalUpdate();
		} break;
	case Qt::PinchGesture: {
		QPinchGesture * pg = (QPinchGesture*)g;
		//qDebug() << pg->totalRotationAngle();
		Qt::KeyboardModifiers km = Qt::NoModifier;
		if (gesture_angle <= 20.) km = Qt::ControlModifier;
		if (gesture_angle >= 70.) km = Qt::ShiftModifier;
		QPoint cp = pg->centerPoint().toPoint();
		if (!fullscr) cp = mapFromGlobal(cp);
		procZoom(cp, (pg->scaleFactor() - 1.) * 500., km);
		totalUpdate();
		} break;
	case Qt::TapAndHoldGesture: {
		QTapAndHoldGesture * pg = (QTapAndHoldGesture*)g;
		if (pg->state() == Qt::GestureStarted)
			QMetaObject::invokeMethod(this, "enterFullscreen", Qt::QueuedConnection);
		} break;
	default:
		qDebug() << g;
		break;
	}
	//qDebug() << g;
}


void Graphic::procZoom(QPointF view_center, double dzoom, Qt::KeyboardModifiers km) {
	double scl, wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top();
	double px = view_center.x() - gridborder.x() - margins_.left(), py = hei - view_center.y() + margins_.height();
	px = px / wid * selrect.width() + selrect.x();
	py = py / hei * selrect.height() + selrect.y();
	scl = 1. - dzoom / 500.;
	if (km == Qt::NoModifier)
		selrect.setRect(px - (px - selrect.x()) * scl, py - (py - selrect.y()) * scl, selrect.width() * scl, selrect.height() * scl);
	else {
		if (km == Qt::ControlModifier)
			selrect.setRect(px - (px - selrect.x()) * scl, selrect.y(), selrect.width() * scl, selrect.height());
		if (km == Qt::ShiftModifier)
			selrect.setRect(selrect.x(), py - (py - selrect.y()) * scl, selrect.width(), selrect.height() * scl);
	}
}


void Graphic::totalUpdate() {
	isFit = false;
	emit visualRectChanged();
	update(true);
	setRectToLines();
}


void Graphic::canvasPaintEvent() {
	if (is_lines_update) return;
	//QMutexLocker ml(&mutex_);
	//static int pwid = 0, phei = 0;
	int wid = canvas->width(), hei = canvas->height();
	if (canvas->isHidden() || wid <= 1 || hei <= 1) return;
	lastw = wid;
	lasth = hei;
	font_sz = fontMetrics().size(0, "0");
	font_sz.setHeight(font_sz.height() * 1.);
#ifdef Q_OS_ANDROID
	font_sz.setWidth(font_sz.width() * 6);
#else
	font_sz.setWidth(font_sz.width() * 8);
#endif
	thick = lineThickness();
	if (buffer != 0) if (buffer->width() != wid || buffer->height() != hei) {delete buffer; buffer = 0;}
	if (buffer == 0) buffer = new QImage(wid, hei, QImage::Format_RGB32);
	if (bufferActive) {
		QPainter p(canvas);
		p.drawImage(0, 0, *buffer);
		painter = &p;
		fp_size.clear();
		if (curpos != startpos) drawAction();
		drawGuides();
		return;
	}
	//if (!aupdate && !mupdate && pwid == wid && phei == hei) return;
	/*if (pwid != wid || phei != hei) {
		line_x_min.move(0, hei - 35);
		line_x_max.move(0, 0);
		line_y_min.move(70, hei - line_x_min.height());
		line_y_max.move(wid - line_y_max.width(), hei - line_x_min.height());
	}
	line_x_min.setVisible(grid);
	line_x_max.setVisible(grid);
	line_y_min.setVisible(grid);
	line_y_max.setVisible(grid);*/
	//pwid = wid;
	//phei = hei;
	QPainter p;
#ifdef HAS_GL
	if (isOGL) {
		p.fillRect(canvas->rect(), Qt::black);
		glClearColor(0.f, 0.f, 0.f, 0.f);
		p.begin(canvas);
	} else
#endif
		p.begin(buffer);
	p.fillRect(canvas->rect(), back_color);
	painter = &p;
	p.setFont(font());
	gridborder = QPoint(5, 5);
	if (grid) {
		gridborder += QPoint(font_sz.width(), font_sz.height());
		if (hasLblY) gridborder += QPoint(font_sz.height(), 0);
		if (hasLblX) gridborder += QPoint(0, font_sz.height());
	}
	painter->setClipping(true);
	painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y()));
	emit beforeGraphicPaintEvent(painter);
	painter->setClipping(false);
	if (grid)
		drawGrid();
	p.setRenderHint(QPainter::Antialiasing, aalias);
#ifndef ANDROID
	if (isOGL) {
		if (aalias) glEnable(GL_MULTISAMPLE);
		else glDisable(GL_MULTISAMPLE);
	}
#endif
	//p.setRenderHint(QPainter::HighQualityAntialiasing, aalias);
	fp_size.clear();
	if (!aalias) p.translate(-0.5, -0.5);
	drawGraphics();
	drawGuides();
	if (pause_) drawPause();
	emit graphicPaintEvent(painter);
	p.end();
	if (isOGL) return;
	p.begin(canvas);
	p.drawImage(0, 0, *buffer);
	p.end();
}


void Graphic::canvasMouseMoveEvent(QMouseEvent * e) {
	isHover = true;
	curpos = e->pos();
	curpos_r = canvas2real(curpos);
	QPointF dp;
	QString cursorstr = tr("Cursor: ") + pointCoords(curpos_r);
	emit graphicMouseMoveEvent(curpos_r, e->buttons());
	if (e->buttons() == Qt::NoButton) {
		ui->status->setText(cursorstr);
		if (guides) update();
		return;
	}
	if (!navigation) return;
	if (gestures) {
		if (!need_mouse_pan) return;
		curaction = gaMove;
	} else
		if (curaction != gaMove && (e->buttons() & Qt::RightButton) == Qt::RightButton) return;
	switch (curaction) {
		case gaZoomInRect:
			ui->status->setText(tr("Selection") + ": " + pointCoords(startpos_r) + " -> " +
								pointCoords(curpos_r) + ",  " + tr("Size") + ": " + pointCoords(absPoint(curpos_r - startpos_r)));
			repaintCanvas(true);
		break;
		case gaZoomRangeX:
			ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.x(), 'f', 3) +
								" -> " + QString::number(curpos_r.x(), 'f', 3) + ",  " + tr("Length") + ": " +
								QString::number(qAbs(curpos_r.x() - startpos_r.x()), 'f', 3));
			repaintCanvas(true);
		break;
		case gaZoomRangeY:
			ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.y(), 'f', 3) +
								" -> " + QString::number(curpos_r.y(), 'f', 3) + ",  " + tr("Length") + ": " +
								QString::number(qAbs(curpos_r.y() - startpos_r.y()), 'f', 3));
			repaintCanvas(true);
		break;
		case gaMove:
			dp = e->pos() - prevpos;
			dp.rx() *= selrect.width() / double(gridborder.x() + 5 - lastw);
			dp.ry() *= selrect.height() / double(lasth - legy - gridborder.y() - 5);
			if (e->modifiers() == Qt::ControlModifier)
				dp.setY(0.);
			if (e->modifiers() == Qt::ShiftModifier)
				dp.setX(0.);
			selrect.translate(dp);
			totalUpdate();
		break;
		default: break;
	}
	prevpos = e->pos();
}


void Graphic::canvasMousePressEvent(QMouseEvent * e) {
	emit graphicMousePressEvent(canvas2real(QPointF(e->pos())), e->buttons());
	if (!navigation) return;
	if (gestures && !need_mouse_pan) return;
#ifdef HAS_GL
	canvas_gl->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor);
#endif
	ui->canvas_raster->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor);
	prevpos = e->pos();
	startpos = prevpos;
	startpos_r = canvas2real(startpos);
	if (cancel || gestures) return;
	if (e->button() == Qt::MidButton) curaction = gaMove;
	if (e->button() == Qt::RightButton) {
		if (bufferActive) {
			curpos = startpos;
			curpos_r = canvas2real(curpos);
			repaintCanvas(true);
			swapToNormal();
			cancel = true;
			return;
		} else {
			prevaction = curaction;
			curaction = gaMove;
			return;
		}
	}
	if (e->button() == Qt::LeftButton) {
		if (e->modifiers() == Qt::ControlModifier) curaction = gaZoomRangeX;
		else if (e->modifiers() == Qt::ShiftModifier) curaction = gaZoomRangeY;
		else curaction = gaZoomInRect;
		switch (curaction) {
			case gaZoomInRect:
			case gaZoomRangeX:
			case gaZoomRangeY:
				swapToBuffer();
			break;
			default: break;
		}
	}
	setCurrentAction(curaction);
}


void Graphic::canvasMouseReleaseEvent(QMouseEvent * e) {
	emit graphicMouseReleaseEvent(canvas2real(QPointF(e->pos())), e->buttons());
	if (gestures) return;
	need_mouse_pan = false;
	if (!navigation) return;
#ifdef HAS_GL
	canvas_gl->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor);
#endif
	ui->canvas_raster->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor);
	QPointF tlp, brp;
	QRect sr;
	sr = QRect(startpos, curpos).normalized();
	if (cancel) {
		if (e->buttons() == Qt::NoButton) cancel = false;
		return;
	}
	if (e->button() == Qt::RightButton && curaction == gaMove) {
		curaction = prevaction;
		return;
	}
	if (e->button() == Qt::LeftButton && (e->buttons() & Qt::RightButton) != Qt::RightButton) {
		if (curpos != startpos) {
			tlp = canvas2real(sr.topLeft());
			brp = canvas2real(sr.bottomRight());
			isFit = false;
			switch (curaction) {
				case gaZoomInRect:
					if (sr.width() <= 1 || sr.height() <= 1) break;
					selrect.setCoords(tlp.x(), brp.y(), brp.x(), tlp.y());
					setRectToLines();
				break;
				case gaZoomRangeX:
					if (sr.width() <= 1) break;
					findGraphicsRect(tlp.x(), brp.x());
				break;
				case gaZoomRangeY:
					if (sr.height() <= 1) break;
					findGraphicsRect(0., 0., brp.y(), tlp.y());
				break;
				default: return;
			}
		}
		swapToNormal();
		update(true);
	}
	QPointF rp = canvas2real(QPointF(e->pos()));
	ui->status->setText(tr("Cursor") + ": " + pointCoords(rp));
	emit visualRectChanged();
}


void Graphic::canvasMouseDoubleClickEvent(QMouseEvent * ) {
	autofit();
}


void Graphic::canvasWheelEvent(QWheelEvent * e) {
	//if (curaction != gaMove) return;
	emit graphicWheelEvent(canvas2real(QPointF(e->pos())), e->delta());
	if (gestures) return;
	if (!navigation) return;
	procZoom(e->pos(), e->delta(), e->modifiers());
	totalUpdate();
}


void Graphic::zoom(float factor) {
	double wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top();
	double px = wid / 2, py = hei / 2;
	px = px / wid * selrect.width() + selrect.x();
	py = py / hei * selrect.height() + selrect.y();
	selrect.setRect(px - (px - selrect.x()) * factor, py - (py - selrect.y()) * factor, selrect.width() * factor, selrect.height() * factor);
	isFit = false;
	update(true);
	setRectToLines();
}


void Graphic::fullscreen() {
	if (fullscr) leaveFullscreen();
	else enterFullscreen();
}


void Graphic::canvasLeaveEvent(QEvent * ) {
	isHover = false;
	if (guides) update(true);
	ui->status->setText(tr("Cursor") + ": ( ; )");
	leaveFullscreen();
}


void Graphic::canvasKeyPressEvent(QKeyEvent * e) {
	switch (e->key()) {
		case Qt::Key_Escape: leaveFullscreen();
		default: break;
	};
}


void Graphic::clear() {
	//cout << "clear" << endl;
	for (int i = 0; i < graphics.size(); ++i) {
		graphics[i].polyline.clear();
		graphics[i].polyline_pause.clear();
		graphics[i].max_x = 0.;
		graphics[i].cvrect = QRectF();
	}
	if (isFit) on_buttonAutofit_clicked();
}


void Graphic::setAntialiasing(bool enabled) {
	if (aalias == enabled) return;
	aalias = enabled;
	/*QGLFormat f = canvas_gl->format();
	f.setSampleBuffers(enabled);
	canvas_gl->setFormat(f);*/
	update();
}


void Graphic::setPaused(bool yes) {
	pause_ = yes;
	ui->checkPause->blockSignals(true);
	ui->checkPause->setChecked(yes);
	ui->checkPause->blockSignals(false);
	for (int i = 0; i < graphics.size(); ++i)
		graphics[i].cvrect = QRectF();
	if (!pause_) {
		killTimer(timer_pause);
		timer_pause = 0;
		update(true);
		return;
	}
	for (int i = 0; i < graphics.size(); ++i) {
		graphics[i].polyline_pause = graphics[i].polyline;
		graphics[i].polyline_pause.detach();
		graphics[i].max_x_pause = graphics[i].max_x;
	}
	timer_pause = startTimer(40);
}


void Graphic::setHistorySize(double val) {
	history = val;
	double x;
	for (int i = 0; i < graphics.size(); ++i) {
		QPolygonF & pol(graphics[i].polyline);
		if (pol.isEmpty() || history <= 0.) continue;
		graphics[i].cvrect = QRectF();
		x = pol.back().x() - history;
		for (int j = pol.size() - 2; j >= 0 ; --j)
			if (pol[j].x() < x) {
				//qDebug() << pol.size() << j;
				pol.erase(pol.begin(), pol.begin() + j);
				break;
			}
	}
}


void Graphic::setMaxVisibleTime(double val) {
	visible_time = val;
	if (isFit) on_buttonAutofit_clicked();
}


void Graphic::setOnlyExpandY(bool yes) {
	only_expand_y = yes;
	ui->checkExpandY->blockSignals(true);
	ui->checkExpandY->setCheckable(yes);
	ui->checkExpandY->blockSignals(false);
}


void Graphic::setOnlyExpandX(bool yes) {
	only_expand_x = yes;
	ui->checkExpandX->blockSignals(true);
	ui->checkExpandX->setCheckable(yes);
	ui->checkExpandX->blockSignals(false);
}


void Graphic::setGesturesNavigation(bool yes) {
	gestures = yes;
}


Graphic::GraphicsData Graphic::graphicsData() const {
	GraphicsData ret;
	ret.resize(graphics.size());
	for (int i = 0; i < graphics.size(); ++i)
		ret[i] = graphics[i].polyline;
	return ret;
}


QByteArray Graphic::graphicsDataRaw() const {
	QByteArray ret;
	QDataStream s(&ret, QIODevice::WriteOnly);
	s << graphicsData();
	return ret;
}


void Graphic::setGraphicsData(const Graphic::GraphicsData & gd) {
	setGraphicsCount(gd.size());
	for (int i = 0; i < gd.size(); ++i)
		setGraphicData(gd[i], i, false);
	updateGraphics();
}


void Graphic::setGraphicsDataRaw(const QByteArray & ba) {
	if (ba.isEmpty()) {
		clear();
		return;
	}
	Graphic::GraphicsData gd;
	QDataStream s(ba);
	s >> gd;
	setGraphicsData(gd);
}


void Graphic::setButtons(Graphic::Buttons b) {
	buttons_ = b;
	ui->buttonAutofit->setVisible(b.testFlag(Autofit));
	ui->checkGrid->setVisible(b.testFlag(Grid));
	ui->checkGuides->setVisible(b.testFlag(CursorAxis));
	ui->checkExpandY->setVisible(b.testFlag(OnlyExpandY));
	ui->checkExpandX->setVisible(b.testFlag(OnlyExpandX));
	ui->buttonFullscreen->setVisible(b.testFlag(Fullscreen));
	ui->checkBorderInputs->setVisible(b.testFlag(BorderInputs));
	ui->checkLegend->setVisible(b.testFlag(Legend));
	ui->buttonClear->setVisible(b.testFlag(Clear));
	ui->buttonConfigure->setVisible(b.testFlag(Configure));
	ui->buttonSave->setVisible(b.testFlag(Save));
	ui->buttonClose->setVisible(b.testFlag(Close));
	ui->checkPause->setVisible(b.testFlag(Pause));
	if (ui->buttonAutofit->isVisible() || ui->checkGrid->isVisible() || ui->checkGuides->isVisible() ||
		ui->buttonConfigure->isVisible() || ui->buttonSave->isVisible() || ui->checkPause->isVisible())
		ui->verticalSpacer->changeSize(0, 30, QSizePolicy::Preferred, QSizePolicy::Preferred);
	else
		ui->verticalSpacer->changeSize(0, 0, QSizePolicy::Preferred, QSizePolicy::Preferred);
	ui->layoutButtons->update();
}


void Graphic::setButtonsPosition(Graphic::Alignment a) {
	align = a;
	ui->widgetLeft->hide();
	ui->widgetRight->hide();
	switch (a) {
		case Graphic::Left:
			ui->widgetLeft->setLayout(ui->layoutButtons);
			ui->widgetLeft->show();
		break;
		case Graphic::Right:
			ui->widgetRight->setLayout(ui->layoutButtons);
			ui->widgetRight->show();
		break;
	}
}


void Graphic::addPoint(const QPointF & p, int graphic, bool update_) {
	if (graphic >= graphics.size() || graphic < 0) return;
	GraphicType & t(graphics[graphic]);
	if (!t.cvrect.isNull() && !pause_) {
//		if (graphics[graphic].cvrect.contains(p))
//			graphics[graphic].cvrect = QRectF();
		if (t.cvrect.top() < p.y()) t.cvrect.setTop(p.y());
		if (t.cvrect.bottom() > p.y()) t.cvrect.setBottom(p.y());
		if (t.cvrect.right() < p.x()) t.cvrect.setRight(p.x());
		if (t.cvrect.left() > p.x()) t.cvrect.setLeft(p.x());
	}
	if (t.polyline.size() == 0) t.max_x = p.x();
	t.polyline << p;
	if (t.max_x < p.x()) t.max_x = p.x();
	tick(graphic, true, update_);
}


void Graphic::setGraphicData(const QVector<QPointF> & g, int graphic, bool update_) {
	if (graphic >= graphics.size() || graphic < 0) return;
	GraphicType & t(graphics[graphic]);
	t.cvrect = QRectF();
	t.polyline.clear();
	t.polyline = g;
	if (t.polyline.size() == 0) {
		t.max_x = 0.;
		tick(graphic, false, update_);
		return;
	}
	t.max_x = t.polyline[0].x();
	for (int i = 1; i < t.polyline.size(); ++i)
		if (t.max_x < t.polyline[i].x())
			t.max_x = t.polyline[i].x();
	tick(graphic, false, update_);
}


void Graphic::setGraphicProperties(int graphic, const QString & name, const QColor& color, Qt::PenStyle style, double width, bool visible) {
	if (graphic < 0 || graphic >= graphics.size()) return;
	graphics[graphic].name = name;
	graphics[graphic].pen.setColor(color);
	graphics[graphic].pen.setStyle(style);
	graphics[graphic].pen.setWidth(width);
	graphics[graphic].visible = visible;
	updateLegend();
}


void Graphic::addGraphic(const QString & name, const QColor & color, Qt::PenStyle style, double width, bool visible) {
	graphics << GraphicType(name, color, style, width, visible);
	updateLegend();
}


void Graphic::setVisualRect(const QRectF & rect) {
	selrect = rect;
	isFit = false;
	update();
}


void Graphic::setDefaultRect(const QRectF & rect) {
	def_rect = rect;
	if (isFit) autofit();
}


void Graphic::saveImage() {
	QString str = QFileDialog::getSaveFileName(this, tr("Save Image"), ppath, "PNG(*.png);;JPEG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tiff *.tif);;PPM(*.ppm)");
	if (str == "") return;
	ppath = str;
	QPixmap im(canvas->size());
	mupdate = true;
	canvas->render(&im);
	mupdate = false;
	im.save(ppath);
	update(true);
}


void Graphic::setOpenGL(bool on) {
#ifdef HAS_GL
	isOGL = on;
	if (on) {
		ui->canvas_raster->hide();
		canvas_gl->show();
		canvas = canvas_gl;
	} else {
		canvas_gl->hide();
		ui->canvas_raster->show();
		canvas = ui->canvas_raster;
	}
#else
	isOGL = false;
	ui->canvas_raster->show();
	canvas = ui->canvas_raster;
#endif
	/*line_x_min.setParent(canvas);
	line_x_max.setParent(canvas);
	line_y_min.setParent(canvas);
	line_y_max.setParent(canvas);
	line_x_min.show();
	line_x_max.show();
	line_y_min.show();
	line_y_max.show();*/
	update();
}


void Graphic::update(bool force) {
	mupdate = true;
	repaintCanvas(force);
	mupdate = false;
}


void Graphic::setGraphicsCount(int arg, bool update) {
	if (arg < 0) return;
	while (graphics.size() < arg)
		graphics.append(GraphicType(tr("y(x)"), QColor::fromHsv((graphics.size() * 55) % 360, 255, 255 - qrand() % 115)));
	while (graphics.size() > arg) {
		delete graphics.back().pb;
		graphics.pop_back();
	}
	if (update) updateLegend();
}


void Graphic::removeGraphic(int arg, bool update) {
	if (arg < 0 || arg >= graphics.size()) return;
	delete graphics[arg].pb;
	graphics.remove(arg);
	if (update) updateLegend();
}


/*void Graphic::setHistogramData(const QVector<float> & g, int graphic) {
	graphics[graphic].polyline.clear();
	if (g.isEmpty()) {
		return;
	}
	QVector<float> data = g;
	QVector<int> hist;
	int ic = max_int, ci;
	double md, cd, min, max, range, cx;
	qSort(data);
	md = DBL_MAX;
	min = max = data[0];
	for (int i = 1; i < data.size(); ++i) {
		if (min > data[i]) min = data[i];
		if (max < data[i]) max = data[i];
		cd = qAbs<float>(data[i] - data[i - 1]);
		if (md > cd && cd != 0.) md = cd;
	}
	range = max - min;
	md = mdm;
	//qDebug() << md << range << ic;
	if (md != 0.)
		ic = qRound(qMax<double>(qMin<double>(double(ic), range / md), double(min_int)));
	md = range / ic;
	hist.resize(ic);
	foreach (const float & i, data) {
		ci = qRound((i - min) / range * double(ic - 1));
		//if (ci < 0) ci = 0;
		//if (ci >= ic) ci = ic - 1;
		hist[ci]++;
	}
	QPolygonF & cpol(graphics[graphic].polyline);
	if (hist.size() == 1 || range == 0.) {
		cpol << QPointF(min - 0.5, 0.) << QPointF(min - 0.25, 0.);
		cpol << QPointF(min - 0.25, hist[0]) << QPointF(min + 0.25, hist[0]);
		cpol << QPointF(min + 0.25, 0.) << QPointF(min + 0.5, 0.);
	} else {
		cpol << QPointF(min, 0.);
		for (int i = 0; i < hist.size(); ++i) {
			cx = i * range / ic + min;
			cpol << QPointF(cx, hist[i]) << QPointF(cx + md, hist[i]);
		}
		cpol << QPointF(range + min, 0.);
	}
	updateGraphics();
}*/


void Graphic::findGraphicsRect(double start_x, double end_x, double start_y, double end_y) {
	double cx, cy, maxX, minX, maxY, minY, vx;
	bool isRangeX = (start_x != end_x), isRangeY = (start_y != end_y);
//	bool isEmpty = true;
	//bool fast = true;
	bool can_fast = (start_x == 0 && end_x == 0 && start_y == 0 && end_y == 0);
	bool anyVisible = false, isTimeLimit = (visible_time > 0.) && !(isRangeX || isRangeY);
	//bool force_find = (visible_time > 0.) && (history > 0.) && (visible_time < history);
	//if (force_find) can_fast = false;
//	foreach (const GraphicType & t, graphics) {
//		const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline);
//		if (!pol.isEmpty()) {
//			isEmpty = false;
//			break;
//		}
//	}
//	if (isEmpty) {
//		grect = def_rect;
//		setRectToLines();
//		return;
//	}
//	can_fast = false;
	vx = -DBL_MAX;
	minY = minX = DBL_MAX;
	maxY = maxX = -DBL_MAX;
	foreach (const GraphicType & t, graphics) {
		if (!t.visible) continue;
		if (vx < (pause_ ? t.max_x_pause : t.max_x)) vx = (pause_ ? t.max_x_pause : t.max_x);
	}
	vx -= visible_time;
	//qDebug() << "[Graphic]" << "can_fast" << can_fast;
	for (int g = 0; g < graphics.size(); g++) {
		GraphicType & t(graphics[g]);
		if (!t.visible) continue;
		const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline);
		if (pol.isEmpty()) continue;
		bool f = true;
		//qDebug() << "[Graphic]" << "cvrect:" << t.cvrect << t.cvrect.isNull();
		if (t.cvrect.isNull() || !can_fast) {
			for (int i = 0; i < pol.size(); i++) {
				cx = pol[i].x();
				cy = pol[i].y();
				if ((start_x > cx || end_x < cx) && isRangeX) continue;
				if ((start_y > cy || end_y < cy) && isRangeY) continue;
				if ((cx < vx) && isTimeLimit) continue;
				if (f) {
					t.cvrect.setRect(cx, cy, 0, 0);
					f = false;
				} else {
					if (t.cvrect.top() < cy) t.cvrect.setTop(cy);
					if (t.cvrect.bottom() > cy) t.cvrect.setBottom(cy);
					if (t.cvrect.right() < cx) t.cvrect.setRight(cx);
					if (t.cvrect.left() > cx) t.cvrect.setLeft(cx);
				}
				//fast = false;
			}
			if (f) continue;
			//qDebug() << "[Graphic]" << "2 cvrect:"  << t.cvrect;
		}
		anyVisible = true;
		if (maxY < t.cvrect.top()) maxY = t.cvrect.top();
		if (minY > t.cvrect.bottom()) minY = t.cvrect.bottom();
		if (maxX < t.cvrect.right()) maxX = t.cvrect.right();
		if (minX > t.cvrect.left()) minX = t.cvrect.left();
		if (!can_fast) t.cvrect = QRectF();
	}
	if (!anyVisible) {
		//qDebug() << "[Graphic]" << "empty autofit";
		grect = def_rect;
		setRectToLines();
		return;
	}
//	if (fast) qDebug() << "[Graphic]" << "FAST autofit";
//	else qDebug() << "[Graphic]" << "autofit";
	if (maxX > limit_.right()) maxX = limit_.right();
	if (minX > limit_.right()) minX = limit_.right();
	if (minX < limit_.left()) minX = limit_.left();
	if (maxX < limit_.left()) maxX = limit_.left();
	if (maxY > limit_.bottom()) maxY = limit_.bottom();
	if (minY > limit_.bottom()) minY = limit_.bottom();
	if (minY < limit_.top()) minY = limit_.top();
	if (maxY < limit_.top()) maxY = limit_.top();
	if (minX > maxX) qSwap<double>(minX, maxX);
	if (minY > maxY) qSwap<double>(minY, maxY);
	if (qAbs<double>(minX - maxX) < 1E-60) {minX -= defaultRect().width()/2; maxX += defaultRect().width()/2;}
	if (qAbs<double>(minY - maxY) < 1E-60) {minY -= defaultRect().height()/2; maxY += defaultRect().height()/2;}
	if (only_expand_x) {
		if (minX > eminx) minX = eminx;
		if (maxX < emaxx) maxX = emaxx;
	}
	if (only_expand_y) {
		if (minY > eminy) minY = eminy;
		if (maxY < emaxy) maxY = emaxy;
	}
	eminx = minX; emaxx = maxX;
	eminy = minY; emaxy = maxY;
	if (isRangeX) selrect.setRect(start_x, minY, end_x - start_x, maxY - minY);
	else if (isRangeY) selrect.setRect(minX, start_y, maxX - minX, end_y - start_y);
	else grect.setRect(minX, minY, maxX - minX, maxY - minY);
	grect = grect.normalized();
	if (isFit)/* || isRangeX || isRangeY)*/ {
		if (visible_time > 0.) {
			if (grect.width() > visible_time)
				grect.setLeft(grect.right() - visible_time);
		}
		selrect = grect;
	}
	setRectToLines();
}


void Graphic::drawAction() {
	//qDebug() << "draw action";
	int wid = canvas->width(), hei = canvas->height() - gridborder.y(), sx = startpos.x(), sy = startpos.y(), cx = curpos.x(), cy = curpos.y();
	painter->setPen(selpen);
	painter->setBrush(selbrush);
	switch (curaction) {
		case gaZoomInRect: {
			QSizeF rsz = QRectF(startpos_r, curpos_r).normalized().size();
			painter->drawRect(QRect(startpos, curpos));
			fp_size = " x " + pointCoords(QPointF(rsz.width(), rsz.height()));
			} break;
		case gaZoomRangeX:
			painter->drawLine(sx, hei, sx, 0);
			painter->drawLine(cx, hei, cx, 0);
			painter->fillRect(sx, 0, cx - sx, hei, selbrush);
			fp_size = " x " + pointCoords(QPointF(qAbs(startpos_r.x() - curpos_r.x()), 0.), true, false);
			break;
		case gaZoomRangeY:
			painter->drawLine(gridborder.x(), sy, wid, sy);
			painter->drawLine(gridborder.x(), cy, wid, cy);
			painter->fillRect(gridborder.x(), sy, wid - gridborder.x(), cy - sy, selbrush);
			fp_size = " x " + pointCoords(QPointF(0., qAbs(startpos_r.y() - curpos_r.y())), false, true);
			break;
		default: break;
	}
}


void Graphic::drawGrid() {
	int gbx = gridborder.x(), gby = gridborder.y(), cwid = canvas->width(), chei = canvas->height() - legy;
	double px, py, range, step, start;
	int wid = cwid - gbx - 5, hei = chei - gby - 5, cx, cy, cnt;
	QRect rect;
	QPair<QString, QString> str;

	range = selrect.bottom() - selrect.top();
	if (grad_y == Graphic::Auto) step = splitRange(range, hei / gridy / font_sz.height() / 1.4);
	else step = gridy;//range / hei * gridy;
	start = roundTo(canvas2realY(-hei), step) - step;
	py = start + step;
	cy = 0;
	cx = gbx - 5;
	grid_pen.setWidth(qMax<int>(qMax<int>(qRound(thick / 1.4), 1), grid_pen.width()));
	QFont sf = font();
	QFont nf = sf;
	sf.setPointSizeF(qMax<qreal>(sf.pointSizeF() / 1.6, 7.));
	QFontMetrics fm(nf), sfm(sf);
	if (step > 0.) {
		cnt = 1000;
		while (cnt-- > 0) {
			py -= step;
			if (fabs(py) < step * .5) py = 0.;
			cy = real2canvasY(py);
			if (cy < 0) continue;
			if (cy > hei + 5) break;
			painter->setPen(grid_pen);
			painter->drawLine(gbx, cy, cwid, cy);
			str = gridMark(py * grid_numbers_y);
			painter->setPen(text_color);
			cy += font_sz.height() / 4.;
			int dx = font_sz.height() / 8.;
			if (!str.second.isEmpty()) {
				rect = sfm.boundingRect(str.second);
				painter->setFont(sf);
				painter->drawText(cx - rect.width() - dx, cy - font_sz.height() / 2.5, str.second);
				dx += rect.width() + font_sz.height() / 6.;
			}
			rect = fm.boundingRect(str.first);
			painter->setFont(nf);
			painter->drawText(cx - rect.width() - dx, cy, str.first);
		}
	}
	cy = real2canvasY(0.);
	if (cy >= 0 && cy <= (hei + 5)) {
		QPen _p(grid_pen);
		_p.setStyle(Qt::SolidLine);
		painter->setPen(_p);
		painter->drawLine(gbx, cy, cwid, cy);
	}
	if (hasLblY) {
		painter->setPen(text_color);
		painter->save();
		painter->translate(5, hei);
		painter->rotate(-90.);
		painter->drawText(0, 0, hei, font_sz.height(), Qt::AlignCenter, label_y);
		painter->restore();
	}

	cy = chei - font_sz.height() / 4;
	if (hasLblX) cy -= font_sz.height();
	range = selrect.right() - selrect.left();
	QString df;
	if (axis_type_x == Graphic::Numeric) {
		if (grad_x == Graphic::Auto) step = splitRange(range, wid / gridx / font_sz.width() * 1.4);
		else step = gridx;//range / wid * gridx;
		start = roundTo(canvas2realX(wid), step) + step;
		px = start + step;
		if (step > 0.) {
			cnt = 1000;
			while (cnt-- > 0) {
				px -= step;
				if (fabs(px) < step * .5) px = 0.;
				cx = real2canvasX(px);
				if (cx > cwid) continue;
				if (cx < gbx) break;
				painter->setPen(grid_pen);
				painter->drawLine(cx, hei + 5, cx, 0);
				painter->setPen(text_color);
				int dx = -font_sz.height() / 4.;
				painter->setFont(nf);
				str = gridMark(px * grid_numbers_x);
				rect = fm.boundingRect(str.first);
				painter->drawText(cx + dx, cy, str.first);
				dx += rect.width() + font_sz.height() / 6.;
				if (!str.second.isEmpty()) {
					rect = sfm.boundingRect(str.second);
					painter->setFont(sf);
					painter->drawText(cx + dx, cy - font_sz.height() / 4., str.second);
				}
			}
		}
		cx = real2canvasX(0.);
		if (cx <= cwid && cx >= gbx) {
			QPen _p(grid_pen);
			_p.setStyle(Qt::SolidLine);
			painter->setPen(_p);
			painter->drawLine(cx, hei + 5, cx, 0);
		}
	} else {
		int cur_scl[7] = {0,0,0,0,0,0,0};
		step = splitRangeDate(range, wid / gridx / font_sz.width() * 1.4, &df, cur_scl);
		start = roundTo(canvas2realX(wid), step) + step;
		px = start + step;
		QDateTime cd = QDateTime::fromMSecsSinceEpoch(px * grid_numbers_x);
		//qDebug() << "*** start" << cd << step;
		roundDateTime(cd, cur_scl);
		//qDebug() << "*** round" << cd;
		addDateTime(cd, cur_scl);
		//qDebug() << "***   add" << cd;
		//qDebug() << "*** cur" << cur_scl[0] << cur_scl[1] << cur_scl[2] << cur_scl[3] << cur_scl[4] << cur_scl[5] << cur_scl[6];
		if (step > 0.) {
			cnt = 1000;
			while (cnt-- > 0) {
				addDateTime(cd, cur_scl, -1);
				//roundDateTime(cd, cur_scl);
				//qDebug() << "next" << cd;
				cx = real2canvasX(cd.toMSecsSinceEpoch() / grid_numbers_x);
				if (cx > cwid) continue;
				if (cx < gbx) {/*qDebug() << cx << "<" << gbx;*/ break;}
				painter->setPen(grid_pen);
				painter->drawLine(cx, hei + 5, cx, 0);
				painter->setPen(text_color);
				int dx = -font_sz.height() / 4.;
				painter->setFont(nf);
				str.first = cd.toString(df);
				painter->drawText(cx + dx, cy, str.first);
			}
		}
	}
	painter->setPen(text_color);
	painter->setFont(nf);
	if (hasLblX) {
		painter->setPen(text_color);
		painter->drawText(gbx, chei - font_sz.height(), wid, font_sz.height(), Qt::AlignCenter, label_x);
	}

	painter->setPen(QPen(grid_pen.color(), qMax<int>(thick, grid_pen.width())));
	painter->drawRect(gbx, -1, wid + 6, hei + 6);
}


QPair<QString, QString> Graphic::gridMark(double v) const {
	QPair<QString, QString> ret;
	if ((qAbs(v) >= 1E+4 || qAbs(v) <= 1E-4) && v != 0.) {
		int p = qFloor(qLn(qAbs(v)) / LN10);
		v /= qPow(10., p);
		if (v == 10.) {
			v = 1.;
			p += 1;
		}
		ret.first = QString::fromUtf8("%1·10").arg(v);
		ret.second = QString::number(p);
	} else
		ret.first = QString::number(v);
	return ret;
}


void Graphic::drawGraphics() {
	if (isHover)
		ui->status->setText(tr("Cursor: ") + pointCoords(canvas2real(QPointF(curpos))));
	QPointF srp = -selrect.topLeft();
	double sclx, scly, wid = canvas->width(), hei = canvas->height() - legy;
	sclx = (wid - gridborder.x() - margins_.left() - margins_.width()) / selrect.width();
	scly = (hei - gridborder.y() - margins_.top() - margins_.height()) / selrect.height();
	painter->setClipping(true);
	painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y()));
	painter->translate(gridborder.x() + margins_.left(), hei - gridborder.y() - margins_.top());
	//if (isOGL && aalias) pen.setWidthF(1.5f);
	painter->scale(sclx, -scly);
	painter->translate(srp);
	QTransform mat = painter->transform();
	painter->resetTransform();
	painter->setWorldMatrixEnabled(false);
	QPolygonF cpol;
	QPen pen;
	for (int i = 0; i < graphics.size(); ++i) {
		GraphicType & t(graphics[i]);
		QPolygonF & rpol(pause_ ? t.polyline_pause : t.polyline);
		if (t.visible && !rpol.isEmpty()) {
			pen = t.pen;
			if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick);
			else pen.setWidthF(pen.widthF()*thick);
			pen.setCosmetic(true);
			if (t.lines) {
				painter->setPen(pen);
				if (t.fill) {
					cpol = rpol;
					painter->setBrush(t.fill_color);
					//cpol.push_front(QPointF(cpol.front().x(), 0.));
					//cpol.push_back(QPointF(cpol.back().x(), 0.));
					painter->drawPolygon(mat.map(cpol));
				} else
					painter->drawPolyline(mat.map(rpol));
			}
			if (t.points) {
				if (qRound(t.pointWidth) == t.pointWidth) pen.setWidth(qRound(t.pointWidth*thick));
				else pen.setWidthF(t.pointWidth*thick);
				painter->setPen(pen);
				painter->drawPoints(mat.map(rpol));
			}
		}
	}
	painter->setWorldMatrixEnabled(true);
}


QString Graphic::pointCoords(QPointF point, bool x, bool y) {
	QString ret = "(";
	if (x) {
		if (axis_type_x == Numeric)
			ret += QString::number(point.x(), 'f', 3);
		else
			ret += QDateTime::fromMSecsSinceEpoch(point.x()).toString();
	}
	if (y) {
		if (ret.size() > 1) ret += " ; ";
		ret += QString::number(point.y(), 'f', 3);
	}
	ret += ")";
	return ret;
}


void Graphic::drawGuides() {
	if (!guides || !isHover) return;
	int wid = canvas->width(), hei = canvas->height();
	painter->setRenderHint(QPainter::Antialiasing, false);
	painter->setPen(QPen(grid_pen.color(), qMax<int>(qRound(thick / 1.4), 1)));
	painter->resetTransform();
	painter->setClipping(true);
	painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y()));
	painter->drawLine(0, curpos.y(), wid, curpos.y());
	painter->drawLine(curpos.x(), 0, curpos.x(), hei);
	QString str = pointCoords(canvas2real(curpos)) + fp_size;
	QFontMetrics fm(font());
	QRect r = fm.boundingRect(str);
	QPoint p = curpos + QPoint(font_sz.height() / 4., -font_sz.height() / 4.);
	if (r.width() + curpos.x() > wid - font_sz.height() / 2.) p.setX(curpos.x() - r.width() - font_sz.height() / 4.);
	if (curpos.y() - r.height() < font_sz.height() / 8.) p.setY(curpos.y() + r.height() - font_sz.height() / 8.);
	painter->setPen(text_color);
	painter->drawText(p, str);
}


void Graphic::drawPause() {
	painter->setClipping(false);
	painter->save();
	painter->resetTransform();
	painter->translate(canvas->width() - icon_pause_b.width() - 6, 6);
	double o = (0.5 - pause_phase) * 2;
	painter->setOpacity(o*o);
	painter->drawImage(0, 0, icon_pause_b);
	painter->setOpacity(1.);
	painter->drawImage(0, 0, icon_pause_f);
	painter->restore();
	painter->setClipping(true);
}


double Graphic::splitRange(double range, int count) {
	double digits, step, tln;
	range = qAbs<double>(range);
	tln = qFloor(qLn(range) / LN10);
	for (int i = 0; i <= 5; ++i) {
		digits = qPow(10., tln - i);
		step = qRound(range / count / digits);
		if (step > 0.) {
			digits = qPow(10., tln - i - 1);
			step = qRound(range / count / digits);
			break;
		}
	}
	double step5 = qRound(step / 5.) * 5., step10 = qRound(step / 10.) * 10.;
	double err5 = qAbs<double>(step - step5), err10 = qAbs<double>(step - step10);
	step = (err5 < err10 ? step5 : step10) * digits;
	return step;
}


double Graphic::splitRangeDate(double range, int count, QString * format, int step[7]) {
	double ret = splitRange(range, count);
	//qDebug() << "ret =" << ret << getScaleX();
	if (ret < 1000. * 1) {*format = "ss.zzz"; step[0] = ret;}
	else if (ret < 1000. * 60) {*format = "h:m:ss"; step[1] = qRound(ret / 1000);}
	else if (ret < 1000. * 60 * 60) {*format = "h:mm"; step[2] = qRound(ret / 1000 / 60);}
	else if (ret < 1000. * 60 * 60 * 24) {*format = "dd(ddd) hh"; step[3] = qRound(ret / 1000 / 60 / 60);}
	else if (ret < 1000. * 60 * 60 * 24 * 30) {*format = "MMM dd"; step[4] = qRound(ret / 1000 / 60 / 60 / 24);}
	else if (ret < 1000. * 60 * 60 * 24 * 30 * 12) {*format = "yyyy MMM"; step[5] = qRound(ret / 1000 / 60 / 60 / 24 / 30);}
	else {*format = "yyyy"; step[6] = qRound(ret / 1000 / 60 / 60 / 24 / 30 / 12);}
	return ret;
}


double Graphic::roundTo(double value, double round_to) {
	if (round_to == 0.) return value;
	return qRound(value / round_to) * round_to;
}


void Graphic::roundDateTime(QDateTime & dt, int c[7]) {
	QDate d(dt.date()); QTime t(dt.time());
	//if (c[0] != 0) t.setHMS(t.hour(), t.minute(), t.second(), 0);
	if (c[1] != 0) t.setHMS(t.hour(), t.minute(), t.second());
	if (c[2] != 0) t.setHMS(t.hour(), t.minute(), 0);
	if (c[3] != 0) t.setHMS(t.hour(), 0, 0);
	if (c[4] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), d.day());}
	if (c[5] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), 1);}
	if (c[6] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), 1, 1);}
	dt = QDateTime(d, t);
}


void Graphic::addDateTime(QDateTime & dt, int c[7], int mul) {
	if (c[0] != 0) dt = dt.addMSecs(mul * c[0]);
	if (c[1] != 0) dt = dt.addSecs(mul * c[1]);
	if (c[2] != 0) dt = dt.addSecs(mul * c[2] * 60);
	if (c[3] != 0) dt = dt.addSecs(mul * c[3] * 60 * 60);
	if (c[4] != 0) dt = dt.addDays(mul * c[4]);
	if (c[5] != 0) dt = dt.addMonths(mul * c[5]);
	if (c[6] != 0) dt = dt.addYears(mul * c[6]);
}


double Graphic::canvas2realX(double px) const {
	int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width();
	double cx = px - gbx, sclx = selrect.width() / (double)wid;
	return cx * sclx + selrect.x();
}


double Graphic::canvas2realY(double py) const {
	int gby = gridborder.y() + margins_.top(), chei = lasth - legy, hei = chei - gby - margins_.height();
	double cy = chei - py - gby, scly = selrect.height() / (double)hei;
	return cy * scly + selrect.y();
}


double Graphic::real2canvasX(double px) const {
	int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width();
	double sclx = selrect.width() / (double)wid;
	return (px - selrect.x()) / sclx + gbx;
}


double Graphic::real2canvasY(double py) const {
	int gby = gridborder.y() + margins_.top(), chei = lasth - legy, hei = chei - gby - margins_.height();
	double scly = selrect.height() / (double)hei;
	return chei - gby - (py - selrect.y()) / scly;
}


QPolygonF Graphic::real2canvas(const QPolygonF & real_polygon) const {
	QPolygonF ret;
	for (int i=0; i<real_polygon.size(); ++i)
		ret << real2canvas(real_polygon[i]);
	return ret;
}


QPolygonF Graphic::canvas2real(const QPolygonF & canvas_polygon) const {
	QPolygonF ret;
	for (int i=0; i<canvas_polygon.size(); ++i)
		ret << canvas2real(canvas_polygon[i]);
	return ret;
}


void Graphic::setCurrentAction(GraphicAction action) {
	curaction = action;
	switch (action) {
		case gaNone:
			if (guides) setCanvasCursor(Qt::BlankCursor);
			else setCanvasCursor(Qt::ArrowCursor);
		break;
		case gaZoomInRect:
			setCanvasCursor(Qt::CrossCursor);
		break;
		case gaZoomRangeX:
			setCanvasCursor(Qt::SplitHCursor);
		break;
		case gaZoomRangeY:
			setCanvasCursor(Qt::SplitVCursor);
		break;
		case gaMove:
			setCanvasCursor(Qt::SizeAllCursor);
		break;
	}
}


void Graphic::setCanvasCursor(QCursor cursor) {
	ui->canvas_raster->setCursor(cursor);
#ifdef HAS_GL
	canvas_gl->setCursor(cursor);
#endif
}


void Graphic::swapToBuffer() {
	QImage timg;
	//qDebug() << "render start";
#ifdef HAS_GL
	if (isOGL) {
		timg = canvas_gl->grabFrameBuffer();
		QPainter p(buffer);
		p.drawImage(0, 0, timg);
		p.end();
	}
#endif
	//qDebug() << "render finish";
	bufferActive = true;
}


void Graphic::setRectToLines() {
	is_lines_update = true;
	//line_x_min.is_auto = line_x_max.is_auto = line_y_min.is_auto = line_y_max.is_auto = true;
	//qDebug() << "set to lines" << selrect;
	//line_x_min.is_reset = line_x_max.is_reset = line_y_min.is_reset = line_y_max.is_reset = isFit;
	if (line_x_min.isVisible() && line_x_max.isVisible() && line_y_min.isVisible() && line_y_max.isVisible()) {
		line_x_min.blockSignals(true); line_x_max.blockSignals(true); line_y_min.blockSignals(true); line_y_max.blockSignals(true);
		if (!line_x_min.hasFocus()) {
			if (isFit) line_x_min.setValue(grect.left());
			else line_x_min.setValue(selrect.left());
		}
		if (!line_x_max.hasFocus()) {
			if(isFit) line_x_max.setValue(grect.right());
			else line_x_max.setValue(selrect.right());
		}
		if (!line_y_min.hasFocus()) {
			if(isFit) line_y_min.setValue(grect.bottom());
			else line_y_min.setValue(selrect.bottom());
		}
		if (!line_y_max.hasFocus()) {
			if(isFit) line_y_max.setValue(grect.top());
			else line_y_max.setValue(selrect.top());
		}
		line_x_min.setDefaultText(QString::number(grect.left()).toUpper());
		line_x_max.setDefaultText(QString::number(grect.right()).toUpper());
		line_y_min.setDefaultText(QString::number(grect.bottom()).toUpper());
		line_y_max.setDefaultText(QString::number(grect.top()).toUpper());
		line_x_min.blockSignals(false); line_x_max.blockSignals(false); line_y_min.blockSignals(false); line_y_max.blockSignals(false);
//		if(isFit) {
//			line_y_min.setValue(grect.left());
//			line_y_max.setValue(grect.left());
//		}
	}
	//line_x_min.is_auto = line_x_max.is_auto = line_y_min.is_auto = line_y_max.is_auto = false;
	is_lines_update = false;
}


void Graphic::checkLines() {
	isFit = (line_x_min.isCleared() && line_x_max.isCleared() && line_y_min.isCleared() && line_y_max.isCleared());
	update(true);
}


void Graphic::tick(int index, bool slide, bool update_) {
	if (slide) {
		///mutex.lock();
		GraphicType & t(graphics[index]);
		if (history > 0.)
			while (t.polyline.size() > 1) {
				if (fabs(t.polyline.back().x() - t.polyline.front().x()) <= history) break;
				/// TODO: [Graphic] fast autofit while addPoint(double y, ...)
				if (!t.cvrect.isNull()) {
					QPointF fp(t.polyline.first());
					if (qFuzzyCompare(t.cvrect.left(),   fp.x()) ||
						qFuzzyCompare(t.cvrect.right(),  fp.x()) ||
						qFuzzyCompare(t.cvrect.top(),    fp.y()) ||
						qFuzzyCompare(t.cvrect.bottom(), fp.y())) {
						t.cvrect = QRectF();
					}
				}
				t.polyline.pop_front();
			}
	}
	if (!update_) {
		if (isFit) findGraphicsRect();
		///mutex.unlock();
		return;
	}
	//polyline.push_back(QPointF(brick->time_, brick->output(port)));
	//cout << polyline.size() << endl;
	if (isFit) findGraphicsRect();
	if (!slide) {
		if (aupdate) update();
		return;
	}
	///mutex.unlock();
	if (aupdate) update();
}


void Graphic::on_buttonAutofit_clicked() {
	isFit = true;
	bool isEmpty = true;
	foreach (const GraphicType & t, graphics) {
		const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline);
		if (!pol.isEmpty()) {
			isEmpty = false;
			break;
		}
	}
	if (isEmpty) grect = def_rect;
	selrect = grect;
	findGraphicsRect();
	update();
}


void Graphic::on_buttonConfigure_clicked() {
	conf->graphicItems.clear();
	for (int i = 0; i < graphics.size(); i++) {
		GraphicConf::GraphicItem item;
		item.icon = graphics[i].icon;
		item.name = graphics[i].name;
		conf->graphicItems.append(item);
	}
	conf->ui->colorGrid->setColor(grid_pen.color());
	conf->ui->comboStyleGrid->setCurrentIndex((int)grid_pen.style());
	conf->ui->spinWidthGrid->setValue(grid_pen.widthF());
	conf->ui->checkOGL->setChecked(isOGL);
	conf->ui->checkAAlias->setChecked(aalias);
	conf->ui->checkInputs->setChecked(borderInputsVisible());
	conf->ui->checkStatus->setChecked(statusVisible());
	conf->ui->checkLegend->setChecked(legendVisible());
	conf->ui->checkGridAutoX->setChecked(grad_x == Auto);
	conf->ui->checkGridAutoY->setChecked(grad_y == Auto);
	conf->ui->colorBackground->setColor(back_color);
	conf->ui->colorText->setColor(text_color);
	conf->ui->spinGridStepX->setValue(gridx);
	conf->ui->spinGridStepY->setValue(gridy);
	conf->ui->spinMarginL->setValue(margins_.left());
	conf->ui->spinMarginT->setValue(margins_.height());
	conf->ui->spinMarginR->setValue(margins_.width());
	conf->ui->spinMarginB->setValue(margins_.top());
	conf->readParams();
	if (conf->exec() == QDialog::Rejected) return;
	grid_pen = QPen(conf->ui->colorGrid->color(), conf->ui->spinWidthGrid->value(), (Qt::PenStyle)conf->ui->comboStyleGrid->currentIndex());
	back_color = conf->ui->colorBackground->color();
	text_color = conf->ui->colorText->color();
	grad_x = conf->ui->checkGridAutoX->isChecked() ? Auto : Fixed;
	grad_y = conf->ui->checkGridAutoY->isChecked() ? Auto : Fixed;
	gridx = conf->ui->spinGridStepX->value();
	gridy = conf->ui->spinGridStepY->value();
	setOpenGL(conf->ui->checkOGL->isChecked());
	setAntialiasing(conf->ui->checkAAlias->isChecked());
	setBorderInputsVisible(conf->ui->checkInputs->isChecked());
	setStatusVisible(conf->ui->checkStatus->isChecked());
	setLegendVisible(conf->ui->checkLegend->isChecked());
	setMargins(conf->ui->spinMarginL->value(), conf->ui->spinMarginR->value(), conf->ui->spinMarginT->value(), conf->ui->spinMarginB->value());
	updateLegend();
	update();
}


void Graphic::on_checkGuides_toggled(bool checked) {
	guides = checked;
	if (guides) setCanvasCursor(Qt::BlankCursor);
	else setCanvasCursor(Qt::ArrowCursor);
	update();
}


void Graphic::updateLegend(bool es) {
	QPixmap pix(60, 22);
	for (int i = 0; i < graphics.size(); i++) {
		pix.fill(back_color);
		QPainter p(&pix);
		QPen pen = graphics[i].pen;
		if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick);
		else pen.setWidthF(pen.widthF()*thick);
		p.setPen(pen);
		p.drawLine(0, pix.height() / 2, pix.width(), pix.height() / 2);
		p.end();
		graphics[i].icon = QIcon(pix);
	}
	if (!ui->widgetLegend->isVisibleTo(this)) {
		if (es) emit graphicSettingsChanged();
//		qDebug() << "skip updateLegend";
		return;
	}
//	qDebug() << "updateLegend" << graphics.size();
	leg_update = false;
	int ps = 100;
	for (int r = 0; r < ui->layoutLegend->rowCount(); ++r)
		for (int c = 0; c < ui->layoutLegend->columnCount(); ++c) {
			QLayoutItem * li = ui->layoutLegend->itemAtPosition(r, c);
			if (!li) continue;
			if (!li->widget()) continue;
			while (li->widget()->actions().isEmpty())
				li->widget()->removeAction(li->widget()->actions()[0]);
			delete li->widget();
		}
	ui->layoutLegend->invalidate();
	for (int i = 0; i < graphics.size(); i++) {
		graphics[i].pb = new QCheckBox(graphics[i].name);
		graphics[i].pb->setIconSize(pix.size());
		//graphics[i].pb->setFlat(true);
		graphics[i].pb->setIcon(graphics[i].icon);
		graphics[i].pb->setChecked(graphics[i].visible);
		graphics[i].pb->setProperty("graphic_num", i);
		graphics[i].pb->setContextMenuPolicy(Qt::ActionsContextMenu);
		//qDebug() << graphics[i].pb->actions();
		QAction * act = new QAction(tr("Check all"), 0);
		act->setCheckable(true);
		act->setChecked(true);
		graphics[i].pb->addAction(act);
		connect(act, SIGNAL(triggered(bool)), this, SLOT(graphicAllVisibleChange(bool)));
		connect(graphics[i].pb, SIGNAL(toggled(bool)), this, SLOT(graphicVisibleChange(bool)));
		int cps = graphics[i].pb->sizeHint().width() + 4;
		if (cps > ps) ps = cps;
	}
	int maxcol = qMax<int>(ui->widgetLegend->width() / ps - 1, 1);
	int row = 0, col = 0;
	bool lv = ui->widgetLegend->isVisibleTo(this);
	ui->widgetLegend->hide();
	for (int i = 0; i < graphics.size(); i++) {
		ui->layoutLegend->addWidget(graphics[i].pb,row,col);
		graphics[i].pb->show();
		col++;
		if (col > maxcol) {col = 0; row++;}
	}
	ui->widgetLegend->setVisible(lv);
	leg_update = true;
	if (es) emit graphicSettingsChanged();
}


void Graphic::updateLegendChecks() {
	for (int i = 0; i < graphics.size(); i++) {
		if (!graphics[i].pb) continue;
		bool pbs = graphics[i].pb->blockSignals(true);
		graphics[i].pb->setChecked(graphics[i].visible);
		graphics[i].pb->blockSignals(pbs);
	}
	emit graphicSettingsChanged();
}


void Graphic::graphicVisibleChange(bool checked) {
	if (visible_update) return;
	QCheckBox * cb = qobject_cast<QCheckBox*>(sender());
	int i = cb->property("graphic_num").toInt();
	graphics[i].visible = checked;
	if (isFit) on_buttonAutofit_clicked();
	else update();
	emit graphicSettingsChanged();
	//update();
}


void Graphic::graphicAllVisibleChange(bool checked) {
	visible_update = true;
	for (int i=0; i<graphics.size(); i++) {
		graphics[i].visible = checked;
		graphics[i].pb->setChecked(checked);
	}
	visible_update = false;
	if (isFit) on_buttonAutofit_clicked();
	else update();
	emit graphicSettingsChanged();
}


void Graphic::enterFullscreen() {
	if (fullscr) return;
	//QMessageBox::information(0, "", "enter");
	fullscr = true;
	canvas->hide();
#ifdef Q_OS_ANDROID
	tm_fscr.restart();
	QDialog dlg;
	dlg.setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
	dlg.layout()->setContentsMargins(0, 0, 0, 0);
	dlg.layout()->addWidget(canvas);
	QPushButton * btn = new QPushButton("Leave fullscreen");
	dlg.layout()->addWidget(btn);
	connect(btn, SIGNAL(clicked(bool)), this, SLOT(leaveFullscreen()));
	//connect(fullscr_dialog, SIGNAL(finished(int)), this, SLOT(leaveFullscreen()));
	canvas->show();
	dlg.showFullScreen();
	dlg.exec();
	dlg.layout()->removeWidget(canvas);
	leaveFullscreen();
#else
	ui->layoutCanvas->removeWidget(canvas);
	canvas->setParent(0);
	canvas->showFullScreen();
	canvas->setFocus();
	canvas->raise();
#endif
}


void Graphic::leaveFullscreen() {
#ifdef Q_OS_ANDROID
	if (tm_fscr.elapsed() < 100) return;
#endif
	if (!fullscr) return;
	//QMessageBox::information(0, "", "leave");
	fullscr = false;
	//canvas->hide();
#ifndef Q_OS_ANDROID
	canvas->showNormal();
	canvas->hide();
#endif
	ui->layoutCanvas->addWidget(canvas);
	canvas->show();
}


QString Graphic::caption() const {
	return ui->labelCaption->text();
}


bool Graphic::borderInputsVisible() const {
	return ui->widgetLX->isVisible();
}


bool Graphic::statusVisible() const {
	return ui->status->isVisible();
}


bool Graphic::legendVisible() const {
	return ui->widgetLegend->isVisible();
}


QByteArray Graphic::save() {
//	QByteArray ba;
//	QDataStream s(&ba, QIODevice::ReadWrite);
//	s << openGL() << antialiasing() << borderInputsVisible() << statusVisible() << legendVisible();
//	s << graphics;
//	return ba;

// version '2':
	ChunkStream cs;
	cs.add(1, antialiasing()).add(2, openGL()).add(3, borderInputsVisible()).add(4, statusVisible()).add(5, legendVisible());
	cs.add(6, backgroundColor()).add(7, textColor()).add(8, margins());
	cs.add(9, gridPen()).add(10, graduationX()).add(11, graduationY()).add(12, graduationStepX()).add(13, graduationStepY());
	cs.add(14, graphics);
	cs.add(15, isFit).add(16, visualRect());
	if (backgroundColor() == palette().color(QPalette::Base) &&
		textColor() == palette().color(QPalette::WindowText) &&
		gridColor() == palette().color(QPalette::Disabled, QPalette::WindowText))
			cs.add(17, true);
	return cs.data().prepend('2');
}


void Graphic::load(QByteArray ba) {
	if (ba.isEmpty()) return;
	char ver = ba[0];
	//qDebug() << "load" << (int)ver;
	switch(ver) {
	case '2': {// version '2':
		ba.remove(0, 1);
		QRectF vrect;
		ChunkStream cs(ba);
		bool def_colors = false;
		while (!cs.atEnd()) {
			switch (cs.read()) {
			case 1: setAntialiasing(cs.getData<bool>()); break;
			case 2: setOpenGL(cs.getData<bool>()); break;
			case 3: setBorderInputsVisible(cs.getData<bool>()); break;
			case 4: setStatusVisible(cs.getData<bool>()); break;
			case 5: setLegendVisible(cs.getData<bool>()); break;
			case 6: if (!def_colors) setBackgroundColor(cs.getData<QColor>()); break;
			case 7: if (!def_colors) setTextColor(cs.getData<QColor>()); break;
			case 8: setMargins(cs.getData<QRect>()); break;
			case 9: if (!def_colors) setGridPen(cs.getData<QPen>()); break;
			case 10: setGraduationX(cs.getData<Graduation>()); break;
			case 11: setGraduationY(cs.getData<Graduation>()); break;
			case 12: setGraduationStepX(cs.getData<double>()); break;
			case 13: setGraduationStepY(cs.getData<double>()); break;
			case 14: graphics = cs.getData<QVector<GraphicType> >(); break;
			case 15: isFit = cs.getData<bool>(); break;
			case 16: vrect = cs.getData<QRectF>(); break;
			case 17: if(cs.getData<bool>()) {
						setTextColor(palette().color(QPalette::WindowText));
						setGridPen(QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 0., Qt::DotLine));
						setBackgroundColor(palette().color(QPalette::Base));
						def_colors = true;
					} break;
			default: break;
			}
		}
		if (!isFit) setVisualRect(vrect);
	} break;
	default: {// old version 0:
		QDataStream s(ba);
		bool a;
		s >> a; setOpenGL(a);
		s >> a; setAntialiasing(a);
		s >> a; setBorderInputsVisible(a);
		s >> a; setStatusVisible(a);
		s >> a;
		s >> graphics;
		setLegendVisible(a);
	} break;
	}
}


void Graphic::setCaption(const QString & str) {
	ui->labelCaption->setText(str);
	ui->labelCaption->setVisible(str.length() > 0);
	if (aupdate) update();
}


void Graphic::setGridEnabled(bool enabled) {
	ui->checkGrid->setChecked(enabled);
}


void Graphic::setBorderInputsVisible(bool visible) {
	ui->widgetLX->setVisible(visible);
	ui->widgetLY->setVisible(visible);
	ui->checkBorderInputs->setChecked(visible);
	if (visible) setRectToLines();
}


void Graphic::setStatusVisible(bool visible) {
	ui->status->setVisible(visible);
}


void Graphic::setLegendVisible(bool visible) {
	ui->widgetLegend->setVisible(visible);
	ui->checkLegend->setChecked(visible);
	updateLegend();
}


void Graphic::on_checkExpandY_toggled(bool checked) {
	only_expand_y = checked;
	ui->checkExpandY->setIcon(checked ? icon_exp_y : icon_exp_sy);
}


void Graphic::on_checkExpandX_toggled(bool checked) {
	only_expand_x = checked;
	ui->checkExpandX->setIcon(checked ? icon_exp_x : icon_exp_sx);
}
