From 85285d986234d99da938e4b02a0a69fb8bbbbfdb Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 13 Nov 2014 21:26:17 +0300
Subject: [PATCH 1/2] fixed os x mouse input, window icon, qt os x
 build-from-source

---
 Telegram/SourceFiles/history.cpp              |  2 +-
 Telegram/SourceFiles/mtproto/mtp.h            |  5 ++-
 .../SourceFiles/mtproto/mtpFileLoader.cpp     | 39 +++++++++++--------
 Telegram/SourceFiles/mtproto/mtpFileLoader.h  |  2 +
 Telegram/SourceFiles/overviewwidget.cpp       | 29 +++++---------
 Telegram/SourceFiles/pspecific_linux.cpp      |  4 +-
 Telegram/SourceFiles/pspecific_linux.h        |  1 +
 Telegram/SourceFiles/pspecific_mac.cpp        |  4 +-
 Telegram/SourceFiles/pspecific_mac.h          |  1 +
 Telegram/SourceFiles/pspecific_wnd.cpp        |  2 +-
 Telegram/SourceFiles/pspecific_wnd.h          |  1 +
 Telegram/SourceFiles/telegram.qrc             |  1 +
 Telegram/SourceFiles/window.cpp               |  5 +--
 Telegram/SourceFiles/window.h                 |  2 -
 .../plugins/platforms/cocoa/qcocoawindow.mm   | 13 ++++++-
 15 files changed, 63 insertions(+), 48 deletions(-)

diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index d89bd59ead..bcb433e63b 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -1755,7 +1755,7 @@ void History::clear(bool leaveItems) {
 		_overview[i].clear();
 		_overviewIds[i].clear();
 	}
-	if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
+	if (App::wnd() && !App::quiting()) App::wnd()->mediaOverviewUpdated(peer);
 	for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) {
 		if (leaveItems) {
 			(*i)->clear(true);
diff --git a/Telegram/SourceFiles/mtproto/mtp.h b/Telegram/SourceFiles/mtproto/mtp.h
index 4093798631..0ac3edef95 100644
--- a/Telegram/SourceFiles/mtproto/mtp.h
+++ b/Telegram/SourceFiles/mtproto/mtp.h
@@ -95,7 +95,10 @@ namespace MTP {
 	void initdc(int32 dc);
 	template <typename TRequest>
 	inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
-		return _mtp_internal::getSession(dc)->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc, after);
+		MTProtoSessionPtr session = _mtp_internal::getSession(dc);
+		if (!session) return 0;
+		
+		return session->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc, after);
 	}
 	template <typename TRequest>
 	inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp
index 28bd479f62..75dca6ed94 100644
--- a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp
+++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp
@@ -46,7 +46,7 @@ namespace {
 mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : prev(0), next(0),
 priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
 dc(dc), locationType(0), volume(volume), local(local), secret(secret),
-id(0), access(0), size(size), type(MTP_storage_fileUnknown()) {
+id(0), access(0), fileIsOpen(false), size(size), type(MTP_storage_fileUnknown()) {
 	LoaderQueues::iterator i = queues.find(dc);
 	if (i == queues.cend()) {
 		i = queues.insert(dc, mtpFileLoaderQueue());
@@ -57,7 +57,7 @@ id(0), access(0), size(size), type(MTP_storage_fileUnknown()) {
 mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size) : prev(0), next(0),
 priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
 dc(dc), locationType(locType),
-id(id), access(access), file(to), duplicateInData(false), size(size), type(MTP_storage_fileUnknown()) {
+id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(false), size(size), type(MTP_storage_fileUnknown()) {
 	LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
 	if (i == queues.cend()) {
 		i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
@@ -68,7 +68,7 @@ id(id), access(access), file(to), duplicateInData(false), size(size), type(MTP_s
 mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata) : prev(0), next(0),
 priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
 dc(dc), locationType(locType),
-id(id), access(access), file(to), duplicateInData(todata), size(size), type(MTP_storage_fileUnknown()) {
+id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(todata), size(size), type(MTP_storage_fileUnknown()) {
 	LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
 	if (i == queues.cend()) {
 		i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
@@ -77,7 +77,7 @@ id(id), access(access), file(to), duplicateInData(todata), size(size), type(MTP_
 }
 
 QString mtpFileLoader::fileName() const {
-	return file.fileName();
+	return fname;
 }
 
 bool mtpFileLoader::done() const {
@@ -99,16 +99,16 @@ float64 mtpFileLoader::currentProgress() const {
 }
 
 int32 mtpFileLoader::currentOffset(bool includeSkipped) const {
-	return (file.isOpen() ? file.size() : data.size()) - (includeSkipped ? 0 : skippedBytes);
+	return (fileIsOpen ? file.size() : data.size()) - (includeSkipped ? 0 : skippedBytes);
 }
 
 int32 mtpFileLoader::fullSize() const {
 	return size;
 }
 
-void mtpFileLoader::setFileName(const QString &fname) {
-	if (duplicateInData && file.fileName().isEmpty()) {
-		file.setFileName(fname);
+void mtpFileLoader::setFileName(const QString &fileName) {
+	if (duplicateInData && fname.isEmpty()) {
+		file.setFileName(fname = fileName);
 	}
 }
 
@@ -132,13 +132,14 @@ void mtpFileLoader::finishFail() {
 	cancelRequests();
 	type = MTP_storage_fileUnknown();
 	complete = true;
-	if (file.isOpen()) {
+	if (fileIsOpen) {
 		file.close();
+		fileIsOpen = false;
 		file.remove();
 	}
 	data = QByteArray();
 	emit failed(this, started);
-	file.setFileName(QString());
+	file.setFileName(fname = QString());
 	loadNext();
 }
 
@@ -198,7 +199,7 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
 	const MTPDupload_file &d(result.c_upload_file());
 	const string &bytes(d.vbytes.c_string().v);
 	if (bytes.size()) {
-		if (file.isOpen()) {
+		if (fileIsOpen) {
 			int64 fsize = file.size();
 			if (offset < fsize) {
 				skippedBytes -= bytes.size();
@@ -230,8 +231,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
 		lastComplete = true;
 	}
 	if (requests.isEmpty() && (lastComplete || (size && nextRequestOffset >= size))) {
-		if (duplicateInData && !file.fileName().isEmpty()) {
-			if (!file.open(QIODevice::WriteOnly)) {
+		if (!fname.isEmpty() && duplicateInData) {
+			if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly);
+			if (!fileIsOpen) {
 				return finishFail();
 			}
 			if (file.write(data) != qint64(data.size())) {
@@ -240,8 +242,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
 		}
 		type = d.vtype;
 		complete = true;
-		if (file.isOpen()) {
+		if (fileIsOpen) {
 			file.close();
+			fileIsOpen = false;
 			psPostprocessFile(QFileInfo(file).absoluteFilePath());
 		}
 		removeFromQueue();
@@ -286,8 +289,9 @@ void mtpFileLoader::pause() {
 void mtpFileLoader::start(bool loadFirst, bool prior) {
 	if (complete) return;
 
-	if (!file.fileName().isEmpty() && !duplicateInData) {
-		if (!file.open(QIODevice::WriteOnly)) {
+	if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) {
+		fileIsOpen = file.open(QIODevice::WriteOnly);
+		if (!fileIsOpen) {
 			finishFail();
 			return;
 		}
@@ -385,8 +389,9 @@ void mtpFileLoader::cancel() {
 	cancelRequests();
 	type = MTP_storage_fileUnknown();
 	complete = true;
-	if (file.isOpen()) {
+	if (fileIsOpen) {
 		file.close();
+		fileIsOpen = false;
 		file.remove();
 	}
 	data = QByteArray();
diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.h b/Telegram/SourceFiles/mtproto/mtpFileLoader.h
index 287fd67f99..99781f6946 100644
--- a/Telegram/SourceFiles/mtproto/mtpFileLoader.h
+++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.h
@@ -89,6 +89,8 @@ private:
 	uint64 id; // for other locations
 	uint64 access;
 	QFile file;
+	QString fname;
+	bool fileIsOpen;
 	bool duplicateInData;
 
 	QByteArray data;
diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp
index 4deb12b3b4..76d1ab4703 100644
--- a/Telegram/SourceFiles/overviewwidget.cpp
+++ b/Telegram/SourceFiles/overviewwidget.cpp
@@ -553,10 +553,14 @@ QPixmap OverviewInner::genPix(PhotoData *photo, int32 size) {
 	if (!photo->full->loaded() && !photo->medium->loaded()) {
 		img = imageBlur(img);
 	}
-	if (img.width() > img.height()) {
-        img = img.scaled(img.width() * size / img.height(), size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
+	if (img.width() == img.height()) {
+		if (img.width() != size) {
+			img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
+		}
+	} else if (img.width() > img.height()) {
+        img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
 	} else {
-        img = img.scaled(size, img.height() * size / img.width(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
+        img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
 	}
 	img.setDevicePixelRatio(cRetinaFactor());
 	photo->forget();
@@ -625,26 +629,13 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
 						it->vsize = _vsize;
 						it->pix = genPix(photo, _vsize);
 					}
-					QPixmap &pix(it->pix);
 					QPoint pos(int32(i * w + st::overviewPhotoSkip), _addToY + row * (_vsize + st::overviewPhotoSkip) + st::overviewPhotoSkip);
-					int32 w = pix.width(), h = pix.height(), size;
-					if (w == h) {
-						p.drawPixmap(pos, pix);
-						size = w;
-					} else if (w > h) {
-						p.drawPixmap(pos, pix, QRect((w - h) / 2, 0, h, h));
-						size = h;
-					} else {
-						p.drawPixmap(pos, pix, QRect(0, (h - w) / 2, w, w));
-						size = w;
-					}
-					size /= cIntRetinaFactor();
-
+					p.drawPixmap(pos, it->pix);
 					if (!quality) {
 						uint64 dt = itemAnimations().animate(item, getms());
 						int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
 
-						int32 x = pos.x() + (size - st::overviewLoader.width()) / 2, y = pos.y() + (size - st::overviewLoader.height()) / 2;
+						int32 x = pos.x() + (_vsize - st::overviewLoader.width()) / 2, y = pos.y() + (_vsize - st::overviewLoader.height()) / 2;
 						p.fillRect(x, y, st::overviewLoader.width(), st::overviewLoader.height(), st::photoLoaderBg->b);
 						x += (st::overviewLoader.width() - cnt * st::overviewLoaderPoint.width() - (cnt - 1) * st::overviewLoaderSkip) / 2;
 						y += (st::overviewLoader.height() - st::overviewLoaderPoint.height()) / 2;
@@ -671,7 +662,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
 						}
 					}
 					if (sel == FullItemSel) {
-						p.fillRect(QRect(pos.x(), pos.y(), size, size), st::msgInSelectOverlay->b);
+						p.fillRect(QRect(pos.x(), pos.y(), _vsize, _vsize), st::msgInSelectOverlay->b);
 					}
 				} break;
 				}
diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp
index 09d14813e1..3da05918ca 100644
--- a/Telegram/SourceFiles/pspecific_linux.cpp
+++ b/Telegram/SourceFiles/pspecific_linux.cpp
@@ -50,7 +50,7 @@ namespace {
 };
 
 PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
-posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) {
+posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")), wndIcon(QPixmap::fromImage(icon256)) {
     connect(&psIdleTimer, SIGNAL(timeout()), this, SLOT(psIdleTimeout()));
     psIdleTimer.setSingleShot(false);
 }
@@ -115,6 +115,8 @@ void PsMainWindow::psUpdateWorkmode() {
 }
 
 void PsMainWindow::psUpdateCounter() {
+	setWindowIcon(myIcon);
+
 	int32 counter = App::histories().unreadFull;
 
     setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h
index 7fba2bb320..7dcd62e2e1 100644
--- a/Telegram/SourceFiles/pspecific_linux.h
+++ b/Telegram/SourceFiles/pspecific_linux.h
@@ -88,6 +88,7 @@ protected:
     QSystemTrayIcon *trayIcon;
     QMenu *trayIconMenu;
     QImage icon256;
+	QIcon wndIcon;
 
 	virtual void setupTrayIcon() = 0;
 	virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp
index b76e618c4d..f9565882cd 100644
--- a/Telegram/SourceFiles/pspecific_mac.cpp
+++ b/Telegram/SourceFiles/pspecific_mac.cpp
@@ -64,7 +64,7 @@ void MacPrivate::notifyReplied(unsigned long long peer, const char *str) {
 }
 
 PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
-posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) {
+posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")), wndIcon(QPixmap(qsl(":/gui/art/iconbig128.png"))) {
 	QImage tray(qsl(":/gui/art/osxtray.png"));
 	trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4));
 	trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4));
@@ -141,6 +141,7 @@ void PsMainWindow::psUpdateWorkmode() {
 		}
 		trayIcon = 0;
 	}
+	setWindowIcon(wndIcon);
 }
 
 void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) {
@@ -182,6 +183,7 @@ void PsMainWindow::psUpdateCounter() {
 	int32 counter = App::histories().unreadFull;
 
     setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
+	setWindowIcon(wndIcon);
 
     QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0'));
     _private.setWindowBadge(counter ? cnt : QString());
diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h
index f057159841..cb8e971a01 100644
--- a/Telegram/SourceFiles/pspecific_mac.h
+++ b/Telegram/SourceFiles/pspecific_mac.h
@@ -99,6 +99,7 @@ protected:
     QSystemTrayIcon *trayIcon;
     QMenu *trayIconMenu;
     QImage icon256;
+	QIcon wndIcon;
 
 	QImage trayImg, trayImgSel;
 
diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp
index 57e6458e8c..0f8fa9ecaf 100644
--- a/Telegram/SourceFiles/pspecific_wnd.cpp
+++ b/Telegram/SourceFiles/pspecific_wnd.cpp
@@ -860,7 +860,7 @@ namespace {
 
 };
 
-PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), ps_hWnd(0), ps_menu(0), icon256(qsl(":/gui/art/iconround256.png")),
+PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), ps_hWnd(0), ps_menu(0), icon256(qsl(":/gui/art/iconround256.png")), wndIcon(QPixmap::fromImage(icon256)),
 	ps_iconBig(0), ps_iconSmall(0), ps_iconOverlay(0), trayIcon(0), trayIconMenu(0), posInited(false), ps_tbHider_hWnd(createTaskbarHider()), psIdle(false) {
 	tbCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated");
 	connect(&psIdleTimer, SIGNAL(timeout()), this, SLOT(psIdleTimeout()));
diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h
index ecfcaf50b6..94b4f4f155 100644
--- a/Telegram/SourceFiles/pspecific_wnd.h
+++ b/Telegram/SourceFiles/pspecific_wnd.h
@@ -87,6 +87,7 @@ protected:
 	QSystemTrayIcon *trayIcon;
 	ContextMenu *trayIconMenu;
 	QImage icon256;
+	QIcon wndIcon;
 
 	virtual void setupTrayIcon() = 0;
 	virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
diff --git a/Telegram/SourceFiles/telegram.qrc b/Telegram/SourceFiles/telegram.qrc
index 93525b0449..8bc2e1f1b2 100644
--- a/Telegram/SourceFiles/telegram.qrc
+++ b/Telegram/SourceFiles/telegram.qrc
@@ -18,6 +18,7 @@
         <file>art/emoji_200x.png</file>
         <file>art/blank.gif</file>
         <file>art/iconround256.png</file>
+		<file>art/iconbig128.png</file>
         <file>art/fonts/DejaVuSans.ttf</file>
 		<file>art/osxtray.png</file>
     </qresource>
diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp
index 9fd6013358..fb78629c08 100644
--- a/Telegram/SourceFiles/window.cpp
+++ b/Telegram/SourceFiles/window.cpp
@@ -337,7 +337,7 @@ NotifyWindow::~NotifyWindow() {
 
 Window::Window(QWidget *parent) : PsMainWindow(parent),
 intro(0), main(0), settings(0), layerBG(0), _topWidget(0),
-_connecting(0), _tempDeleter(0), _tempDeleterThread(0), myIcon(QPixmap::fromImage(icon256)), dragging(false), _inactivePress(false), _mediaView(0) {
+_connecting(0), _tempDeleter(0), _tempDeleterThread(0), dragging(false), _inactivePress(false), _mediaView(0) {
 
 	icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation);
 	icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation);
@@ -380,7 +380,7 @@ void Window::onInactiveTimer() {
 
 void Window::init() {
 	psInitFrameless();
-	setWindowIcon(myIcon);
+	setWindowIcon(wndIcon);
 
 	App::app()->installEventFilter(this);
     connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()));
@@ -875,7 +875,6 @@ void Window::noTopWidget(QWidget *w) {
 void Window::showFromTray(QSystemTrayIcon::ActivationReason reason) {
 	if (reason != QSystemTrayIcon::Context) {
         activate();
-		setWindowIcon(myIcon);
 		psUpdateCounter();
 		if (App::main()) App::main()->setOnline(windowState());
 		QTimer::singleShot(1, this, SLOT(updateTrayMenu()));
diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h
index 18bcb44a62..15b2ea0b56 100644
--- a/Telegram/SourceFiles/window.h
+++ b/Telegram/SourceFiles/window.h
@@ -281,8 +281,6 @@ private:
 
 	void clearWidgets();
 
-	QIcon myIcon;
-
 	bool dragging;
 	QPoint dragStart;
 
diff --git a/Telegram/_qt_5_3_1_patch/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm b/Telegram/_qt_5_3_1_patch/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm
index f3033316f3..159395aa2a 100644
--- a/Telegram/_qt_5_3_1_patch/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/Telegram/_qt_5_3_1_patch/qtbase/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -167,7 +167,7 @@ static bool isMouseEvent(NSEvent *ev)
     if (!self.window.delegate)
         return; // Already detached, pending NSAppKitDefined event
 
-    if (pw && pw->frameStrutEventsEnabled() && pw->m_synchedWindowState != Qt::WindowMinimized && isMouseEvent(theEvent)) {
+    if (pw && pw->frameStrutEventsEnabled() && pw->m_synchedWindowState != Qt::WindowMinimized && pw->m_isExposed && isMouseEvent(theEvent)) {
         NSPoint loc = [theEvent locationInWindow];
         NSRect windowFrame = [self.window legacyConvertRectFromScreen:[self.window frame]];
         NSRect contentFrame = [[self.window contentView] frame];
@@ -903,6 +903,14 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
     [m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""];
 }
 
+qreal _win_devicePixelRatio() {
+	qreal result = 1.0;
+	foreach (QScreen *screen, QGuiApplication::screens()) {
+		result = qMax(result, screen->devicePixelRatio());
+	}
+	return result;
+}
+
 void QCocoaWindow::setWindowIcon(const QIcon &icon)
 {
     QCocoaAutoReleasePool pool;
@@ -918,7 +926,8 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
     if (icon.isNull()) {
         [iconButton setImage:nil];
     } else {
-        QPixmap pixmap = icon.pixmap(QSize(22, 22));
+		CGFloat hgt = 16. * _win_devicePixelRatio();
+        QPixmap pixmap = icon.pixmap(QSize(hgt, hgt));
         NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
         [iconButton setImage:image];
         [image release];

From 4ee33d3bd92134e3480fbb8799e57d05e263e367 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 13 Nov 2014 21:26:39 +0300
Subject: [PATCH 2/2] added os x window icon and qt os x build fix to git

---
 Telegram/SourceFiles/art/iconbig128.png       | Bin 0 -> 7007 bytes
 .../mediaplayer/avfmediaplayersession.mm      | 851 ++++++++++++++++++
 2 files changed, 851 insertions(+)
 create mode 100644 Telegram/SourceFiles/art/iconbig128.png
 create mode 100644 Telegram/_qt_5_3_1_patch/qtmultimedia/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm

diff --git a/Telegram/SourceFiles/art/iconbig128.png b/Telegram/SourceFiles/art/iconbig128.png
new file mode 100644
index 0000000000000000000000000000000000000000..32b44a5eaee7a8f74a4d85568c086c33a1e884fe
GIT binary patch
literal 7007
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
F`X6{u<TL;P

literal 0
HcmV?d00001

diff --git a/Telegram/_qt_5_3_1_patch/qtmultimedia/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/Telegram/_qt_5_3_1_patch/qtmultimedia/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
new file mode 100644
index 0000000000..1b7b863f99
--- /dev/null
+++ b/Telegram/_qt_5_3_1_patch/qtmultimedia/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
@@ -0,0 +1,851 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "avfmediaplayersession.h"
+#include "avfmediaplayerservice.h"
+#include "avfvideooutput.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_USE_NAMESPACE
+
+//AVAsset Keys
+static NSString* const AVF_TRACKS_KEY       = @"tracks";
+static NSString* const AVF_PLAYABLE_KEY     = @"playable";
+
+//AVPlayerItem keys
+static NSString* const AVF_STATUS_KEY       = @"status";
+
+//AVPlayer keys
+static NSString* const AVF_RATE_KEY         = @"rate";
+static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem";
+
+static void *AVFMediaPlayerSessionObserverRateObservationContext = &AVFMediaPlayerSessionObserverRateObservationContext;
+static void *AVFMediaPlayerSessionObserverStatusObservationContext = &AVFMediaPlayerSessionObserverStatusObservationContext;
+static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMediaPlayerSessionObserverCurrentItemObservationContext;
+
+@interface AVFMediaPlayerSessionObserver : NSObject
+{
+@private
+    AVFMediaPlayerSession *m_session;
+    AVPlayer *m_player;
+    AVPlayerItem *m_playerItem;
+    AVPlayerLayer *m_playerLayer;
+    NSURL *m_URL;
+    bool m_audioAvailable;
+    bool m_videoAvailable;
+}
+
+@property (readonly, getter=player) AVPlayer* m_player;
+@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
+@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
+@property (readonly, getter=audioAvailable) bool m_audioAvailable;
+@property (readonly, getter=videoAvailable) bool m_videoAvailable;
+@property (readonly, getter=session) AVFMediaPlayerSession* m_session;
+
+- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session;
+- (void) setURL:(NSURL *)url;
+- (void) unloadMedia;
+- (void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
+- (void) assetFailedToPrepareForPlayback:(NSError *)error;
+- (void) playerItemDidReachEnd:(NSNotification *)notification;
+- (void) playerItemTimeJumped:(NSNotification *)notification;
+- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
+                         change:(NSDictionary *)change context:(void *)context;
+- (void) detatchSession;
+- (void) dealloc;
+@end
+
+@implementation AVFMediaPlayerSessionObserver
+
+@synthesize m_player, m_playerItem, m_playerLayer, m_audioAvailable, m_videoAvailable, m_session;
+
+- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session
+{
+    if (!(self = [super init]))
+        return nil;
+
+    self->m_session = session;
+    return self;
+}
+
+- (void) setURL:(NSURL *)url
+{
+    if (m_URL != url)
+    {
+        [m_URL release];
+        m_URL = [url copy];
+
+        //Create an asset for inspection of a resource referenced by a given URL.
+        //Load the values for the asset keys "tracks", "playable".
+
+        AVURLAsset *asset = [AVURLAsset URLAssetWithURL:m_URL options:nil];
+        NSArray *requestedKeys = [NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil];
+
+        // Tells the asset to load the values of any of the specified keys that are not already loaded.
+        [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
+         ^{
+             dispatch_async( dispatch_get_main_queue(),
+                           ^{
+                                [self prepareToPlayAsset:asset withKeys:requestedKeys];
+                            });
+         }];
+    }
+}
+
+- (void) unloadMedia
+{
+    if (m_player)
+        [m_player setRate:0.0];
+    if (m_playerItem) {
+        [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
+
+        [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:AVPlayerItemDidPlayToEndTimeNotification
+                                                  object:m_playerItem];
+        [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:AVPlayerItemTimeJumpedNotification
+                                                    object:m_playerItem];
+        m_playerItem = 0;
+    }
+}
+
+- (void) prepareToPlayAsset:(AVURLAsset *)asset
+                   withKeys:(NSArray *)requestedKeys
+{
+    //Make sure that the value of each key has loaded successfully.
+    for (NSString *thisKey in requestedKeys)
+    {
+        NSError *error = nil;
+        AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
+#ifdef QT_DEBUG_AVF
+        qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus;
+#endif
+        if (keyStatus == AVKeyValueStatusFailed)
+        {
+            [self assetFailedToPrepareForPlayback:error];
+            return;
+        }
+    }
+
+    //Use the AVAsset playable property to detect whether the asset can be played.
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable];
+#endif
+    if (!asset.playable)
+    {
+        //Generate an error describing the failure.
+        NSString *localizedDescription = NSLocalizedString(@"Item cannot be played", @"Item cannot be played description");
+        NSString *localizedFailureReason = NSLocalizedString(@"The assets tracks were loaded, but could not be made playable.", @"Item cannot be played failure reason");
+        NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
+                localizedDescription, NSLocalizedDescriptionKey,
+                localizedFailureReason, NSLocalizedFailureReasonErrorKey,
+                nil];
+        NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
+
+        [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
+
+        return;
+    }
+
+    m_audioAvailable = false;
+    m_videoAvailable = false;
+
+    //Check each track of asset for audio and video content
+    NSArray *tracks = [asset tracks];
+    for (AVAssetTrack *track in tracks) {
+        if ([track hasMediaCharacteristic:AVMediaCharacteristicAudible])
+            m_audioAvailable = true;
+        if ([track hasMediaCharacteristic:AVMediaCharacteristicVisual])
+            m_videoAvailable = true;
+    }
+
+    //At this point we're ready to set up for playback of the asset.
+    //Stop observing our prior AVPlayerItem, if we have one.
+    if (m_playerItem)
+    {
+        //Remove existing player item key value observers and notifications.
+        [self unloadMedia];
+    }
+
+    //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset.
+    m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
+
+    //Observe the player item "status" key to determine when it is ready to play.
+    [m_playerItem addObserver:self
+                   forKeyPath:AVF_STATUS_KEY
+                      options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+                      context:AVFMediaPlayerSessionObserverStatusObservationContext];
+
+    //When the player item has played to its end time we'll toggle
+    //the movie controller Pause button to be the Play button
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(playerItemDidReachEnd:)
+                                                 name:AVPlayerItemDidPlayToEndTimeNotification
+                                               object:m_playerItem];
+
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(playerItemTimeJumped:)
+                                                 name:AVPlayerItemTimeJumpedNotification
+                                               object:m_playerItem];
+
+
+    //Clean up old player if we have one
+    if (m_player) {
+        [m_player setRate:0.0];
+        [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
+        [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
+        [m_player release];
+        m_player = 0;
+
+        if (m_playerLayer) {
+            [m_playerLayer release];
+            m_playerLayer = 0; //Will have been released
+        }
+    }
+
+    //Get a new AVPlayer initialized to play the specified player item.
+    m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
+    [m_player retain];
+
+#if defined(Q_OS_OSX)
+    //Set the initial volume on new player object
+    if (self.session)
+        m_player.volume = m_session->volume() / 100.0f;
+#endif
+
+    //Create a new player layer if we don't have one already
+    if (!m_playerLayer)
+    {
+        m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player];
+        [m_playerLayer retain];
+        m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+
+        //Get the native size of the new item, and reset the bounds of the player layer
+        AVAsset *asset = m_playerItem.asset;
+        if (asset) {
+            NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
+            if ([tracks count]) {
+                AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
+                m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
+                m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
+            }
+        }
+
+    }
+
+    //Observe the AVPlayer "currentItem" property to find out when any
+    //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
+    //occur.
+    [m_player addObserver:self
+                          forKeyPath:AVF_CURRENT_ITEM_KEY
+                          options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+                          context:AVFMediaPlayerSessionObserverCurrentItemObservationContext];
+
+    //Observe the AVPlayer "rate" property to update the scrubber control.
+    [m_player addObserver:self
+                          forKeyPath:AVF_RATE_KEY
+                          options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+                          context:AVFMediaPlayerSessionObserverRateObservationContext];
+
+}
+
+-(void) assetFailedToPrepareForPlayback:(NSError *)error
+{
+    Q_UNUSED(error)
+    QMetaObject::invokeMethod(m_session, "processMediaLoadError", Qt::AutoConnection);
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+    qDebug() << [[error localizedDescription] UTF8String];
+    qDebug() << [[error localizedFailureReason] UTF8String];
+    qDebug() << [[error localizedRecoverySuggestion] UTF8String];
+#endif
+}
+
+- (void) playerItemDidReachEnd:(NSNotification *)notification
+{
+    Q_UNUSED(notification)
+    if (self.session)
+        QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection);
+}
+
+- (void) playerItemTimeJumped:(NSNotification *)notification
+{
+    Q_UNUSED(notification)
+    if (self.session)
+        QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection);
+}
+
+- (void) observeValueForKeyPath:(NSString*) path
+                       ofObject:(id)object
+                         change:(NSDictionary*)change
+                        context:(void*)context
+{
+    //AVPlayerItem "status" property value observer.
+    if (context == AVFMediaPlayerSessionObserverStatusObservationContext)
+    {
+		AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
+        switch (status)
+        {
+            //Indicates that the status of the player is not yet known because
+            //it has not tried to load new media resources for playback
+            case AVPlayerStatusUnknown:
+            {
+                //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
+            }
+            break;
+
+            case AVPlayerStatusReadyToPlay:
+            {
+                //Once the AVPlayerItem becomes ready to play, i.e.
+                //[playerItem status] == AVPlayerItemStatusReadyToPlay,
+                //its duration can be fetched from the item.
+                if (self.session)
+                    QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
+            }
+            break;
+
+            case AVPlayerStatusFailed:
+            {
+                AVPlayerItem *playerItem = (AVPlayerItem *)object;
+                [self assetFailedToPrepareForPlayback:playerItem.error];
+
+                if (self.session)
+                    QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
+            }
+            break;
+        }
+    }
+    //AVPlayer "rate" property value observer.
+    else if (context == AVFMediaPlayerSessionObserverRateObservationContext)
+    {
+        //QMetaObject::invokeMethod(m_session, "setPlaybackRate",  Qt::AutoConnection, Q_ARG(qreal, [m_player rate]));
+    }
+    //AVPlayer "currentItem" property observer.
+    //Called when the AVPlayer replaceCurrentItemWithPlayerItem:
+    //replacement will/did occur.
+    else if (context == AVFMediaPlayerSessionObserverCurrentItemObservationContext)
+    {
+        AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
+        if (m_playerItem != newPlayerItem)
+        {
+            m_playerItem = newPlayerItem;
+
+            //Get the native size of the new item, and reset the bounds of the player layer
+            //AVAsset *asset = m_playerItem.asset;
+            AVAsset *asset = [m_playerItem asset];
+            if (asset) {
+                NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
+                if ([tracks count]) {
+                    AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
+                    m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
+                    m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
+                }
+            }
+
+        }
+        if (self.session)
+            QMetaObject::invokeMethod(m_session, "processCurrentItemChanged", Qt::AutoConnection);
+    }
+    else
+    {
+        [super observeValueForKeyPath:path ofObject:object change:change context:context];
+    }
+}
+
+- (void) detatchSession
+{
+#ifdef QT_DEBUG_AVF
+        qDebug() << Q_FUNC_INFO;
+#endif
+        m_session = 0;
+}
+
+- (void) dealloc
+{
+#ifdef QT_DEBUG_AVF
+        qDebug() << Q_FUNC_INFO;
+#endif
+    [self unloadMedia];
+
+    if (m_player) {
+        [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
+        [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
+        [m_player release];
+        m_player = 0;
+    }
+
+    if (m_playerLayer) {
+        [m_playerLayer release];
+        m_playerLayer = 0;
+    }
+
+    if (m_URL) {
+        [m_URL release];
+    }
+
+    [super dealloc];
+}
+
+@end
+
+AVFMediaPlayerSession::AVFMediaPlayerSession(AVFMediaPlayerService *service, QObject *parent)
+    : QObject(parent)
+    , m_service(service)
+    , m_videoOutput(0)
+    , m_state(QMediaPlayer::StoppedState)
+    , m_mediaStatus(QMediaPlayer::NoMedia)
+    , m_mediaStream(0)
+    , m_muted(false)
+    , m_tryingAsync(false)
+    , m_volume(100)
+    , m_rate(1.0)
+    , m_duration(0)
+    , m_videoAvailable(false)
+    , m_audioAvailable(false)
+{
+    m_observer = [[AVFMediaPlayerSessionObserver alloc] initWithMediaPlayerSession:this];
+}
+
+AVFMediaPlayerSession::~AVFMediaPlayerSession()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+    //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session).
+    [(AVFMediaPlayerSessionObserver*)m_observer detatchSession];
+    [(AVFMediaPlayerSessionObserver*)m_observer release];
+}
+
+void AVFMediaPlayerSession::setVideoOutput(AVFVideoOutput *output)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << output;
+#endif
+
+    if (m_videoOutput == output)
+        return;
+
+    //Set the current output layer to null to stop rendering
+    if (m_videoOutput) {
+        m_videoOutput->setLayer(0);
+    }
+
+    m_videoOutput = output;
+
+    if (m_videoOutput && m_state != QMediaPlayer::StoppedState)
+        m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]);
+}
+
+void *AVFMediaPlayerSession::currentAssetHandle()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+    AVAsset *currentAsset = [[(AVFMediaPlayerSessionObserver*)m_observer playerItem] asset];
+    return currentAsset;
+}
+
+QMediaPlayer::State AVFMediaPlayerSession::state() const
+{
+    return m_state;
+}
+
+QMediaPlayer::MediaStatus AVFMediaPlayerSession::mediaStatus() const
+{
+    return m_mediaStatus;
+}
+
+QMediaContent AVFMediaPlayerSession::media() const
+{
+    return m_resources;
+}
+
+const QIODevice *AVFMediaPlayerSession::mediaStream() const
+{
+    return m_mediaStream;
+}
+
+void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *stream)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << content.canonicalUrl();
+#endif
+
+    m_resources = content;
+    m_mediaStream = stream;
+
+    QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
+
+    if (content.isNull() || content.canonicalUrl().isEmpty()) {
+        [(AVFMediaPlayerSessionObserver*)m_observer unloadMedia];
+        m_mediaStatus = QMediaPlayer::NoMedia;
+        if (m_state != QMediaPlayer::StoppedState)
+            Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
+
+        if (m_mediaStatus != oldMediaStatus)
+            Q_EMIT mediaStatusChanged(m_mediaStatus);
+        Q_EMIT positionChanged(position());
+        return;
+    } else {
+
+        m_mediaStatus = QMediaPlayer::LoadingMedia;
+        if (m_mediaStatus != oldMediaStatus)
+            Q_EMIT mediaStatusChanged(m_mediaStatus);
+    }
+    //Load AVURLAsset
+    //initialize asset using content's URL
+    NSString *urlString = [NSString stringWithUTF8String:content.canonicalUrl().toEncoded().constData()];
+    NSURL *url = [NSURL URLWithString:urlString];
+    [(AVFMediaPlayerSessionObserver*)m_observer setURL:url];
+}
+
+qint64 AVFMediaPlayerSession::position() const
+{
+    AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
+
+    if (!playerItem)
+        return 0;
+
+    CMTime time = [playerItem currentTime];
+    return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
+}
+
+qint64 AVFMediaPlayerSession::duration() const
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+    AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
+
+    if (!playerItem)
+        return 0;
+
+    CMTime time = [playerItem duration];
+    return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
+}
+
+int AVFMediaPlayerSession::bufferStatus() const
+{
+    //BUG: bufferStatus may be relevant?
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+    return 100;
+}
+
+int AVFMediaPlayerSession::volume() const
+{
+    return m_volume;
+}
+
+bool AVFMediaPlayerSession::isMuted() const
+{
+    return m_muted;
+}
+
+bool AVFMediaPlayerSession::isAudioAvailable() const
+{
+    return [(AVFMediaPlayerSessionObserver*)m_observer audioAvailable];
+}
+
+bool AVFMediaPlayerSession::isVideoAvailable() const
+{
+    return [(AVFMediaPlayerSessionObserver*)m_observer videoAvailable];
+}
+
+bool AVFMediaPlayerSession::isSeekable() const
+{
+    return true;
+}
+
+QMediaTimeRange AVFMediaPlayerSession::availablePlaybackRanges() const
+{
+    AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
+
+    if (playerItem) {
+        QMediaTimeRange timeRanges;
+
+        NSArray *ranges = [playerItem loadedTimeRanges];
+        for (NSValue *timeRange in ranges) {
+            CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
+            qint64 startTime = qint64(float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
+            timeRanges.addInterval(startTime, startTime + qint64(float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
+        }
+        if (!timeRanges.isEmpty())
+            return timeRanges;
+    }
+    return QMediaTimeRange(0, duration());
+}
+
+qreal AVFMediaPlayerSession::playbackRate() const
+{
+    return m_rate;
+}
+
+void AVFMediaPlayerSession::setPlaybackRate(qreal rate)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << rate;
+#endif
+
+    if (qFuzzyCompare(m_rate, rate))
+        return;
+
+    m_rate = rate;
+
+    AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
+
+    if (player != 0 && m_state == QMediaPlayer::PlayingState) {
+        [player setRate:m_rate];
+    }
+}
+
+void AVFMediaPlayerSession::setPosition(qint64 pos)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << pos;
+#endif
+
+    if ( !isSeekable() || pos == position())
+        return;
+
+    AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
+
+    if (!playerItem)
+        return;
+
+    if (duration() > 0)
+        pos = qMin(pos, duration());
+
+    CMTime newTime = [playerItem currentTime];
+    newTime.value = (pos / 1000.0f) * newTime.timescale;
+    [playerItem seekToTime:newTime];
+
+    //reset the EndOfMedia status position is changed after playback is finished
+    if (m_mediaStatus == QMediaPlayer::EndOfMedia)
+        processLoadStateChange();
+}
+
+void AVFMediaPlayerSession::play()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+    if (m_state == QMediaPlayer::PlayingState)
+        return;
+
+    m_state = QMediaPlayer::PlayingState;
+
+    if (m_videoOutput) {
+        m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]);
+    }
+
+    //reset the EndOfMedia status if the same file is played again
+    if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
+        setPosition(0);
+        processLoadStateChange();
+    }
+
+    if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia)
+        [[(AVFMediaPlayerSessionObserver*)m_observer player] play];
+
+    //processLoadStateChange();
+    Q_EMIT stateChanged(m_state);
+}
+
+void AVFMediaPlayerSession::pause()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+    if (m_state == QMediaPlayer::PausedState)
+        return;
+
+    m_state = QMediaPlayer::PausedState;
+
+    if (m_videoOutput) {
+        m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]);
+    }
+
+    //reset the EndOfMedia status if the same file is played again
+    if (m_mediaStatus == QMediaPlayer::EndOfMedia)
+        processLoadStateChange();
+
+    [[(AVFMediaPlayerSessionObserver*)m_observer player] pause];
+
+    //processLoadStateChange();
+    Q_EMIT stateChanged(m_state);
+}
+
+void AVFMediaPlayerSession::stop()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+    if (m_state == QMediaPlayer::StoppedState)
+        return;
+
+    m_state = QMediaPlayer::StoppedState;
+    m_rate = 0.0f;
+    [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate];
+    setPosition(0);
+
+    if (m_videoOutput) {
+        m_videoOutput->setLayer(0);
+    }
+
+    processLoadStateChange();
+    Q_EMIT stateChanged(m_state);
+    Q_EMIT positionChanged(position());
+}
+
+void AVFMediaPlayerSession::setVolume(int volume)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << volume;
+#endif
+
+    if (m_volume == volume)
+        return;
+
+    m_volume = volume;
+
+#if defined(Q_OS_OSX)
+    AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
+    if (player) {
+        [[(AVFMediaPlayerSessionObserver*)m_observer player] setVolume:m_volume / 100.0f];
+    }
+#endif
+
+    Q_EMIT volumeChanged(m_volume);
+}
+
+void AVFMediaPlayerSession::setMuted(bool muted)
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << muted;
+#endif
+    if (m_muted == muted)
+        return;
+
+    m_muted = muted;
+#if defined(Q_OS_OSX)
+    [[(AVFMediaPlayerSessionObserver*)m_observer player] setMuted:m_muted];
+#endif
+    Q_EMIT mutedChanged(muted);
+}
+
+void AVFMediaPlayerSession::processEOS()
+{
+    //AVPlayerItem has reached end of track/stream
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+    Q_EMIT positionChanged(position());
+    m_mediaStatus = QMediaPlayer::EndOfMedia;
+
+    Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
+    Q_EMIT mediaStatusChanged(m_mediaStatus);
+}
+
+void AVFMediaPlayerSession::processLoadStateChange()
+{
+    AVPlayerStatus currentStatus = [[(AVFMediaPlayerSessionObserver*)m_observer player] status];
+
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO << currentStatus;
+#endif
+
+    QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia;
+    bool isPlaying = (m_state != QMediaPlayer::StoppedState);
+
+    if (currentStatus == AVPlayerStatusReadyToPlay) {
+        qint64 currentDuration = duration();
+        if (m_duration != currentDuration)
+            Q_EMIT durationChanged(m_duration = currentDuration);
+
+        if (m_audioAvailable != isAudioAvailable())
+            Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable);
+
+        if (m_videoAvailable != isVideoAvailable())
+            Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable);
+
+        newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia;
+
+        if (m_state == QMediaPlayer::PlayingState && [(AVFMediaPlayerSessionObserver*)m_observer player]) {
+            [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate];
+            [[(AVFMediaPlayerSessionObserver*)m_observer player] play];
+        }
+    }
+
+    if (newStatus != m_mediaStatus)
+        Q_EMIT mediaStatusChanged(m_mediaStatus = newStatus);
+}
+
+void AVFMediaPlayerSession::processPositionChange()
+{
+    Q_EMIT positionChanged(position());
+}
+
+void AVFMediaPlayerSession::processMediaLoadError()
+{
+    Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
+    Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia);
+    Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
+}
+
+void AVFMediaPlayerSession::processCurrentItemChanged()
+{
+#ifdef QT_DEBUG_AVF
+    qDebug() << Q_FUNC_INFO;
+#endif
+
+    AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
+
+    if (m_videoOutput && m_state != QMediaPlayer::StoppedState) {
+        m_videoOutput->setLayer(playerLayer);
+    }
+
+}