diff -Nru sview-13.10/Makefile sview-14.01/Makefile --- sview-13.10/Makefile 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/Makefile 2014-01-30 10:45:04.000000000 +0000 @@ -72,9 +72,10 @@ rm -f $(DESTDIR)/usr/$(USR_LIB)/sView/*.a pre_all: - mkdir -p $(BUILD_ROOT)/lang/english + mkdir -p $(BUILD_ROOT)/lang/English mkdir -p $(BUILD_ROOT)/lang/русский mkdir -p $(BUILD_ROOT)/lang/français + mkdir -p $(BUILD_ROOT)/lang/Deutsch mkdir -p $(BUILD_ROOT)/textures mkdir -p $(BUILD_ROOT)/web cp -f -r textures/*.png $(BUILD_ROOT)/textures/ @@ -131,9 +132,10 @@ pre_StOutAnaglyph: mkdir -p $(BUILD_ROOT)/shaders/StOutAnaglyph/ cp -f -r StOutAnaglyph/shaders/* $(BUILD_ROOT)/shaders/StOutAnaglyph/ - cp -f -r StOutAnaglyph/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutAnaglyph/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutAnaglyph/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutAnaglyph/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutAnaglyph/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutAnaglyph: rm -f $(aStOutAnaglyph) rm -rf StOutAnaglyph/*.o @@ -145,9 +147,10 @@ $(aStOutDual) : pre_StOutDual $(aStOutDual_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStOutDual_OBJS) $(aStOutDual_LIB) -o $(aStOutDual) pre_StOutDual: - cp -f -r StOutDual/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutDual/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutDual/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutDual/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutDual/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutDual: rm -f $(aStOutDual) rm -rf StOutDual/*.o @@ -161,9 +164,10 @@ pre_StOutIZ3D: mkdir -p $(BUILD_ROOT)/shaders/StOutIZ3D/ cp -f -r StOutIZ3D/shaders/* $(BUILD_ROOT)/shaders/StOutIZ3D/ - cp -f -r StOutIZ3D/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutIZ3D/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutIZ3D/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutIZ3D/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutIZ3D/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutIZ3D: rm -f $(aStOutIZ3D) rm -rf StOutIZ3D/*.o @@ -177,9 +181,10 @@ pre_StOutInterlace: mkdir -p $(BUILD_ROOT)/shaders/StOutInterlace/ cp -f -r StOutInterlace/shaders/* $(BUILD_ROOT)/shaders/StOutInterlace/ - cp -f -r StOutInterlace/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutInterlace/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutInterlace/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutInterlace/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutInterlace/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutInterlace: rm -f $(aStOutInterlace) rm -rf StOutInterlace/*.o @@ -191,9 +196,10 @@ $(aStOutPageFlip) : pre_StOutPageFlip $(aStOutPageFlip_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStOutPageFlip_OBJS) $(aStOutPageFlip_LIB) -o $(aStOutPageFlip) pre_StOutPageFlip: - cp -f -r StOutPageFlip/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutPageFlip/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutPageFlip/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutPageFlip/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutPageFlip/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutPageFlip: rm -f $(aStOutPageFlip) rm -rf StOutPageFlip/*.o @@ -205,9 +211,10 @@ $(aStOutDistorted) : pre_StOutDistorted $(aStOutDistorted_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStOutDistorted_OBJS) $(aStOutDistorted_LIB) -o $(aStOutDistorted) pre_StOutDistorted: - cp -f -r StOutDistorted/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StOutDistorted/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StOutDistorted/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StOutDistorted/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StOutDistorted/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StOutDistorted: rm -f $(aStOutDistorted) rm -rf StOutDistorted/*.o @@ -219,9 +226,10 @@ $(aStImageViewer) : pre_StImageViewer $(aStImageViewer_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStImageViewer_OBJS) $(aStImageViewer_LIB) -o $(aStImageViewer) pre_StImageViewer: - cp -f -r StImageViewer/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StImageViewer/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StImageViewer/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StImageViewer/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StImageViewer/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StImageViewer: rm -f $(aStImageViewer) rm -rf StImageViewer/*.o @@ -237,9 +245,10 @@ $(aStMoviePlayer) : pre_StMoviePlayer $(aStMoviePlayer_OBJS1) $(aStMoviePlayer_OBJS2) $(aStMoviePlayer_OBJS3) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStMoviePlayer_OBJS1) $(aStMoviePlayer_OBJS2) $(aStMoviePlayer_OBJS3) $(aStMoviePlayer_LIB) -o $(aStMoviePlayer) pre_StMoviePlayer: - cp -f -r StMoviePlayer/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StMoviePlayer/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StMoviePlayer/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StMoviePlayer/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StMoviePlayer/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ cp -f -r StMoviePlayer/web/* $(BUILD_ROOT)/web/ clean_StMoviePlayer: rm -f $(aStMoviePlayer) @@ -252,9 +261,10 @@ $(aStDiagnostics) : pre_StDiagnostics $(aStDiagnostics_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStDiagnostics_OBJS) $(aStDiagnostics_LIB) -o $(aStDiagnostics) pre_StDiagnostics: - cp -f -r StDiagnostics/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StDiagnostics/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StDiagnostics/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StDiagnostics/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StDiagnostics/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StDiagnostics: rm -f $(aStDiagnostics) rm -rf StDiagnostics/*.o @@ -269,9 +279,10 @@ $(aStCADViewer) : pre_StCADViewer $(aStCADViewer_OBJS) $(LD) -shared -z defs $(LDFLAGS) $(LIBDIR) $(aStCADViewer_OBJS) $(aStCADViewer_LIB) -o $(aStCADViewer) pre_StCADViewer: - cp -f -r StCADViewer/lang/english/* $(BUILD_ROOT)/lang/english/ + cp -f -r StCADViewer/lang/english/* $(BUILD_ROOT)/lang/English/ cp -f -r StCADViewer/lang/russian/* $(BUILD_ROOT)/lang/русский/ cp -f -r StCADViewer/lang/french/* $(BUILD_ROOT)/lang/français/ + cp -f -r StCADViewer/lang/german/* $(BUILD_ROOT)/lang/Deutsch/ clean_StCADViewer: rm -f $(aStCADViewer) rm -rf StCADViewer/*.o diff -Nru sview-13.10/StCADViewer/StCADViewer.cbp sview-14.01/StCADViewer/StCADViewer.cbp --- sview-13.10/StCADViewer/StCADViewer.cbp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/StCADViewer.cbp 2014-01-30 10:45:03.000000000 +0000 @@ -38,12 +38,7 @@ - - - - - - + @@ -78,12 +73,7 @@ - - - - - - + @@ -125,12 +115,7 @@ - - - - - - + @@ -166,12 +151,7 @@ - - - - - - + @@ -201,12 +181,7 @@ - - - - - - + @@ -235,12 +210,7 @@ - - - - - - + @@ -266,12 +236,7 @@ - - - - - - + @@ -299,12 +264,7 @@ - - - - - - + @@ -373,6 +333,9 @@ + + diff -Nru sview-13.10/StCADViewer/StCADViewer.rc sview-14.01/StCADViewer/StCADViewer.rc --- sview-13.10/StCADViewer/StCADViewer.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/StCADViewer.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "Stereoscopic CAD Viewer\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2011-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2011-2014 Kirill Gavrilov\000" VALUE "ProductName", "StCADViewer\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StCADViewer/StCADViewerGUI.cpp sview-14.01/StCADViewer/StCADViewerGUI.cpp --- sview-13.10/StCADViewer/StCADViewerGUI.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/StCADViewerGUI.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -207,8 +207,8 @@ const StString& aVerString = myLangMap->changeValueId(ABOUT_VERSION, "version"); const StString& aDescr = myLangMap->changeValueId(ABOUT_DESCRIPTION, "CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OCCT.\n" - "(C) 2011-2013 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru"); - StGLMessageBox* anAboutDialog = new StGLMessageBox(this, aTitle + '\n' + "(C) 2011-2014 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru"); + StGLMessageBox* anAboutDialog = new StGLMessageBox(this, "", aTitle + '\n' + aVerString + ": " + StVersionInfo::getSDKVersionString() + "\n \n" + aDescr, 512, 300); anAboutDialog->addButton("Close"); diff -Nru sview-13.10/StCADViewer/lang/english/StCADViewer.lng sview-14.01/StCADViewer/lang/english/StCADViewer.lng --- sview-13.10/StCADViewer/lang/english/StCADViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/lang/english/StCADViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -23,4 +23,4 @@ 1504="Language" 3000="sView - Tiny CAD Viewer" 3001="version" -3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2013 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" +3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2014 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" diff -Nru sview-13.10/StCADViewer/lang/french/StCADViewer.lng sview-14.01/StCADViewer/lang/french/StCADViewer.lng --- sview-13.10/StCADViewer/lang/french/StCADViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/lang/french/StCADViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -23,4 +23,4 @@ 1504="Language" 3000="sView - Tiny CAD Viewer" 3001="version" -3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2013 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" +3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2014 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" diff -Nru sview-13.10/StCADViewer/lang/german/StCADViewer.lng sview-14.01/StCADViewer/lang/german/StCADViewer.lng --- sview-13.10/StCADViewer/lang/german/StCADViewer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StCADViewer/lang/german/StCADViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,26 @@ +German translation file for StCADViewer +@author Kirill Gavrilov + +-------- +1200="Ansicht" +1201="Projection type" +1202="Vollbild" +1203="Normalen anzeigen" +1204="Dreibein anzeigen" +1205="Zwei einseitige Beleuchtung" +1206="Projektion" +1207="Füllmodus" +1208="Fit ALL" +1240="Orthogonal" +1241="Perspektive" +1242="Stereo" +1250="Mesh" +1251="Beschattet" +1252="Beschattet + Mesh" +1500="Hilfe" +1501="Über..." +1503="Lizenztext" +1504="Language" +3000="sView - winzig CAD Viewer" +3001="Version" +3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2014 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" diff -Nru sview-13.10/StCADViewer/lang/korean/StCADViewer.lng sview-14.01/StCADViewer/lang/korean/StCADViewer.lng --- sview-13.10/StCADViewer/lang/korean/StCADViewer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StCADViewer/lang/korean/StCADViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,26 @@ +English translation file for StCADViewer +@author Kirill Gavrilov + +-------- +1200="View" +1201="Projection type" +1202="Fullscreen" +1203="Show normals" +1204="Show trihedron" +1205="Two sides lighting" +1206="Projection" +1207="Fill Mode" +1208="Fit ALL" +1240="Orthogonal" +1241="Perspective" +1242="Stereo" +1250="Mesh" +1251="Shaded" +1252="Shaded + Mesh" +1500="Help" +1501="About..." +1503="License text" +1504="Language" +3000="sView - Tiny CAD Viewer" +3001="version" +3002="CAD viewer allows you to view CAD files in formats IGES, STEP, BREP using OpenCASCADE Technology.\n © 2011-2014 Kirill Gavrilov (kirill@sview.ru).\nOfficial site: www.sview.ru" diff -Nru sview-13.10/StCADViewer/lang/russian/StCADViewer.lng sview-14.01/StCADViewer/lang/russian/StCADViewer.lng --- sview-13.10/StCADViewer/lang/russian/StCADViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCADViewer/lang/russian/StCADViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -23,4 +23,4 @@ 1504="Language" 3000="sView - приложение для просмотра CAD моделей" 3001="версия" -3002="Программа открывает CAD модели в форматах IGES, STEP, BREP с помощью OpenCASCADE Technology.\n © 2011-2013 Гаврилов Кирилл (kirill@sview.ru).\nОфициальный сайт: www.sview.ru" +3002="Программа открывает CAD модели в форматах IGES, STEP, BREP с помощью OpenCASCADE Technology.\n © 2011-2014 Гаврилов Кирилл (kirill@sview.ru).\nОфициальный сайт: www.sview.ru" diff -Nru sview-13.10/StCore/StCore.rc sview-14.01/StCore/StCore.rc --- sview-13.10/StCore/StCore.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StCore.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "sView core library\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2007-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2007-2014 Kirill Gavrilov\000" VALUE "ProductName", "StCore\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StCore/StSearchMonitors.cpp sview-14.01/StCore/StSearchMonitors.cpp --- sview-13.10/StCore/StSearchMonitors.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StSearchMonitors.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -587,10 +587,8 @@ void StSearchMonitors::initGlobal() { clear(); + initFromSystem(); initFromConfig(); - if(isEmpty()) { - initFromSystem(); - } } void StSearchMonitors::init(const bool theForced) { @@ -607,46 +605,70 @@ } } -void StSearchMonitors::initFromConfig() { - clear(); - const StString ST_GLOBAL_SETTINGS_GROUP("sview"); - const StString ST_GLOBAL_SETTINGS_MON_MASTER("monMaster"); - const StString ST_GLOBAL_SETTINGS_MON_SLAVE("monSlave"); - - StSearchMonitors sysMons; - sysMons.initFromSystem(); - if(sysMons.isEmpty()) { - return; +static void readMonitor(const StString& thePrefix, + StSettings& theConfig, + StMonitor& theMon) { + // read/override monitor properties + const StString aRectKey = thePrefix + stCString(".rect"); + const StString aScaleKey = thePrefix + stCString(".scale"); + theConfig.loadInt32Rect(aRectKey, theMon.changeVRect()); + + float aScale = 1.0f; + if(theConfig.loadFloat(aScaleKey, aScale)) { + theMon.setScale(aScale); } +} - StMonitor aMonMaster; - StMonitor aMonSlave; +void StSearchMonitors::initFromConfig() { + const StString ST_GLOBAL_SETTINGS_GROUP("sview"); + const StString ST_GLOBAL_SETTINGS_MONITORS("monitors"); StSettings aGlobalSettings(ST_GLOBAL_SETTINGS_GROUP); - aGlobalSettings.loadInt32Rect(ST_GLOBAL_SETTINGS_MON_MASTER, aMonMaster.changeVRect()); - aGlobalSettings.loadInt32Rect(ST_GLOBAL_SETTINGS_MON_SLAVE, aMonSlave.changeVRect()); + StMonitor aMonDummy; + for(size_t aParamIter = 0; aParamIter < 256; ++aParamIter) { + const StString aPrefix = ST_GLOBAL_SETTINGS_MONITORS + stCString(".") + aParamIter; + const StString anActiveKey = aPrefix + stCString(".active"); + bool isActive = false; + aGlobalSettings.loadBool(anActiveKey, isActive); + if(!isActive) { + break; + } - // save settings (to simple change them) - aGlobalSettings.saveInt32Rect(ST_GLOBAL_SETTINGS_MON_MASTER, aMonMaster.changeVRect()); - aGlobalSettings.saveInt32Rect(ST_GLOBAL_SETTINGS_MON_SLAVE, aMonSlave.changeVRect()); - - if(aMonMaster.isValid()) { - StRectI_t aRectCopy = aMonMaster.getVRect(); - // copy setting from some real display - aMonMaster = sysMons[aRectCopy.center()]; - aMonMaster.setVRect(aRectCopy); - aMonMaster.setId(0); - aMonMaster.setName("StMasterDisplay"); - add(aMonMaster); - if(aMonSlave.isValid()) { - aRectCopy = aMonSlave.getVRect(); - aMonSlave = sysMons[aRectCopy.center()]; - aMonSlave.setVRect(aRectCopy); - aMonSlave.setId(1); - aMonSlave.setName("StSlaveDisplay"); - add(aMonSlave); + // read monitor(s) identifier + const StString anIdKey = aPrefix + stCString(".id"); + const StString aPnpIdKey = aPrefix + stCString(".pnpid"); + int32_t aMonId = -1; + StString aPnpId; + aGlobalSettings.loadInt32 (anIdKey, aMonId); + aGlobalSettings.loadString(aPnpIdKey, aPnpId); + if(aPnpId.getLength() == 7) { + for(size_t aMonIter = 0; aMonIter < size(); ++aMonIter) { + StMonitor& aMon = changeValue(aMonIter); + if(aMon.getPnPId() == aPnpId) { + readMonitor(aPrefix, aGlobalSettings, aMon); + } + } + } else if(aMonId >= 0) { + for(int aMonAddIter = (int )size(); aMonAddIter <= aMonId; ++aMonAddIter) { + aMonDummy.setId(aMonAddIter); + add(aMonDummy); + } + readMonitor(aPrefix, aGlobalSettings, changeValue((size_t )aMonId)); } } + + // save sample configuration to simplify manual edition + const StString anActiveKey = ST_GLOBAL_SETTINGS_MONITORS + stCString(".999.active"); + bool isActive = false; + if(aGlobalSettings.loadBool(anActiveKey, isActive)) { + return; + } + + aGlobalSettings.saveBool (anActiveKey, false); + aGlobalSettings.saveInt32 (ST_GLOBAL_SETTINGS_MONITORS + stCString(".999.id"), 999); + aGlobalSettings.saveString (ST_GLOBAL_SETTINGS_MONITORS + stCString(".999.pnpid"), "PNP0000"); + aGlobalSettings.saveInt32Rect(ST_GLOBAL_SETTINGS_MONITORS + stCString(".999.rect"), StRect(0, 1080, 0, 1920)); + aGlobalSettings.saveFloat (ST_GLOBAL_SETTINGS_MONITORS + stCString(".999.scale"), 1.2f); } void StSearchMonitors::initFromSystem() { diff -Nru sview-13.10/StCore/StWindow.cpp sview-14.01/StCore/StWindow.cpp --- sview-13.10/StCore/StWindow.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindow.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -43,6 +43,7 @@ StWindow::StWindow() : myWin(new StWindowImpl((StNativeWin_t )NULL)), myTargetFps(0.0), + myWasUsed(false), myIsForcedStereo(false) { copySignals(); } @@ -50,6 +51,7 @@ StWindow::StWindow(const StNativeWin_t theParentWindow) : myWin(new StWindowImpl(theParentWindow)), myTargetFps(0.0), + myWasUsed(false), myIsForcedStereo(false) { copySignals(); } @@ -180,6 +182,7 @@ } bool StWindow::create() { + myWasUsed = true; return myWin->create(); } @@ -273,3 +276,7 @@ GLfloat StWindow::getLensDist() const { return 0.0f; } + +void StWindow::toClipboard(const StString& theText) { + myWin->toClipboard(theText); +} diff -Nru sview-13.10/StCore/StWindowImpl.cpp sview-14.01/StCore/StWindowImpl.cpp --- sview-13.10/StCore/StWindowImpl.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindowImpl.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -59,6 +59,7 @@ myMonMasterFull(-1), mySyncCounter(0), myWinOnMonitorId(0), + myWinMonScaleId(0), myTiledCfg(TiledCfg_Separate), #ifdef _WIN32 myEventInitWin(false), @@ -94,6 +95,7 @@ attribs.Slave = StWinSlave_slaveOff; attribs.SlaveMonId = 1; attribs.Split = StWinSlave_splitOff; + attribs.ToAlignEven = false; myMonSlave.idMaster = 0; myMonSlave.idSlave = 1; // second by default @@ -340,6 +342,9 @@ case StWinAttr_SplitCfg: anIter[1] = (StWinAttr )attribs.Split; break; + case StWinAttr_ToAlignEven: + anIter[1] = (StWinAttr )attribs.ToAlignEven; + break; default: ST_DEBUG_LOG("UNKNOWN window attribute #" + anIter[0] + " requested"); break; @@ -406,6 +411,9 @@ } attribs.Split = (StWinSplit )anIter[1]; break; + case StWinAttr_ToAlignEven: + attribs.ToAlignEven = (anIter[1] == 1); + break; default: ST_DEBUG_LOG("UNKNOWN window attribute #" + anIter[0] + " requested"); break; diff -Nru sview-13.10/StCore/StWindowImpl.h sview-14.01/StCore/StWindowImpl.h --- sview-13.10/StCore/StWindowImpl.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindowImpl.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -33,7 +33,12 @@ #include #endif -class NSOpenGLContext; +#ifdef __OBJC__ + @class NSOpenGLContext; +#else + class NSOpenGLContext; +#endif + class StGLContext; class StThread; @@ -79,6 +84,14 @@ ST_LOCAL bool isParentOnScreen() const; + public: //! @name clipboard + + ST_LOCAL bool toClipboard(const StString& theText); + +#if defined(__linux__) + StString myTextToCopy; +#endif + public: //! @name additional ST_LOCAL void updateChildRect(); @@ -262,6 +275,7 @@ StSlaveWindowCfg_t myMonSlave; //!< slave window options size_t mySyncCounter; int myWinOnMonitorId; //!< monitor id where window is placed + int myWinMonScaleId; //!< monitor id from which scale factor is applied TiledCfg myTiledCfg; //!< tiles configuration (multiple viewports within the same window) #ifdef _WIN32 @@ -289,7 +303,7 @@ /** * Window attributes structure for internal use. - * Notice that some options couln't be changed after window was created! + * Notice that some options could not be changed after window was created! */ struct { bool IsNoDecor; //!< to decorate master window or not (will be ignored in case of embedded and fullscreen) @@ -308,6 +322,7 @@ StWinSlave Slave; //!< slave configuration int8_t SlaveMonId; //!< on which monitor show slave window (1 by default) StWinSplit Split; //!< split window configuration + bool ToAlignEven; //!< align window position to even numbers } attribs; struct { diff -Nru sview-13.10/StCore/StWindowImplLin.cpp sview-14.01/StCore/StWindowImplLin.cpp --- sview-13.10/StCore/StWindowImplLin.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindowImplLin.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -795,6 +795,42 @@ parseXDNDSelectionMsg(); break; } + case SelectionRequest: { + const XSelectionRequestEvent& aRequest = myXEvent.xselectionrequest; + if(aRequest.selection != aDisplay->XA_CLIPBOARD) { + break; + } + + XSelectionEvent aReply; + stMemZero(&aReply, sizeof(aReply)); + aReply.type = SelectionNotify; + aReply.serial = myXEvent.xany.send_event; + aReply.display = aRequest.display; + aReply.requestor = aRequest.requestor; + aReply.selection = aRequest.selection; + aReply.property = aRequest.property; + aReply.target = None; + aReply.time = aRequest.time; + if(aRequest.target == aDisplay->XA_TARGETS) { + //ST_DEBUG_LOG("SelectionRequest(XA_TARGETS)"); + Atom aTargets[] = { XA_STRING, aDisplay->XA_UTF8_STRING, aDisplay->XA_COMPOUND_TEXT }; + XChangeProperty(aDisplay->hDisplay, aRequest.requestor, aRequest.property, + XA_ATOM, 32, PropModeReplace, (unsigned char* )aTargets, 3); + + } else if(aRequest.target == XA_STRING + || aRequest.target == aDisplay->XA_UTF8_STRING + || aRequest.target == aDisplay->XA_COMPOUND_TEXT) { + //ST_DEBUG_LOG("SelectionRequest(XA_STRING)= " + myTextToCopy); + XChangeProperty(aDisplay->hDisplay, aRequest.requestor, aRequest.property, + aRequest.target, 8, PropModeReplace, (unsigned char* )myTextToCopy.toCString(), myTextToCopy.getSize()); + } else { + aReply.property = None; + } + + XSendEvent(aDisplay->hDisplay, aRequest.requestor, False, NoEventMask, (XEvent* )&aReply); + XFlush(aDisplay->hDisplay); + break; + } case DestroyNotify: { // something else... break; @@ -955,4 +991,19 @@ swapEventsBuffers(); } +bool StWindowImpl::toClipboard(const StString& theText) { + const StXDisplayH& aDisplay = myMaster.stXDisplay; + if(aDisplay.isNull() || myMaster.hWindowGl == 0) { + // window is closed! + return false; + } + + myTextToCopy = theText; + + // setup owner of the XA_CLIPBOARD atom + XSetSelectionOwner(aDisplay->hDisplay, aDisplay->XA_CLIPBOARD, + myMaster.hWindowGl, CurrentTime); + return true; +} + #endif diff -Nru sview-13.10/StCore/StWindowImplMac.mm sview-14.01/StCore/StWindowImplMac.mm --- sview-13.10/StCore/StWindowImplMac.mm 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindowImplMac.mm 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011-2013 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -514,4 +514,13 @@ swapEventsBuffers(); } +bool StWindowImpl::toClipboard(const StString& theText) { + StCocoaLocalPool aLocalPool; + NSPasteboard* aPasteBoard = [NSPasteboard generalPasteboard]; + [aPasteBoard clearContents]; + StCocoaString aStringMy(theText); + NSArray* anObjects = [NSArray arrayWithObject: aStringMy.toStringNs()]; + return [aPasteBoard writeObjects: anObjects] == YES; +} + #endif // __APPLE__ diff -Nru sview-13.10/StCore/StWindowImplWin.cpp sview-14.01/StCore/StWindowImplWin.cpp --- sview-13.10/StCore/StWindowImplWin.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StWindowImplWin.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -138,7 +138,10 @@ myIsUpdated = true; } - // use WS_EX_NOPARENTNOTIFY style to prevent to send notify on destroing our child window (NPAPI plugin -> deadlock) + myWinOnMonitorId = myMonitors[myRectNorm.center()].getId(); + myWinMonScaleId = myWinOnMonitorId; + + // use WS_EX_NOPARENTNOTIFY style to prevent to send notify on destroying our child window (NPAPI plugin -> deadlock) DWORD masterWindowGl_dwExStyle = (myParentWin == NULL) ? WS_EX_NOACTIVATE : (WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY); myMaster.hWindowGl = CreateWindowExW(masterWindowGl_dwExStyle, myMaster.ClassGL.toCString(), @@ -375,6 +378,16 @@ } } +/** + * @return normalized delta (-1 or 0 or +1) + */ +inline int getDirNorm(const int theFrom, + const int theTo) { + return theFrom > theTo + ? -1 + : (theFrom < theTo ? 1 : 0); +} + LRESULT StWindowImpl::stWndProc(HWND theWin, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { // Check For Windows Messages case WM_ACTIVATE: { // Watch For Window Activate Message @@ -478,10 +491,67 @@ break; } else if(theWin == myMaster.hWindow) { RECT* aRect = (RECT* )(LPARAM )lParam; + const StRectI_t aPrevRect = myRectNorm; myRectNorm.left() = aRect->left + GetSystemMetrics(SM_CXSIZEFRAME); myRectNorm.right() = aRect->right - GetSystemMetrics(SM_CXSIZEFRAME); myRectNorm.top() = aRect->top + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION); myRectNorm.bottom() = aRect->bottom - GetSystemMetrics(SM_CYSIZEFRAME); + if(attribs.ToAlignEven) + { + // adjust window position to ensure alignment + const int aDL = getDirNorm(aPrevRect.left(), myRectNorm.left()); + const int aDR = getDirNorm(aPrevRect.right(), myRectNorm.right()); + const int aDT = getDirNorm(aPrevRect.top(), myRectNorm.top()); + const int aDB = getDirNorm(aPrevRect.bottom(), myRectNorm.bottom()); + if(isOddNumber(myRectNorm.left())) { + myRectNorm.left() += aDL; + aRect->left += aDL; + } + if(isEvenNumber(myRectNorm.right())) { + myRectNorm.right() += aDR; + aRect->right += aDR; + } + if(isOddNumber(myRectNorm.top())) { + myRectNorm.top() += aDT; + aRect->top += aDT; + } + if(isEvenNumber(myRectNorm.bottom())) { + aRect->bottom += aDB; + myRectNorm.bottom() += aDB; + } + } + + // determine monitor scale factor change + const StMonitor& aMonTo = myMonitors[myRectNorm.center()]; + const int aNewMonId = aMonTo.getId(); + if(myWinMonScaleId != aNewMonId) { + const StMonitor& aMonFrom = myMonitors[myWinMonScaleId]; + if(!stAreEqual(aMonTo.getScale(), aMonFrom.getScale(), 0.1f)) { + StRectI_t aRectScaled = myRectNorm; + const double aCoeff = double(aMonTo.getScale()) / double(aMonFrom.getScale()); + const int aWidth = int(double(myRectNorm.width()) * aCoeff); + const int aHeight = int(double(myRectNorm.height()) * aCoeff); + aRectScaled.right() = aRectScaled.left() + aWidth; + aRectScaled.bottom() = aRectScaled.top() + aHeight; + const StMonitor& aMonMon = myMonitors[aRectScaled.center()]; + if(aMonMon.getId() == aNewMonId) { + // process only if resized window is still on the same monitor (protect against cascade scaling) + myRectNorm = aRectScaled; + aRect->top = myRectNorm.top(); + aRect->bottom = myRectNorm.bottom(); + aRect->left = myRectNorm.left(); + aRect->right = myRectNorm.right(); + // take into account decorations + const DWORD aWinStyle = (!attribs.IsNoDecor ? WS_OVERLAPPEDWINDOW : WS_POPUP) | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + const DWORD aWinStyleEx = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES; + AdjustWindowRectEx(aRect, aWinStyle, FALSE, aWinStyleEx); + myWinMonScaleId = aNewMonId; + } + } else { + myWinMonScaleId = aNewMonId; + } + } + myIsUpdated = true; myStEvent.Type = stEvent_Size; @@ -800,9 +870,10 @@ // detect when window moved to another monitor if(!attribs.IsFullScreen && myMonitors.size() > 1) { - int aNewMonId = myMonitors[myRectNorm.center()].getId(); + const StMonitor& aMonTo = myMonitors[myRectNorm.center()]; + const int aNewMonId = aMonTo.getId(); if(myWinOnMonitorId != aNewMonId) { - myStEventAux.Type = stEvent_NewMonitor; + myStEventAux.Type = stEvent_NewMonitor; myStEventAux.Size.Time = getEventTime(); myStEventAux.Size.SizeX = myRectNorm.width(); myStEventAux.Size.SizeY = myRectNorm.height(); @@ -863,4 +934,26 @@ swapEventsBuffers(); } +bool StWindowImpl::toClipboard(const StString& theText) { + const StStringUtfWide aWideText = theText.toUtfWide(); + HGLOBAL aMem = GlobalAlloc(GMEM_MOVEABLE, aWideText.Size + sizeof(wchar_t)); + if(aMem == NULL) { + return false; + } + + stMemCpy(GlobalLock(aMem), aWideText.String, aWideText.Size + sizeof(wchar_t)); + GlobalUnlock(aMem); + if(!OpenClipboard(NULL)) { + return false; + } + + EmptyClipboard(); + if(!SetClipboardData(CF_UNICODETEXT, aMem)) { + CloseClipboard(); + return false; + } + CloseClipboard(); + return true; +} + #endif diff -Nru sview-13.10/StCore/StXDisplay.cpp sview-14.01/StCore/StXDisplay.cpp --- sview-13.10/StCore/StXDisplay.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StXDisplay.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,7 +41,10 @@ xDNDAware(None), xDNDPlainText(None), xDNDPrimary(None), - XA_TARGETS(None) { + XA_TARGETS(None), + XA_COMPOUND_TEXT(None), + XA_UTF8_STRING(None), + XA_CLIPBOARD(None) { stMemZero(&FBCfg, sizeof(GLXFBConfig)); // should be just a pointer open(); } @@ -119,12 +122,15 @@ xDNDSelection = XInternAtom(hDisplay, "XdndSelection", False); xDNDProxy = XInternAtom(hDisplay, "XdndProxy", False); xDNDAware = XInternAtom(hDisplay, "XdndAware", False); - xDNDPlainText = XInternAtom(hDisplay, "text/plain", False); //"UTF8_STRING", "COMPOUND_TEXT" + xDNDPlainText = XInternAtom(hDisplay, "text/plain", False); xDNDPrimary = XInternAtom(hDisplay, "PRIMARY", False); // This is a meta-format for data to be "pasted" in to. // Requesting this format acquires a list of possible // formats from the application which copied the data. - XA_TARGETS = XInternAtom(hDisplay, "TARGETS", False); + XA_TARGETS = XInternAtom(hDisplay, "TARGETS", True); + XA_COMPOUND_TEXT = XInternAtom(hDisplay, "COMPOUND_TEXT", True); + XA_UTF8_STRING = XInternAtom(hDisplay, "UTF8_STRING", True); + XA_CLIPBOARD = XInternAtom(hDisplay, "CLIPBOARD", True); } diff -Nru sview-13.10/StCore/StXDisplay.h sview-14.01/StCore/StXDisplay.h --- sview-13.10/StCore/StXDisplay.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StCore/StXDisplay.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StCore library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -42,28 +42,32 @@ Display* hDisplay; // connection to the X-server XVisualInfo* hVisInfo; // visual info - GLXFBConfig FBCfg; + GLXFBConfig FBCfg; - XIM hInputMethod; - XIC hInputCtx; + XIM hInputMethod; + XIC hInputCtx; - Atom wndProtocols; - Atom wndDestroyAtom; // Atom for close message + Atom wndProtocols; + Atom wndDestroyAtom; // Atom for close message - Atom xDNDEnter; // Atoms for X drag&drop protocol - Atom xDNDPosition; - Atom xDNDStatus; - Atom xDNDTypeList; - Atom xDNDActionCopy; + Atom xDNDEnter; // Atoms for X drag&drop protocol + Atom xDNDPosition; + Atom xDNDStatus; + Atom xDNDTypeList; + Atom xDNDActionCopy; Atom xDNDDrop; - Atom xDNDLeave; - Atom xDNDFinished; - Atom xDNDSelection; - Atom xDNDProxy; - Atom xDNDAware; - Atom xDNDPlainText; - Atom xDNDPrimary; - Atom XA_TARGETS; + Atom xDNDLeave; + Atom xDNDFinished; + Atom xDNDSelection; + Atom xDNDProxy; + Atom xDNDAware; + Atom xDNDPlainText; + Atom xDNDPrimary; + + Atom XA_TARGETS; + Atom XA_COMPOUND_TEXT; + Atom XA_UTF8_STRING; + Atom XA_CLIPBOARD; private: diff -Nru sview-13.10/StDiagnostics/StDiagnostics.cbp sview-14.01/StDiagnostics/StDiagnostics.cbp --- sview-13.10/StDiagnostics/StDiagnostics.cbp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StDiagnostics/StDiagnostics.cbp 2014-01-30 10:45:03.000000000 +0000 @@ -33,12 +33,7 @@ - - - - - - + @@ -69,12 +64,7 @@ - - - - - - + @@ -111,12 +101,7 @@ - - - - - - + @@ -148,12 +133,7 @@ - - - - - - + @@ -181,12 +161,7 @@ - - - - - - + @@ -213,12 +188,7 @@ - - - - - - + @@ -240,12 +210,7 @@ - - - - - - + @@ -269,12 +234,7 @@ - - - - - - + @@ -329,6 +289,9 @@ + + diff -Nru sview-13.10/StDiagnostics/StDiagnostics.cpp sview-14.01/StDiagnostics/StDiagnostics.cpp --- sview-13.10/StDiagnostics/StDiagnostics.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StDiagnostics/StDiagnostics.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -35,8 +35,7 @@ StDiagnostics::StDiagnostics(const StNativeWin_t theParentWin, const StHandle& theOpenInfo) -: StApplication(theParentWin, theOpenInfo), - myToQuit(false) { +: StApplication(theParentWin, theOpenInfo) { myTitle = "sView - Stereoscopic Device Diagnostics"; params.IsFullscreen = new StBoolParam(false); params.IsFullscreen->signals.onChanged.connect(this, &StDiagnostics::doFullscreen); diff -Nru sview-13.10/StDiagnostics/StDiagnostics.h sview-14.01/StDiagnostics/StDiagnostics.h --- sview-13.10/StDiagnostics/StDiagnostics.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StDiagnostics/StDiagnostics.h 2014-01-30 10:45:03.000000000 +0000 @@ -79,7 +79,6 @@ StHandle myContext; StHandle mySettings; //!< settings manager for Diagnostics plugin StHandle myGUI; //!< GUI root widget - bool myToQuit; }; diff -Nru sview-13.10/StDiagnostics/StDiagnostics.rc sview-14.01/StDiagnostics/StDiagnostics.rc --- sview-13.10/StDiagnostics/StDiagnostics.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StDiagnostics/StDiagnostics.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "Stereoscopic Device Diagnostics\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2010-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2010-2014 Kirill Gavrilov\000" VALUE "ProductName", "StDiagnostics\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StDiagnostics/lang/german/StDiagnostics.lng sview-14.01/StDiagnostics/lang/german/StDiagnostics.lng --- sview-13.10/StDiagnostics/lang/german/StDiagnostics.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StDiagnostics/lang/german/StDiagnostics.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,4 @@ +German translation file for StDiagnostics plugin +@author Kirill Gavrilov + +-------- diff -Nru sview-13.10/StDiagnostics/lang/korean/StDiagnostics.lng sview-14.01/StDiagnostics/lang/korean/StDiagnostics.lng --- sview-13.10/StDiagnostics/lang/korean/StDiagnostics.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StDiagnostics/lang/korean/StDiagnostics.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,4 @@ +English translation file for StDiagnostics plugin +@author Kirill Gavrilov + +-------- diff -Nru sview-13.10/StGLWidgets/StGLMenuItem.cpp sview-14.01/StGLWidgets/StGLMenuItem.cpp --- sview-13.10/StGLWidgets/StGLMenuItem.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLMenuItem.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * Distributed under the Boost Software License, Version 1.0. * See accompanying file license-boost.txt or copy at @@ -98,35 +98,6 @@ myToHilightText = true; } -const int StGLMenuItem::computeTextWidth() { - StHandle& aFont = myFont->getFont(); - if(aFont.isNull() || !aFont->isValid()) { - return myRoot->scale(int(10 * (myText.getLength() + 2))); - } - - GLfloat aWidth = 0.0f; - GLfloat aWidthMax = 0.0f; - for(StUtf8Iter anIter = myText.iterator(); *anIter != 0;) { - const stUtf32_t aCharThis = *anIter; - const stUtf32_t aCharNext = *++anIter; - - if(aCharThis == '\x0D') { - continue; // ignore CR - } else if(aCharThis == '\x0A') { - aWidthMax = stMax(aWidthMax, aWidth); - aWidth = 0.0f; - continue; // will be processed on second pass - } else if(aCharThis == ' ') { - aWidth += aFont->getAdvanceX(aCharThis, aCharNext); - continue; - } - - aWidth += aFont->getAdvanceX(aCharThis, aCharNext); - } - aWidthMax = stMax(aWidthMax, aWidth); - return int(aWidthMax + 0.5f); -} - void StGLMenuItem::stglResize() { StGLContext& aCtx = getContext(); diff -Nru sview-13.10/StGLWidgets/StGLMessageBox.cpp sview-14.01/StGLWidgets/StGLMessageBox.cpp --- sview-13.10/StGLWidgets/StGLMessageBox.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLMessageBox.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov +namespace { + static const int OFFSET_PIXELS = 32; +}; + StGLMessageBox::StGLMessageBox(StGLWidget* theParent, + const StString& theTitle, const StString& theText, const int theWidth, const int theHeight) : StGLWidget(theParent, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER), theWidth, theHeight), myContent(NULL), + myTitle(NULL), myBtnPanel(NULL), myDefaultBtn(NULL), - myButtonsNb(0) { - create(theText, theWidth, theHeight); + myButtonsNb(0), + myMarginLeft(0), + myMarginRight(0), + myMarginTop(0), + myMarginBottom(0), + myMinSizeY(0), + myToAdjustY(true) { + create(theTitle, theText, theWidth, theHeight); } StGLMessageBox::StGLMessageBox(StGLWidget* theParent, + const StString& theTitle, const StString& theText) : StGLWidget(theParent, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER), theParent->getRoot()->scale(384), theParent->getRoot()->scale(200)), myContent(NULL), + myTitle(NULL), myBtnPanel(NULL), myDefaultBtn(NULL), - myButtonsNb(0) { - create(theText, myRoot->scale(384), myRoot->scale(200)); + myButtonsNb(0), + myMarginLeft(0), + myMarginRight(0), + myMarginTop(0), + myMarginBottom(0), + myMinSizeY(0), + myToAdjustY(true) { + create(theTitle, theText, myRoot->scale(384), myRoot->scale(200)); } -void StGLMessageBox::create(const StString& theText, +void StGLMessageBox::create(const StString& theTitle, + const StString& theText, const int theWidth, const int theHeight) { StGLWidget::signals.onMouseUnclick.connect(this, &StGLMessageBox::doMouseUnclick); - const int OFFSET_PIXELS = myRoot->scale(32); - int anOffsetX = theWidth > 2 * OFFSET_PIXELS ? OFFSET_PIXELS : 0; - int anOffsetY = theHeight > 2 * OFFSET_PIXELS ? OFFSET_PIXELS : 0; - myContent = new StGLScrollArea(this, anOffsetX, anOffsetY, + myMarginLeft = myRoot->scale(OFFSET_PIXELS); + myMarginRight = myRoot->scale(OFFSET_PIXELS); + myMarginTop = myRoot->scale(OFFSET_PIXELS); + myMarginBottom = myRoot->scale(24 * 3); + myMinSizeY = myRoot->scale(200); + + int aTitleHeight = 0; + if(!theTitle.isEmpty()) { + myTitle = new StGLTextArea(this, 0, myMarginTop, + StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER), + theWidth - myMarginLeft - myMarginRight, theHeight - myMarginTop - myMarginBottom); + myTitle->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, + StGLTextFormatter::ST_ALIGN_Y_TOP); + myTitle->setupStyle(StFTFont::Style_Bold); + myTitle->setText(theTitle); + myTitle->setTextColor(StGLVec3(1.0f, 1.0f, 1.0f)); + myTitle->setVisibility(true, true); + int aWidth = 0; + myTitle->computeTextWidth(GLfloat(myTitle->getRectPx().width()), aWidth, aTitleHeight); + myTitle->changeRectPx().bottom() = myTitle->getRectPx().top() + aTitleHeight; + //myTitle->stglInitAutoHeight(); + } + + const int aTitleOffset = aTitleHeight > 0 ? (aTitleHeight + myRoot->scale(24)) : 0; + myMarginTop += aTitleOffset; + myContent = new StGLScrollArea(this, myMarginLeft, myMarginTop, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT), - theWidth - 2 * anOffsetX, theHeight - myRoot->scale(24 * 3) - anOffsetY); + theWidth - myMarginLeft - myMarginRight, theHeight - myMarginTop - myMarginBottom); setText(theText); myIsTopWidget = true; @@ -66,6 +109,12 @@ myProgram.release(aCtx); } +void StGLMessageBox::setTitle(const StString& theTitle) { + if(myTitle != NULL) { + myTitle->setText(theTitle); + } +} + void StGLMessageBox::setText(const StString& theText) { myContent->destroyChildren(); if(theText.isEmpty()) { @@ -199,6 +248,18 @@ } void StGLMessageBox::stglResize() { + if(myContent != NULL) { + const StGLWidget* aContent = myContent->getChildren()->getStart(); + if(aContent != NULL + && myToAdjustY) { + // adjust message box to fit content + const int aSizeYToFit = aContent->getRectPx().height() + myMarginTop + myMarginBottom; + const int aNewSizeY = stMax(myMinSizeY, stMin(aSizeYToFit, myParent->getRectPx().height() - myRoot->scale(120))); + changeRectPx().bottom() = aNewSizeY; + myContent->changeRectPx().bottom() = myContent->getRectPx().top() + aNewSizeY - myMarginTop - myMarginBottom; + } + } + StGLWidget::stglResize(); GLfloat toZScreen = -getCamera()->getZScreen(); @@ -226,7 +287,7 @@ aCtx.core20fwd->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); aCtx.core20fwd->glEnable(GL_BLEND); - myProgram.use(aCtx); + myProgram.use(aCtx, getRoot()->getScreenDispX()); myProgram.setProjMat(aCtx, getCamera()->getProjMatrix()); myProgram.setColor(aCtx, StGLVec4(0.06f, 0.06f, 0.06f, 1.0f), GLfloat(opacityValue) * 0.8f); diff -Nru sview-13.10/StGLWidgets/StGLMsgStack.cpp sview-14.01/StGLWidgets/StGLMsgStack.cpp --- sview-13.10/StGLWidgets/StGLMsgStack.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLMsgStack.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -32,7 +32,7 @@ // check messages stack while(myMsgQueue->pop(myMsgTmp)) { - StGLMessageBox* aMsgBox = new StGLMessageBox(this, *myMsgTmp.Text); + StGLMessageBox* aMsgBox = new StGLMessageBox(this, "", *myMsgTmp.Text); aMsgBox->addButton("Close"); aMsgBox->setVisibility(true, true); aMsgBox->stglInit(); diff -Nru sview-13.10/StGLWidgets/StGLRootWidget.cpp sview-14.01/StGLWidgets/StGLRootWidget.cpp --- sview-13.10/StGLWidgets/StGLRootWidget.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLRootWidget.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -42,6 +42,7 @@ myShareSize(10), myScrDispX(0.0f), myLensDist(0.0f), + myScrDispXPx(0), myScaleGlX(1.0), myScaleGlY(1.0), myScaleGUI(1.0f), @@ -64,6 +65,7 @@ for(size_t aResId = 0; aResId < myShareSize; ++aResId) { myShareArray[aResId] = new StGLSharePointer(); } + myGlFontMgr = new StGLFontManager(myResolution); } StGLRootWidget::~StGLRootWidget() { @@ -75,13 +77,17 @@ delete myShareArray[aResId]; } delete[] myShareArray; + if(!myGlCtx.isNull()) { + myGlFontMgr->release(*myGlCtx); + myGlFontMgr.nullify(); + } } StGLContext& StGLRootWidget::getContext() { return *myGlCtx; } -const StHandle& StGLRootWidget::getContextHandle() { +const StHandle& StGLRootWidget::getContextHandle() const { return myGlCtx; } @@ -142,6 +148,7 @@ } myScaleGUI = aScale; myResolution = (unsigned int )(72.0f * aScale + 0.1f); + myGlFontMgr->setResolution(myResolution); } bool StGLRootWidget::stglInit() { @@ -160,14 +167,17 @@ switch(theView) { case ST_DRAW_LEFT: - myScrDispX = myLensDist * GLfloat(0.5 * myRectGl.width()); + myScrDispX = myLensDist * GLfloat(0.5 * myRectGl.width()); + myScrDispXPx = int(double(myLensDist) * 0.5 * double(rectPx.width())); break; case ST_DRAW_RIGHT: - myScrDispX = -myLensDist * GLfloat(0.5 * myRectGl.width()); + myScrDispX = -myLensDist * GLfloat(0.5 * myRectGl.width()); + myScrDispXPx = -int(double(myLensDist) * 0.5 * double(rectPx.width())); break; case ST_DRAW_MONO: default: - myScrDispX = 0.0f; + myScrDispX = 0.0f; + myScrDispXPx = 0; break; } @@ -214,7 +224,7 @@ const GLdouble aWidthFactor = GLdouble(aVPortWidth) / GLdouble(aRootWidth); const GLdouble aHeightFactor = GLdouble(aVPortHeight) / GLdouble(aRootHeight); - theScissorRect.x() = myViewport[0] + GLint(aWidthFactor * GLdouble(theRect.left())); + theScissorRect.x() = myViewport[0] + GLint(aWidthFactor * GLdouble(theRect.left())) + myScrDispXPx; theScissorRect.y() = myViewport[1] + GLint(aHeightFactor * GLdouble(aRootHeight - theRect.bottom())); theScissorRect.width() = GLint(aWidthFactor * GLdouble(theRect.width())); diff -Nru sview-13.10/StGLWidgets/StGLScrollArea.cpp sview-14.01/StGLWidgets/StGLScrollArea.cpp --- sview-13.10/StGLWidgets/StGLScrollArea.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLScrollArea.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -44,6 +44,16 @@ } void StGLScrollArea::stglResize() { + StGLWidget* aContent = myChildren.getStart(); + if(!isScrollable() + && aContent != NULL + && aContent->getRectPx().top() < 0 + && aContent->getCorner().v == ST_VCORNER_TOP) { + const int aDelta = -aContent->getRectPx().top(); + aContent->changeRectPx().top() += aDelta; + aContent->changeRectPx().bottom() += aDelta; + } + StGLWidget::stglResize(); } diff -Nru sview-13.10/StGLWidgets/StGLSubtitles.cpp sview-14.01/StGLWidgets/StGLSubtitles.cpp --- sview-13.10/StGLWidgets/StGLSubtitles.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLSubtitles.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2010-2013 Kirill Gavrilov + * Copyright © 2010-2014 Kirill Gavrilov * * Distributed under the Boost Software License, Version 1.0. * See accompanying file license-boost.txt or copy at @@ -7,11 +7,83 @@ */ #include + +#include +#include #include namespace { static const StString CLASS_NAME("StGLSubtitles"); - static const size_t SHARE_SUBS_FONT_ID = StGLRootWidget::generateShareId(); + static const size_t SHARE_IMAGE_PROGRAM_ID = StGLRootWidget::generateShareId(); +}; + +class StGLSubtitles::StImgProgram : public StGLProgram { + + public: + + StImgProgram() : StGLProgram("StGLSubtitles") {} + + StGLVarLocation getVVertexLoc() const { return StGLVarLocation(0); } + StGLVarLocation getVTexCoordLoc() const { return StGLVarLocation(1); } + + void setProjMat(StGLContext& theCtx, + const StGLMatrix& theProjMat) { + theCtx.core20fwd->glUniformMatrix4fv(uniProjMatLoc, 1, GL_FALSE, theProjMat); + } + + virtual bool init(StGLContext& theCtx) { + const char VERTEX_SHADER[] = + "uniform mat4 uProjMat;\n" + "uniform vec4 uDisp;\n" + "attribute vec4 vVertex;\n" + "attribute vec2 vTexCoord;\n" + "varying vec2 fTexCoord;\n" + "void main(void) {\n" + " fTexCoord = vTexCoord;\n" + " gl_Position = uProjMat * (vVertex + uDisp);\n" + "}\n"; + + const char FRAGMENT_SHADER[] = + "uniform sampler2D uTexture;\n" + "varying vec2 fTexCoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(uTexture, fTexCoord);\n" + "}\n"; + + StGLVertexShader aVertexShader(StGLProgram::getTitle()); + aVertexShader.init(theCtx, VERTEX_SHADER); + StGLAutoRelease aTmp1(theCtx, aVertexShader); + + StGLFragmentShader aFragmentShader(StGLProgram::getTitle()); + aFragmentShader.init(theCtx, FRAGMENT_SHADER); + StGLAutoRelease aTmp2(theCtx, aFragmentShader); + if(!StGLProgram::create(theCtx) + .attachShader(theCtx, aVertexShader) + .attachShader(theCtx, aFragmentShader) + .bindAttribLocation(theCtx, "vVertex", getVVertexLoc()) + .bindAttribLocation(theCtx, "vTexCoord", getVTexCoordLoc()) + .link(theCtx)) { + return false; + } + + StGLVarLocation uniTextureLoc = StGLProgram::getUniformLocation(theCtx, "uTexture"); + if(uniTextureLoc.isValid()) { + StGLProgram::use(theCtx); + theCtx.core20fwd->glUniform1i(uniTextureLoc, StGLProgram::TEXTURE_SAMPLE_0); + StGLProgram::unuse(theCtx); + } + + uniProjMatLoc = StGLProgram::getUniformLocation(theCtx, "uProjMat"); + uniDispLoc = StGLProgram::getUniformLocation(theCtx, "uDisp"); + return uniProjMatLoc.isValid() + && uniTextureLoc.isValid(); + } + + private: + + StGLVarLocation uniProjMatLoc; + StGLVarLocation uniDispLoc; + }; StGLSubtitles::StSubShowItems::StSubShowItems() @@ -24,33 +96,49 @@ for(size_t anId = size() - 1; anId < size_t(-1); --anId) { // filter outdated and forward items const StHandle& anItem = getValue(anId); - if(anItem->myTimeEnd < thePTS || anItem->myTimeStart > thePTS) { + if(anItem->TimeEnd < thePTS || anItem->TimeStart > thePTS) { remove(anId); isChanged = true; } } if(!isChanged) { - return isChanged; + return false; } else if(isEmpty()) { - myText.clear(); - return isChanged; + Text.clear(); + Image.nullify(); + return true; } // update active text - myText = getFirst()->myText; + Text = getFirst()->Text; for(size_t anId = 1; anId < size(); ++anId) { const StHandle& anItem = getValue(anId); - myText += StString('\n'); - myText += anItem->myText; + Text += StString('\n'); + Text += anItem->Text; } + + // update active image + const StImagePlane& anImage = getFirst()->Image; + if(!anImage.isNull()) { + Image.initCopy(anImage); + } else { + Image.nullify(); + } + return isChanged; } void StGLSubtitles::StSubShowItems::add(const StHandle& theItem) { - if(!myText.isEmpty()) { - myText += StString('\n'); + if(!Text.isEmpty()) { + Text += StString('\n'); } - myText += theItem->myText; + Text += theItem->Text; + + const StImagePlane& anImage = theItem->Image; + if(!anImage.isNull()) { + Image.initCopy(anImage); + } + StArrayList >::add(theItem); } @@ -61,18 +149,18 @@ StGLSubtitles::StGLSubtitles(StGLWidget* theParent, const StHandle& theSubQueue, const StHandle& theFontSize, - const StHandle& theParallax) + const StHandle& theParallax, + const StHandle& theParser) : StGLTextArea(theParent, 0, -theParent->getRoot()->scale(100), StGLCorner(ST_VCORNER_BOTTOM, ST_HCORNER_CENTER), - theParent->getRoot()->scale(800), theParent->getRoot()->scale(160), - (FontSize )(int )theFontSize->getValue(), - SHARE_SUBS_FONT_ID), + theParent->getRoot()->scale(800), theParent->getRoot()->scale(160)), myFontSize(theFontSize), myParallax(theParallax), + myParser(theParser), myQueue(theSubQueue), - myShowItems(), - myPTS(0.0) { + myPTS(0.0), + myImgProgram(getRoot()->getShare(SHARE_IMAGE_PROGRAM_ID)) { if(myQueue.isNull()) { myQueue = new StSubQueue(); } @@ -83,10 +171,56 @@ myFormatter.setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, StGLTextFormatter::ST_ALIGN_Y_BOTTOM); + + StHandle aFontNew = new StGLFont(); + StHandle aLib = getRoot()->getFontManager()->getLibraty(); + const FontSize aSize = (FontSize )(int )myFontSize->getValue(); + const unsigned int aResolution = getRoot()->getFontManager()->getResolution(); + for(size_t anIter = 0; anIter < StFTFont::SubsetsNB; ++anIter) { + StHandle& aFontGlSrc = myFont->changeFont((StFTFont::Subset )anIter); + if(aFontGlSrc.isNull()) { + continue; + } + + StHandle aFontFt = new StFTFont(aLib); + for(int aStyleIt = 0; aStyleIt < StFTFont::StylesNB; ++aStyleIt) { + aFontFt->load(aFontGlSrc->getFont()->getFilePath((StFTFont::Style )aStyleIt), (StFTFont::Style )aStyleIt); + } + aFontFt->init(aSize, aResolution); + aFontNew->changeFont((StFTFont::Subset )anIter) = new StGLFontEntry(aFontFt); + } + mySize = aSize; + myFont = aFontNew; } StGLSubtitles::~StGLSubtitles() { - // + StGLContext& aCtx = getContext(); + myFont->release(aCtx); + myFont.nullify(); + myTexture.release(aCtx); + myVertBuf.release(aCtx); + myTCrdBuf.release(aCtx); +} + +bool StGLSubtitles::stglInit() { + if(!myVertBuf.isValid()) { + StArray aDummyVert(4); + StArray aTexCoords(4); + aTexCoords[0] = StGLVec2(1.0f, 0.0f); + aTexCoords[1] = StGLVec2(1.0f, 1.0f); + aTexCoords[2] = StGLVec2(0.0f, 0.0f); + aTexCoords[3] = StGLVec2(0.0f, 1.0f); + + StGLContext& aCtx = getContext(); + myVertBuf.init(aCtx, aDummyVert); + myTCrdBuf.init(aCtx, aTexCoords); + + if(myImgProgram.isNull()) { + myImgProgram.create(getRoot()->getContextHandle(), new StImgProgram()); + myImgProgram->init(aCtx); + } + } + return StGLTextArea::stglInit(); } void StGLSubtitles::stglUpdate(const StPointD_t& ) { @@ -96,8 +230,16 @@ myShowItems.add(aNewSubItem); } + StGLContext& aCtx = getContext(); if(isChanged) { - setText(myShowItems.myText); + setText(myShowItems.Text); + + if(!myShowItems.Image.isNull()) { + myTexture.init(aCtx, myShowItems.Image); + } else { + myTexture.release(aCtx); + } + StString aLog; /**for(size_t anId = 0; anId < myShowItems.size(); ++anId) { aLog += ST_STRING(" from ") + myShowItems[anId]->myTimeStart + " to " + myShowItems[anId]->myTimeEnd + "\n"; @@ -108,31 +250,73 @@ const FontSize aNewSize = (FontSize )(int )myFontSize->getValue(); if(!myText.isEmpty() && aNewSize != mySize) { - stglSetTextSize(aNewSize); + mySize = aNewSize; + myToRecompute = true; + + myFont->stglInit(aCtx, getFontSize(), myRoot->getResolution()); } } void StGLSubtitles::stglDraw(unsigned int theView) { - if(!myIsInitialized || !isVisible() || myText.isEmpty()) { + if(!myIsInitialized || !isVisible()) { return; } StGLContext& aCtx = getContext(); - formatText(aCtx); + if(myFormatter.getParser() != (StGLTextFormatter::Parser )myParser->getValue()) { + myFormatter.setupParser((StGLTextFormatter::Parser )myParser->getValue()); + myToRecompute = true; + } + if(!myText.isEmpty()) { + formatText(aCtx); - switch(theView) { - case ST_DRAW_LEFT: - myTextDX = -myParallax->getValue() * GLfloat(0.5 * 0.001 * myRoot->getRootRectGl().width()); - break; - case ST_DRAW_RIGHT: - myTextDX = myParallax->getValue() * GLfloat(0.5 * 0.001 * myRoot->getRootRectGl().width()); - break; - case ST_DRAW_MONO: - default: - myTextDX = 0.0f; - break; + switch(theView) { + case ST_DRAW_LEFT: + myTextDX = -myParallax->getValue() * GLfloat(0.5 * 0.001 * myRoot->getRootRectGl().width()); + break; + case ST_DRAW_RIGHT: + myTextDX = myParallax->getValue() * GLfloat(0.5 * 0.001 * myRoot->getRootRectGl().width()); + break; + case ST_DRAW_MONO: + default: + myTextDX = 0.0f; + break; + } + StGLTextArea::stglDraw(theView); } - StGLTextArea::stglDraw(theView); + + if(!myTexture.isValid() + || !myImgProgram->isValid()) { + return; + } + + aCtx.core20fwd->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + aCtx.core20fwd->glEnable(GL_BLEND); + myTexture.bind(aCtx); + + // update vertices + StRectI_t aRect = getRectPxAbsolute(); + aRect.top() = aRect.bottom() - myTexture.getSizeY(); + aRect.left() = aRect.left() + aRect.width() / 2 - myTexture.getSizeX() / 2; + aRect.right() = aRect.left() + myTexture.getSizeX(); + + StArray aVertices(4); + myRoot->getRectGl(aRect, aVertices); + myVertBuf.init(aCtx, aVertices); + + myImgProgram->use(aCtx); + + myVertBuf.bindVertexAttrib(aCtx, myImgProgram->getVVertexLoc()); + myTCrdBuf.bindVertexAttrib(aCtx, myImgProgram->getVTexCoordLoc()); + + aCtx.core20fwd->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + myTCrdBuf.unBindVertexAttrib(aCtx, myImgProgram->getVTexCoordLoc()); + myVertBuf.unBindVertexAttrib(aCtx, myImgProgram->getVVertexLoc()); + + myImgProgram->unuse(aCtx); + myTexture.unbind(aCtx); + aCtx.core20fwd->glDisable(GL_BLEND); } void StGLSubtitles::stglResize() { @@ -140,6 +324,14 @@ myTextWidth = (GLfloat )getRectPx().width(); myToRecompute = true; StGLTextArea::stglResize(); + + // update projection matrix + if(!myImgProgram.isNull()) { + StGLContext& aCtx = getContext(); + myImgProgram->use(aCtx); + myImgProgram->setProjMat(aCtx, getRoot()->getScreenProjection()); + myImgProgram->unuse(aCtx); + } } const StHandle& StGLSubtitles::getQueue() const { diff -Nru sview-13.10/StGLWidgets/StGLTable.cpp sview-14.01/StGLWidgets/StGLTable.cpp --- sview-13.10/StGLWidgets/StGLTable.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLTable.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,251 @@ +/** + * Copyright © 2014 Kirill Gavrilov + * + * Distributed under the Boost Software License, Version 1.0. + * See accompanying file license-boost.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt + */ + +#include +#include + +#include +#include + +#include + +#include + +namespace { + static const StString CLASS_NAME("StGLTable"); +}; + +StGLTableItem::StGLTableItem(StGLTable* theParent) +: StGLWidget(theParent, + theParent->getMarginLeft(), theParent->getMarginTop(), + StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT), + theParent->getRoot()->scale(32), + theParent->getRoot()->scale(32)), + myColSpan(1), + myRowSpan(1) { + setVisibility(true, true); +} + +StGLTableItem::~StGLTableItem() { + // +} + +StGLTable::StGLTable(StGLWidget* theParent, + const int theLeft, + const int theTop, + StGLCorner theCorner) +: StGLWidget(theParent, + theLeft, theTop, + theCorner, + theParent->getRoot()->scale(32), + theParent->getRoot()->scale(32)), + myMarginLeft (theParent->getRoot()->scale(5)), + myMarginRight (theParent->getRoot()->scale(5)), + myMarginTop (theParent->getRoot()->scale(2)), + myMarginBottom (theParent->getRoot()->scale(2)), + myIsInitialized(false) { + // +} + +StGLTable::~StGLTable() { + // +} + +const StString& StGLTable::getClassName() { + return CLASS_NAME; +} + +void StGLTable::setupTable(const int theNbRows, + const int theNbColumns) { + // destroy old content + for(size_t aRowIter = 0; aRowIter < myTable.size(); ++aRowIter) { + StArrayList& aRow = myTable.changeValue(aRowIter); + for(size_t aColIter = 0; aColIter < aRow.size(); ++aColIter) { + StGLTableItem* anItem = aRow.changeValue(aColIter); + delete anItem; + } + } + myTable.clear(); + + // initialize new empty content + for(int aRowIter = 0; aRowIter < theNbRows; ++aRowIter) { + myTable.add(StArrayList()); + StArrayList& aRow = myTable.changeLast(); + aRow.initArray(theNbColumns); + for(size_t aColIter = 0; aColIter < aRow.size(); ++aColIter) { + aRow.changeValue(aColIter) = new StGLTableItem(this); + } + } + myRowBottoms.initArray(theNbRows); + myColRights .initArray(theNbColumns); + stMemZero(&myRowBottoms.changeFirst(), sizeof(int) * myRowBottoms.size()); + stMemZero(&myColRights .changeFirst(), sizeof(int) * myColRights .size()); +} + +void StGLTable::fillFromMap(const StDictionary& theMap, + const StGLVec3& theTextColor, + const int theMaxWidth, + const int theCol1MaxWidth, + const int theRowId, + const int theColId) { + ST_ASSERT_SLIP(theRowId >= 0 && theColId >= 0, + "StGLTable::fillFromMap() out of range", + return); + const int aRowsNb = theRowId + (int )theMap.size(); + const int aColsNb = theColId + 2; + if(aRowsNb > (int )myRowBottoms.size() + || aColsNb > (int )myColRights.size()) { + setupTable(aRowsNb, aColsNb); + } + + // fill first column with keys + const int aCol1MaxWidth = theCol1MaxWidth - (myMarginLeft + myMarginRight); + int aCol1Width = 0; + for(size_t anIter = 0; anIter < theMap.size(); ++anIter) { + const StDictEntry& aPair = theMap.getValue(anIter); + StGLTableItem& anItem = changeElement(theRowId + (int )anIter, theColId); + anItem.setRowSpan(1); + anItem.setColSpan(1); + + StGLTextArea* aText = new StGLTextArea(&anItem, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT)); + aText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_RIGHT, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aText->setText(aPair.getName().isEmpty() ? aPair.getKey() : aPair.getName()); + aText->setTextColor(theTextColor); + aText->setupStyle(StFTFont::Style_Bold); + aText->setVisibility(true, true); + aText->stglInitAutoHeightWidth(aCol1MaxWidth); + aCol1Width = stMax(aCol1Width, aText->getRectPx().width()); + } + + // adjust width of all elements in first column + // (alternatively we might adjust right corner) + for(size_t anIter = 0; anIter < theMap.size(); ++anIter) { + StGLTableItem& anItem = changeElement(theRowId + (int )anIter, theColId); + anItem.getItem()->changeRectPx().right() = anItem.getItem()->getRectPx().left() + aCol1Width; + ((StGLTextArea* )anItem.getItem())->setTextWidth(aCol1Width); + } + + // fill second column with values + int aCol2MaxWidth = theMaxWidth - aCol1Width - 2 * (myMarginLeft + myMarginRight); + for(size_t anIter = 0; anIter < theMap.size(); ++anIter) { + const StDictEntry& aPair = theMap.getValue(anIter); + StGLTableItem& anItem = changeElement(theRowId + (int )anIter, theColId + 1); + anItem.setRowSpan(1); + anItem.setColSpan(1); + + StGLTextArea* aText = new StGLTextArea(&anItem, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT)); + aText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_LEFT, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aText->setText(aPair.getValue()); + aText->setTextColor(theTextColor); + aText->setVisibility(true, true); + aText->stglInitAutoHeightWidth(aCol2MaxWidth); + } + + updateLayout(); +} + +void StGLTable::stglResize() { + StGLWidget::stglResize(); +} + +bool StGLTable::stglInit() { + myIsInitialized = StGLWidget::stglInit(); + if(!myIsInitialized) { + return false; + } + + updateLayout(); + return true; +} + +void StGLTable::updateLayout() { + if(myRowBottoms.isEmpty() + || myColRights .isEmpty()) { + return; + } + + // determine rows heights + int aBottomPrev = 0; + for(size_t aRowIter = 0; aRowIter < myRowBottoms.size(); ++aRowIter) { + StArrayList& aRow = myTable.changeValue(aRowIter); + for(size_t aColIter = 0; aColIter < myColRights.size(); ++aColIter) { + StGLTableItem* anItem = aRow.changeValue(aColIter); + if(anItem->getItem() == NULL) { + continue; + } + + const int aBefore = aRowIter != 0 + ? myRowBottoms.changeValue(aRowIter - 1) + : 0; + size_t aBotRowId = aRowIter + anItem->getRowSpan() - 1; + int& aBottom = myRowBottoms.changeValue(aBotRowId); + aBottom = stMax(aBottom, + aBefore + anItem->getItem()->getRectPx().height() + + myMarginTop + myMarginBottom); + } + int& aBottom = myRowBottoms.changeValue(aRowIter); + aBottom = stMax(aBottom, aBottomPrev); + aBottomPrev = aBottom; + } + + // determine columns widths + int aRightPrev = 0; + for(size_t aColIter = 0; aColIter < myColRights.size(); ++aColIter) { + for(size_t aRowIter = 0; aRowIter < myRowBottoms.size(); ++aRowIter) { + StGLTableItem* anItem = myTable.changeValue(aRowIter).changeValue(aColIter); + if(anItem->getItem() == NULL) { + continue; + } + + const int aBefore = aColIter != 0 + ? myColRights.changeValue(aColIter - 1) + : 0; + size_t aRightColId = aColIter + anItem->getColSpan() - 1; + int& aRight = myColRights.changeValue(aRightColId); + aRight = stMax(aRight, + aBefore + anItem->getItem()->getRectPx().width() + + myMarginLeft + myMarginRight); + } + int& aRight = myColRights.changeValue(aColIter); + aRight = stMax(aRight, aRightPrev); + aRightPrev = aRight; + } + changeRectPx().right() = getRectPx().left() + myColRights.getLast(); + changeRectPx().bottom() = getRectPx().top() + myRowBottoms.getLast(); + + // adjust table elements positions + int aTop = 0; + for(size_t aRowIter = 0; aRowIter < myRowBottoms.size(); aTop = myRowBottoms.getValue(aRowIter++)) { + StArrayList& aRow = myTable.changeValue(aRowIter); + int aLeft = 0; + for(size_t aColIter = 0; aColIter < myColRights.size(); aLeft = myColRights.getValue(aColIter++)) { + StGLTableItem* anItem = aRow.changeValue(aColIter); + const size_t aBotRowId = aRowIter + anItem->getRowSpan() - 1; + const size_t aRightColId = aColIter + anItem->getColSpan() - 1; + anItem->changeRectPx().top() = aTop + myMarginTop; + anItem->changeRectPx().left() = aLeft + myMarginLeft; + anItem->changeRectPx().bottom() = myRowBottoms.changeValue(aBotRowId) - myMarginBottom; + anItem->changeRectPx().right() = myColRights .changeValue(aRightColId) - myMarginRight; + } + } +} + +void StGLTable::stglDraw(unsigned int theView) { + if(!myIsInitialized || !isVisible()) { + return; + } + + if(isResized) { + stglResize(); + isResized = false; + } + + StGLWidget::stglDraw(theView); +} diff -Nru sview-13.10/StGLWidgets/StGLTextArea.cpp sview-14.01/StGLWidgets/StGLTextArea.cpp --- sview-13.10/StGLWidgets/StGLTextArea.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLTextArea.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * Distributed under the Boost Software License, Version 1.0. * See accompanying file license-boost.txt or copy at @@ -206,87 +206,16 @@ static const StString CLASS_NAME("StGLTextArea"); static const size_t SHARE_TEXT_PROGRAM_ID = StGLRootWidget::generateShareId(); static const size_t SHARE_BORDER_PROGRAM_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_NORMAL_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_SMALLEST_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_SMALL_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_BIG_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_BIGGEST_ID = StGLRootWidget::generateShareId(); - static const size_t SHARE_FONT_DOUBLE_ID = StGLRootWidget::generateShareId(); - - enum { - ST_FONT_SERIF, - ST_FONT_SANS, - ST_FONT_MONO, - }; - - static StString getFont(const int theFontType) { - #ifdef _WIN32 - switch(theFontType) { - case ST_FONT_SERIF: - return "times.ttf"; - case ST_FONT_SANS: - return "micross.ttf"; - case ST_FONT_MONO: - default: - return "tahoma.ttf"; - } - #elif defined(__APPLE__) - switch(theFontType) { - case ST_FONT_SERIF: - return "Times.dfont"; - case ST_FONT_SANS: - return "Trebuchet MS.ttf"; - //return "LucidaGrande.ttc"; - //return "Geneva.dfont"; - //return "HelveticaNeue.dfont"; - //return "Helvetica.dfont"; - case ST_FONT_MONO: - default: - return "Monaco.dfont"; - } - #elif defined(__linux__) - switch(theFontType) { - case ST_FONT_SERIF: - return "DejaVuSerif.ttf"; - case ST_FONT_SANS: - return "DejaVuSans.ttf"; - case ST_FONT_MONO: - default: - return "DejaVuSansMono.ttf"; - } - #else - #error undefined fonts - #endif - } - - inline size_t getFontShareId(const StGLTextArea::FontSize theSize, - const size_t theShareId) { - if(theShareId != size_t(-1)) { - return theShareId; - } - - switch(theSize) { - case StGLTextArea::SIZE_SMALLEST: return SHARE_FONT_SMALLEST_ID; - case StGLTextArea::SIZE_SMALL: return SHARE_FONT_SMALL_ID; - case StGLTextArea::SIZE_NORMAL: return SHARE_FONT_NORMAL_ID; - case StGLTextArea::SIZE_BIGGEST: return SHARE_FONT_BIGGEST_ID; - case StGLTextArea::SIZE_DOUBLE: return SHARE_FONT_DOUBLE_ID; - case StGLTextArea::SIZE_BIG: - default: return SHARE_FONT_BIG_ID; - } - } }; StGLTextArea::StGLTextArea(StGLWidget* theParent, const int theLeft, const int theTop, const StGLCorner theCorner, const int theWidth, const int theHeight, - const FontSize theSize, - const size_t theShareId) + const FontSize theSize) : StGLWidget(theParent, theLeft, theTop, theCorner, theWidth, theHeight), myTextProgram(getRoot()->getShare(SHARE_TEXT_PROGRAM_ID)), myBorderProgram(getRoot()->getShare(SHARE_BORDER_PROGRAM_ID)), - myFont(getRoot()->getShare(getFontShareId(theSize, theShareId))), mySize(theSize), myTextColor(0.0f, 0.0f, 0.0f, 1.0f), myShadowColor(0.0f, 0.0f, 0.0f, 1.0f), @@ -302,17 +231,7 @@ myToShowBorder(false), myToDrawShadow(false), myIsInitialized(false) { - // initialize FreeType font without GL resources - if(myFont.isNull()) { - const StString ST_FONT_SANS_PATH = StProcess::getFontsRoot() + getFont(ST_FONT_SANS); - StStringUtf8 aFontPath = StFileNode::getCompatibleName(ST_FONT_SANS_PATH); - - StHandle aFont = new StFTFont(); - if(!aFont->init(aFontPath, getFontSize(), myRoot->getResolution())) { - ST_ERROR_LOG("Could not load font '" + ST_FONT_SANS_PATH + '\''); - } - myFont.create(getRoot()->getContextHandle(), new StGLFont(aFont)); - } + myFont = getRoot()->getFontManager()->findCreate(StFTFont::Typeface_SansSerif, getFontSize()); } StGLTextArea::~StGLTextArea() { @@ -342,22 +261,80 @@ } } -void StGLTextArea::stglSetTextSize(const FontSize theSize) { - if(mySize == theSize - || myFont.isNull()) { +void StGLTextArea::setTextWidth(const int theWidth) { + myTextWidth = (GLfloat )theWidth; + myToRecompute = true; +} + +void StGLTextArea::computeTextWidth(const GLfloat theWidthMax, + int& theWidth, + int& theHeight) { + StHandle& aFontGen = myFont->changeFont()->getFont(); + if(aFontGen.isNull() || !aFontGen->isValid()) { + theWidth = myRoot->scale(int(10 * (myText.getLength() + 2))); + theHeight = myRoot->scale(16); return; } - mySize = theSize; - myToRecompute = true; - - StGLContext& aCtx = getContext(); - myFont->stglInit(aCtx, getFontSize(), myRoot->getResolution()); -} + GLfloat aWidth = 0.0f; + GLfloat aWidthMax = 0.0f; + size_t aCharsInLine = 0; + size_t aNbLines = 1; + aFontGen->setActiveStyle(myFormatter.getDefaultStyle()); + for(StUtf8Iter anIter = myText.iterator(); *anIter != 0;) { + const stUtf32_t aCharThis = *anIter; + const stUtf32_t aCharNext = *++anIter; + + if(aCharThis == '\x0D') { + continue; // ignore CR + } else if(aCharThis == '\x0A') { + aWidthMax = stMax(aWidthMax, aWidth); + aWidth = 0.0f; + ++aNbLines; + aCharsInLine = 0; + continue; // will be processed on second pass + } else if(aCharThis == ' ') { + aWidth += aFontGen->getAdvanceX(aCharThis, aCharNext); + continue; + } -void StGLTextArea::setTextWidth(const int theWidth) { - myTextWidth = (GLfloat )theWidth; - myToRecompute = true; + const StFTFont::Subset aSubset = StFTFont::subset(aCharThis); + StHandle& aFont = myFont->changeFont(aSubset)->getFont(); + GLfloat anAdvance = (!aFont.isNull() && aFont->hasSymbol(aCharThis)) + ? aFont->getAdvanceX(aCharThis, aCharNext) + : aFontGen->getAdvanceX(aCharThis, aCharNext); + aWidth += anAdvance; + if(theWidthMax > 0.0f + && aWidth > theWidthMax) { + if(aCharsInLine != 0) { + aWidthMax = stMax(aWidthMax, aWidth - anAdvance); + aWidth = anAdvance; + } else { + aWidthMax = stMax(aWidthMax, aWidth); + aWidth = 0.0f; + } + ++aNbLines; + } + } + aWidthMax = stMax(aWidthMax, aWidth); + theWidth = int(aWidthMax + 1.5f); + theHeight = int(GLfloat(aNbLines) * aFontGen->getLineSpacing() + 0.5f); +} + +bool StGLTextArea::stglInitAutoHeightWidth(const int theMaxWidth) { + changeRectPx().right() = getRectPx().left() + theMaxWidth; // compute width from text + if(!stglInit()) { + return false; + } + changeRectPx().bottom() = getRectPx().top() + getTextHeight(); + if(theMaxWidth > 0) { + changeRectPx().right() = getRectPx().left() + GLint(myFormatter.getMaxLineWidth() + 2.5f); + myTextWidth = (GLfloat )getRectPx().width(); + myToRecompute = true; + } else { + changeRectPx().right() = getRectPx().left() + getTextWidth(); + } + return true; } bool StGLTextArea::stglInit() { @@ -368,14 +345,15 @@ // initialize GL resources for the font StGLContext& aCtx = getContext(); if(!myFont->wasInitialized()) { - myFont.setContext(getRoot()->getContextHandle()); - if(!myFont->getFont()->isValid()) { + if( myFont->changeFont().isNull() + || myFont->changeFont()->getFont().isNull() + || !myFont->changeFont()->getFont()->isValid()) { return false; // critical error } else if(!myFont->stglInit(aCtx)) { ST_ERROR_LOG("Could not initialize OpenGL resources for font"); return false; } - } else if(!myFont->isValid()) { + } else if(!myFont->changeFont()->isValid()) { return false; } diff -Nru sview-13.10/StGLWidgets/StGLWidget.cpp sview-14.01/StGLWidgets/StGLWidget.cpp --- sview-13.10/StGLWidgets/StGLWidget.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLWidget.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -67,14 +67,6 @@ } } -StGLCorner StGLWidget::getCorner() const { - return myCorner; -} - -void StGLWidget::setCorner(const StGLCorner theCorner) { - myCorner = theCorner; -} - bool StGLWidget::isChild(StGLWidget* theWidget, const bool theIsRecursive) { for(StGLWidget* aChild = myChildren.getStart(); aChild != NULL; aChild = aChild->getNext()) { diff -Nru sview-13.10/StGLWidgets/StGLWidgets.cbp sview-14.01/StGLWidgets/StGLWidgets.cbp --- sview-13.10/StGLWidgets/StGLWidgets.cbp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLWidgets.cbp 2014-01-30 10:45:03.000000000 +0000 @@ -26,6 +26,7 @@ + @@ -51,6 +52,7 @@ + @@ -79,6 +81,7 @@ + @@ -104,6 +107,7 @@ + @@ -257,6 +261,7 @@ + @@ -304,6 +309,7 @@ + diff -Nru sview-13.10/StGLWidgets/StGLWidgets.rc sview-14.01/StGLWidgets/StGLWidgets.rc --- sview-13.10/StGLWidgets/StGLWidgets.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StGLWidgets.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "OpenGL Widgets library\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2009-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2009-2014 Kirill Gavrilov\000" VALUE "ProductName", "StGLWidgets\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StGLWidgets/StSubQueue.cpp sview-14.01/StGLWidgets/StSubQueue.cpp --- sview-13.10/StGLWidgets/StSubQueue.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StGLWidgets/StSubQueue.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2010-2011 Kirill Gavrilov + * Copyright © 2010-2014 Kirill Gavrilov * * Distributed under the Boost Software License, Version 1.0. * See accompanying file license-boost.txt or copy at @@ -42,12 +42,12 @@ myMutex.lock(); for(QueueItem* anItem = myFront; anItem != NULL;) { StHandle aSubItem = anItem->myItem; - if(anItem->myItem->myTimeEnd < thePTS) { + if(anItem->myItem->TimeEnd < thePTS) { // remove outdated items myFront = myFront->myNext; delete anItem; anItem = myFront; - } else if(anItem->myItem->myTimeStart <= thePTS) { + } else if(anItem->myItem->TimeStart <= thePTS) { // pop the item StHandle aSubItem = anItem->myItem; myFront = myFront->myNext; diff -Nru sview-13.10/StImageViewer/StImageLoader.cpp sview-14.01/StImageViewer/StImageLoader.cpp --- sview-13.10/StImageViewer/StImageLoader.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageLoader.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,17 +19,30 @@ #include "StImageLoader.h" #include "StImagePluginInfo.h" #include "StImageViewerStrings.h" +#include "StImageViewerGUI.h" -#include -#include #include +using namespace StImageViewerStrings; + const char* StImageLoader::ST_IMAGES_MIME_STRING = ST_IMAGE_PLUGIN_MIME_CHAR; -static SV_THREAD_FUNCTION threadFunction(void* inStImageLoader) { - StImageLoader* stImageLoader = (StImageLoader* )inStImageLoader; - stImageLoader->mainLoop(); - return SV_THREAD_RETURN 0; +namespace { + + static StString formatError(const StString& theFilePath, + const StString& theImgLibDescr) { + StString aFileName, aFolderName; + StFileNode::getFolderAndFile(theFilePath, aFolderName, aFileName); + ST_ERROR_LOG("Can not load image file \"" + theFilePath + "\" (" + theImgLibDescr + ')'); + return StString("Can not load image file:\n\"") + aFileName + "\"\n" + theImgLibDescr; + } + + static SV_THREAD_FUNCTION threadFunction(void* theImageLoader) { + StImageLoader* anImageLoader = (StImageLoader* )theImageLoader; + anImageLoader->mainLoop(); + return SV_THREAD_RETURN 0; + } + } StImageLoader::StImageLoader(const StImageFile::ImageClass theImageLib, @@ -44,124 +57,231 @@ myTextureQueue(theTextureQueue), myMsgQueue(theMsgQueue), myImageLib(theImageLib), - myToSave(StImageFile::ST_TYPE_NONE), - myToQuit(false) { + myAction(Action_NONE) { myPlayList.setExtensions(myMimeList.getExtensionsList()); myThread = new StThread(threadFunction, (void* )this); } StImageLoader::~StImageLoader() { - myToQuit = true; + myAction = Action_Quit; myLoadNextEvent.set(); // stop the thread myThread->wait(); myThread.nullify(); - ///ST_DEBUG_LOG_AT("Destructor done"); } void StImageLoader::setCompressMemory(const bool theToCompress) { myTextureQueue->setCompressMemory(theToCompress); } -static StString formatError(const StString& theFilePath, const StString& theImgLibDescr) { - StString aFileName, aFolderName; - StFileNode::getFolderAndFile(theFilePath, aFolderName, aFileName); - ST_ERROR_LOG("Can not load image file \"" + theFilePath + "\" (" + theImgLibDescr + ')'); - return StString("Can not load image file:\n\"") + aFileName + "\"\n" + theImgLibDescr; -} - void StImageLoader::processLoadFail(const StString& theErrorDesc) { myMsgQueue->pushError(theErrorDesc); myTextureQueue->setConnectedStream(false); myTextureQueue->clear(); } +void StImageLoader::metadataFromExif(const StHandle& theDir, + StHandle& theInfo) { + if(theDir.isNull()) { + return; + } + + if(!theDir->CameraMaker.isEmpty()) { + StDictEntry& anEntry = theInfo->Info.addChange("Exif.Image.Make"); + anEntry.changeValue() = theDir->CameraMaker; + } + if(!theDir->CameraModel.isEmpty()) { + StDictEntry& anEntry = theInfo->Info.addChange("Exif.Image.Model"); + anEntry.changeValue() = theDir->CameraModel; + } + if(!theDir->UserComment.isEmpty()) { + StDictEntry& anEntry = theInfo->Info.addChange("Exif.UserComment"); + anEntry.changeValue() = theDir->UserComment; + } + + for(size_t anExifId = 0; anExifId < theDir->SubDirs.size(); ++anExifId) { + metadataFromExif(theDir->SubDirs[anExifId], theInfo); + } +} + bool StImageLoader::loadImage(const StHandle& theSource, StHandle& theParams) { - StString fileToLoadPath = theSource->getPath(); - StImageFile::ImageType anImgType = StImageFile::guessImageType(fileToLoadPath, theSource->getMIME()); + const StString aFilePath = theSource->getPath(); + const StImageFile::ImageType anImgType = StImageFile::guessImageType(aFilePath, theSource->getMIME()); - StHandle stImageL = StImageFile::create(myImageLib, anImgType); - StHandle stImageR = StImageFile::create(myImageLib, anImgType); - if(stImageL.isNull() || stImageR.isNull()) { + StHandle anImageL = StImageFile::create(myImageLib, anImgType); + StHandle anImageR = StImageFile::create(myImageLib, anImgType); + if(anImageL.isNull() + || anImageR.isNull()) { processLoadFail("No any image library was found!"); return false; } + StHandle anImgInfo = new StImageInfo(); + anImgInfo->Id = theParams; + anImgInfo->Path = aFilePath; + anImgInfo->ImageType = anImgType; + anImgInfo->IsSavable = false; + + StString aTitleString, aFolder; + if(theSource->size() >= 2) { + StString aTitleString2; + StFileNode::getFolderAndFile(theSource->getValue(0)->getPath(), aFolder, aTitleString); + StFileNode::getFolderAndFile(theSource->getValue(1)->getPath(), aFolder, aTitleString2); + anImgInfo->Info.add(StArgument(tr(INFO_FILE_NAME), + aTitleString + " " + tr(INFO_LEFT) + "\n" + + aTitleString2 + " " + tr(INFO_RIGHT))); + } else { + StFileNode::getFolderAndFile(aFilePath, aFolder, aTitleString); + anImgInfo->Info.add(StArgument(tr(INFO_FILE_NAME), aTitleString)); + } + StTimer aLoadTimer(true); StFormatEnum aSrcFormatCurr = mySrcFormat; if(anImgType == StImageFile::ST_TYPE_MPO - || anImgType == StImageFile::ST_TYPE_JPEG) { + || anImgType == StImageFile::ST_TYPE_JPEG + || anImgType == StImageFile::ST_TYPE_JPS) { // special procedure to divide MPO (Multi Picture Object) - StJpegParser aJpegParser; + StJpegParser aParser; double anHParallax = 0.0; // parallax in percents - if(!aJpegParser.read(fileToLoadPath)) { - processLoadFail(StString("Can not read the file \"") + fileToLoadPath + '\"'); + const bool isParsed = aParser.readFile(aFilePath); + + StHandle anImg1, anImg2; + size_t aMaxSizeX = 0; + size_t aMaxSizeY = 0; + for(StHandle anImgIter = aParser.getImage(0); !anImgIter.isNull(); + anImgIter = anImgIter->Next) { + aMaxSizeX = stMax(aMaxSizeX, anImgIter->SizeX); + aMaxSizeY = stMax(aMaxSizeY, anImgIter->SizeY); + } + + int anImgCounter = 1; + for(StHandle anImgIter = aParser.getImage(0); !anImgIter.isNull(); + anImgIter = anImgIter->Next, ++anImgCounter) { + if(anImgIter->SizeX == aMaxSizeX + && anImgIter->SizeY == aMaxSizeY) { + if(anImg1.isNull()) { + anImg1 = anImgIter; + continue; + } else if(anImg2.isNull()) { + anImg2 = anImgIter; + continue; + } + } + anImgInfo->Info.add(StArgument(tr(INFO_DIMENSIONS) + (" (") + anImgCounter + ")", + StString() + anImgIter->SizeX + " x " + anImgIter->SizeY)); + } + + // copy metadata + if(!aParser.getComment().isEmpty()) { + StDictEntry& anEntry = anImgInfo->Info.addChange("Jpeg.Comment"); + anEntry.changeValue() = aParser.getComment(); + } + if(!aParser.getJpsComment().isEmpty()) { + StDictEntry& anEntry = anImgInfo->Info.addChange("Jpeg.JpsComment"); + anEntry.changeValue() = aParser.getJpsComment(); + } + if(!anImg1.isNull()) { + for(size_t anExifId = 0; anExifId < anImg1->Exif.size(); ++anExifId) { + metadataFromExif(anImg1->Exif[anExifId], anImgInfo); + } + const StString aTime = anImg1->getDateTime(); + if(!aTime.isEmpty()) { + StDictEntry& anEntry = anImgInfo->Info.addChange("Exif.Image.DateTime"); + anEntry.changeValue() = aTime; + } + } + if(mySrcFormat == ST_V_SRC_AUTODETECT) { + if(aParser.getSrcFormat() != ST_V_SRC_AUTODETECT) { + aSrcFormatCurr = aParser.getSrcFormat(); + } else if(anImgType == StImageFile::ST_TYPE_JPS) { + aSrcFormatCurr = ST_V_SRC_SIDE_BY_SIDE; + } + } + + //aParser.fillDictionary(anImgInfo->Info, true); + if(!isParsed) { + processLoadFail(StString("Can not read the file \"") + aFilePath + '\"'); return false; } - // read image from memory - StHandle anImg1 = aJpegParser.getImage(0); + anImgInfo->IsSavable = anImg2.isNull(); + anImgInfo->SrcFormat = aParser.getSrcFormat(); + if(anImgInfo->SrcFormat != ST_V_SRC_AUTODETECT) { + StDictEntry& anEntry = anImgInfo->Info.addChange("Jpeg.JpsStereo"); + anEntry.changeValue() = tr(StImageViewerGUI::trSrcFormatId(anImgInfo->SrcFormat)); + } - StJpegParser::Orient anOrient = anImg1->getOrientation(); + // read image from memory + const StJpegParser::Orient anOrient = anImg1->getOrientation(); theParams->setZRotateZero((GLfloat )StJpegParser::getRotationAngle(anOrient)); anImg1->getParallax(anHParallax); - if(!stImageL->load(fileToLoadPath, StImageFile::ST_TYPE_JPEG, - (uint8_t* )anImg1->myData, (int )anImg1->myLength) - && !stImageL->load(fileToLoadPath, StImageFile::ST_TYPE_JPEG, - (uint8_t* )aJpegParser.getData(), (int )aJpegParser.getDataSize())) { - processLoadFail(formatError(fileToLoadPath, stImageL->getState())); + if(!anImageL->load(aFilePath, StImageFile::ST_TYPE_JPEG, + (uint8_t* )anImg1->Data, (int )anImg1->Length) + && !anImageL->load(aFilePath, StImageFile::ST_TYPE_JPEG, + (uint8_t* )aParser.getBuffer(), (int )aParser.getSize())) { + processLoadFail(formatError(aFilePath, anImageL->getState())); return false; } - StHandle anImg2 = aJpegParser.getImage(1); - if(!anImg2.isNull() && anImgType == StImageFile::ST_TYPE_MPO) { + if(!anImg2.isNull()) { // read image from memory anImg2->getParallax(anHParallax); // in MPO parallax generally stored ONLY in second frame - if(!stImageR->load(fileToLoadPath, StImageFile::ST_TYPE_JPEG, - (uint8_t* )anImg2->myData, (int )anImg2->myLength)) { - processLoadFail(formatError(fileToLoadPath, stImageR->getState())); - stImageL->close(); - stImageL->nullify(); + if(!anImageR->load(aFilePath, StImageFile::ST_TYPE_JPEG, + (uint8_t* )anImg2->Data, (int )anImg2->Length)) { + processLoadFail(formatError(aFilePath, anImageR->getState())); + anImageL->close(); + anImageL->nullify(); return false; } // convert percents to pixels - theParams->setSeparationNeutral(GLint(anHParallax * stImageR->getSizeX() * 0.01)); + const GLint aParallaxPx = GLint(anHParallax * anImageR->getSizeX() * 0.01); + if(aParallaxPx != 0) { + StDictEntry& anEntry = anImgInfo->Info.addChange("Exif.Fujifilm.Parallax"); + anEntry.changeValue() = StString(anHParallax); + } + theParams->setSeparationNeutral(aParallaxPx); } else if(anImgType == StImageFile::ST_TYPE_MPO) { - ST_DEBUG_LOG("MPO image \"" + fileToLoadPath + "\" is invalid!"); + ST_DEBUG_LOG("MPO image \"" + aFilePath + "\" is invalid!"); } } else if(theSource->size() >= 2) { - StString fileToLoadPathLeft = theSource->getValue(0)->getPath(); - StString fileToLoadPathRight = theSource->getValue(1)->getPath(); + const StString aFilePathLeft = theSource->getValue(0)->getPath(); + const StString aFilePathRight = theSource->getValue(1)->getPath(); // loading image with format autodetection - if(!stImageL->load(fileToLoadPathLeft)) { - processLoadFail(formatError(fileToLoadPathLeft, stImageL->getState())); + if(!anImageL->load(aFilePathLeft)) { + processLoadFail(formatError(aFilePathLeft, anImageL->getState())); return false; } - if(!stImageR->load(fileToLoadPathRight)) { - processLoadFail(formatError(fileToLoadPathRight, stImageR->getState())); - stImageL->close(); - stImageL->nullify(); + if(!anImageR->load(aFilePathRight)) { + processLoadFail(formatError(aFilePathRight, anImageR->getState())); + anImageL->close(); + anImageL->nullify(); return false; } } else { - if(mySrcFormat == ST_V_SRC_AUTODETECT && (anImgType == StImageFile::ST_TYPE_JPS || anImgType == StImageFile::ST_TYPE_PNS)) { - aSrcFormatCurr = ST_V_SRC_SIDE_BY_SIDE; - } - if(!stImageL->load(fileToLoadPath, anImgType)) { - processLoadFail(formatError(fileToLoadPath, stImageL->getState())); + if(!anImageL->load(aFilePath, anImgType)) { + processLoadFail(formatError(aFilePath, anImageL->getState())); return false; } + + anImgInfo->SrcFormat = anImageL->getFormat(); + if(mySrcFormat == ST_V_SRC_AUTODETECT) { + aSrcFormatCurr = anImageL->getFormat(); + } + if(aSrcFormatCurr == ST_V_SRC_AUTODETECT + && anImgType == StImageFile::ST_TYPE_PNS) { + aSrcFormatCurr = ST_V_SRC_SIDE_BY_SIDE; + } } const double aLoadTimeMSec = aLoadTimer.getElapsedTimeInMilliSec(); #ifdef __ST_DEBUG__ - if(!stImageL->isNull()) { - ST_DEBUG_LOG(stImageL->getState()); + if(!anImageL->isNull()) { + ST_DEBUG_LOG(anImageL->getState()); } - if(!stImageR->isNull()) { - ST_DEBUG_LOG(stImageR->getState()); + if(!anImageR->isNull()) { + ST_DEBUG_LOG(anImageR->getState()); } #endif @@ -171,45 +291,45 @@ } myTextureQueue->setConnectedStream(true); - if(!stImageR->isNull()) { - myTextureQueue->push(*stImageL, *stImageR, theParams, ST_V_SRC_SEPARATE_FRAMES, 0.0); + if(!anImageR->isNull()) { + myTextureQueue->push(*anImageL, *anImageR, theParams, ST_V_SRC_SEPARATE_FRAMES, 0.0); } else { - myTextureQueue->push(*stImageL, *stImageR, theParams, aSrcFormatCurr, 0.0); + myTextureQueue->push(*anImageL, *anImageR, theParams, aSrcFormatCurr, 0.0); } - StHandle anImgInfo = new StImageInfo(); - anImgInfo->myId = theParams; - - StString aTitleString, aFolder; - if(theSource->size() >= 2) { - StFileNode::getFolderAndFile(theSource->getValue(0)->getPath(), aFolder, aTitleString); - anImgInfo->myInfo.add(StArgument("Name (L)", aTitleString)); - StFileNode::getFolderAndFile(theSource->getValue(1)->getPath(), aFolder, aTitleString); - anImgInfo->myInfo.add(StArgument("Name (R)", aTitleString)); - } else { - StFileNode::getFolderAndFile(fileToLoadPath, aFolder, aTitleString); - anImgInfo->myInfo.add(StArgument("Name", aTitleString)); - } - if(!stImageR->isNull()) { - anImgInfo->myInfo.add(StArgument("Dimensions (L)", StString() + stImageL->getSizeX() - + " x " + stImageL->getSizeY())); - anImgInfo->myInfo.add(StArgument("Dimensions (R)", StString() + stImageR->getSizeX() - + " x " + stImageR->getSizeY())); - anImgInfo->myInfo.add(StArgument("Color Model (L)", stImageL->formatImgColorModel())); - anImgInfo->myInfo.add(StArgument("Color Model (R)", stImageR->formatImgColorModel())); + if(!stAreEqual(anImageL->getPixelRatio(), 1.0f, 0.001f)) { + anImgInfo->Info.add(StArgument(tr(INFO_PIXEL_RATIO), + StString(anImageL->getPixelRatio()))); + } + const StString aModelL = anImageL->formatImgColorModel(); + if(!anImageR->isNull()) { + anImgInfo->Info.add(StArgument(tr(INFO_DIMENSIONS), StString() + + anImageL->getSizeX() + " x " + anImageL->getSizeY() + " " + tr(INFO_LEFT) + "\n" + + anImageR->getSizeX() + " x " + anImageR->getSizeY() + " " + tr(INFO_RIGHT))); + const StString aModelR = anImageR->formatImgColorModel(); + if(aModelL == aModelR) { + anImgInfo->Info.add(StArgument(tr(INFO_COLOR_MODEL), aModelL)); + } else { + anImgInfo->Info.add(StArgument(tr(INFO_COLOR_MODEL), + aModelL + " " + tr(INFO_LEFT) + "\n" + + aModelR + " " + tr(INFO_RIGHT))); + } } else { - anImgInfo->myInfo.add(StArgument("Dimensions", StString() + stImageL->getSizeX() - + " x " + stImageL->getSizeY())); - anImgInfo->myInfo.add(StArgument("Color Model", stImageL->formatImgColorModel())); + anImgInfo->Info.add(StArgument(tr(INFO_DIMENSIONS), StString() + + anImageL->getSizeX() + " x " + anImageL->getSizeY())); + anImgInfo->Info.add(StArgument(tr(INFO_COLOR_MODEL), + aModelL)); } - anImgInfo->myInfo.add(StArgument("Load time", StString(aLoadTimeMSec) + " msec")); + anImgInfo->Info.add(StArgument(tr(INFO_LOAD_TIME), StString(aLoadTimeMSec) + " " + tr(INFO_TIME_MSEC))); + myLock.lock(); myImgInfo = anImgInfo; + myLock.unlock(); // clean up - close opened files and reset memory - stImageL->close(); - stImageL->nullify(); - stImageR->close(); - stImageR->nullify(); + anImageL->close(); + anImageL->nullify(); + anImageR->close(); + anImageR->nullify(); myTextureQueue->stglSwapFB(0); @@ -221,52 +341,54 @@ bool StImageLoader::saveImage(const StHandle& theSource, const StHandle& theParams, StImageFile::ImageType theImgType) { - if(theParams.isNull() || theParams.isNull() || theImgType == StImageFile::ST_TYPE_NONE) { - stInfo(myLangMap->getValue(StImageViewerStrings::DIALOG_NOTHING_TO_SAVE)); + if(theParams.isNull() + || theImgType == StImageFile::ST_TYPE_NONE) { + myMsgQueue->pushError(tr(DIALOG_NOTHING_TO_SAVE)); return false; } - int result = StGLTextureQueue::SNAPSHOT_NO_NEW; - StImage dataLeft; - StImage dataRight; + int aResult = StGLTextureQueue::SNAPSHOT_NO_NEW; + StImage aDataLeft, aDataRight; if(!theParams->isSwapLR()) { - result = getSnapshot(&dataLeft, &dataRight, true); + aResult = getSnapshot(&aDataLeft, &aDataRight, true); } else { - result = getSnapshot(&dataRight, &dataLeft, true); + aResult = getSnapshot(&aDataRight, &aDataLeft, true); } - if(result == StGLTextureQueue::SNAPSHOT_NO_NEW || dataLeft.isNull()) { - stInfo(myLangMap->getValue(StImageViewerStrings::DIALOG_NO_SNAPSHOT)); + if(aResult == StGLTextureQueue::SNAPSHOT_NO_NEW + || aDataLeft.isNull()) { + myMsgQueue->pushInfo(tr(DIALOG_NO_SNAPSHOT)); return false; } - StHandle dataResult = StImageFile::create(myImageLib); - if(dataResult.isNull()) { + StHandle aDataResult = StImageFile::create(myImageLib); + if(aDataResult.isNull()) { myMsgQueue->pushError(stCString("No any image library was found!")); return false; } - bool toSaveStereo = !dataRight.isNull(); - if(toSaveStereo && dataResult->initSideBySide(dataLeft, dataRight, - theParams->getSeparationDx(), - theParams->getSeparationDy())) { - dataLeft.nullify(); - dataRight.nullify(); + const bool toSaveStereo = !aDataRight.isNull(); + if(toSaveStereo + && aDataResult->initSideBySide(aDataLeft, aDataRight, + theParams->getSeparationDx(), + theParams->getSeparationDy())) { + aDataLeft.nullify(); + aDataRight.nullify(); } else { - dataResult->initWrapper(dataLeft); + aDataResult->initWrapper(aDataLeft); } const StString& aTitle = myLangMap->getValue(StImageViewerStrings::DIALOG_SAVE_SNAPSHOT); - StMIMEList filter; - StString saveExt; + StMIMEList aFilter; + StString aSaveExt; if(toSaveStereo) { switch(theImgType) { case StImageFile::ST_TYPE_PNG: - saveExt = ST_PNS_EXT; - filter.add(StMIME(ST_PNS_MIME, saveExt, ST_PNS_DESC)); + aSaveExt = ST_PNS_EXT; + aFilter.add(StMIME(ST_PNS_MIME, aSaveExt, ST_PNS_DESC)); break; case StImageFile::ST_TYPE_JPEG: - saveExt = ST_JPS_EXT; - filter.add(StMIME(ST_JPS_MIME, saveExt, ST_JPS_DESC)); + aSaveExt = ST_JPS_EXT; + aFilter.add(StMIME(ST_JPS_MIME, aSaveExt, ST_JPS_DESC)); break; default: return false; @@ -274,28 +396,28 @@ } else { switch(theImgType) { case StImageFile::ST_TYPE_PNG: - saveExt = ST_PNG_EXT; - filter.add(StMIME(ST_PNG_MIME, saveExt, ST_PNG_DESC)); + aSaveExt = ST_PNG_EXT; + aFilter.add(StMIME(ST_PNG_MIME, aSaveExt, ST_PNG_DESC)); break; case StImageFile::ST_TYPE_JPEG: - saveExt = ST_JPG_EXT; - filter.add(StMIME(ST_JPG_MIME, saveExt, ST_JPEG_DESC)); + aSaveExt = ST_JPG_EXT; + aFilter.add(StMIME(ST_JPG_MIME, aSaveExt, ST_JPEG_DESC)); break; default: return false; } } - StString fileToSave; - if(StFileNode::openFileDialog(theSource->getFolderPath(), aTitle, filter, fileToSave, true)) { - if(StFileNode::getExtension(fileToSave) != saveExt) { - fileToSave += StString('.') + saveExt; + StString aFileToSave; + if(StFileNode::openFileDialog(theSource->getFolderPath(), aTitle, aFilter, aFileToSave, true)) { + if(StFileNode::getExtension(aFileToSave) != aSaveExt) { + aFileToSave += StString('.') + aSaveExt; } - bool toSave = !StFileNode::isFileExists(fileToSave); + bool toSave = !StFileNode::isFileExists(aFileToSave); if(!toSave) { if(stQuestion("File already exists!\nOverride the file?")) { - toSave = StFileNode::removeFile(fileToSave); + toSave = StFileNode::removeFile(aFileToSave); if(!toSave) { myMsgQueue->pushError(stCString("Could not remove the file!")); return false; @@ -304,15 +426,16 @@ } if(toSave) { - ST_DEBUG_LOG("Save snapshot to the path '" + fileToSave + '\''); + ST_DEBUG_LOG("Save snapshot to the path '" + aFileToSave + '\''); StString strSaveState; - if(!dataResult->save(fileToSave, theImgType)) { + if(!aDataResult->save(aFileToSave, theImgType, + toSaveStereo ? ST_V_SRC_SIDE_BY_SIDE : ST_V_SRC_AUTODETECT)) { // TODO (Kirill Gavrilov#7) - myMsgQueue->pushError(dataResult->getState()); + myMsgQueue->pushError(aDataResult->getState()); return false; } - if(!dataResult->getState().isEmpty()) { - ST_DEBUG_LOG(dataResult->getState()); + if(!aDataResult->getState().isEmpty()) { + ST_DEBUG_LOG(aDataResult->getState()); } // TODO (Kirill Gavrilov#8) - update playlist (append new file) } @@ -320,27 +443,79 @@ return true; } +bool StImageLoader::saveImageInfo(const StHandle& theInfo) { + if(theInfo.isNull() + || theInfo->Path.isEmpty()) { + myMsgQueue->pushError(tr(DIALOG_NOTHING_TO_SAVE)); + return false; + } else if(theInfo->ImageType != StImageFile::ST_TYPE_JPEG + && theInfo->ImageType != StImageFile::ST_TYPE_JPS) { + myMsgQueue->pushError(stCString("Operation is unavailable for this image type")); + return false; + } + + StFormatEnum aSrcFormat = theInfo->Id->getSrcFormat(); + if(theInfo->Id->isSwapLR()) { + aSrcFormat = st::formatReversed(aSrcFormat); + } + + StJpegParser aParser; + if(!aParser.readFile(theInfo->Path)) { + myMsgQueue->pushError(tr(DIALOG_NOTHING_TO_SAVE)); + return false; + } + + aParser.setupJps(aSrcFormat); + if(!aParser.saveFile(theInfo->Path)) { + myMsgQueue->pushError(StString("File can not be saved at path '") + theInfo->Path + "'!"); + return false; + } + return true; +} + void StImageLoader::mainLoop() { - StHandle aFileToLoad; + StHandle aFileToLoad; StHandle aFileParams; for(;;) { myLoadNextEvent.wait(); - if(myToQuit) { - // exit the loop - return; - } else if(myToSave != StImageFile::ST_TYPE_NONE) { - StImageFile::ImageType anImgType = myToSave; - myToSave = StImageFile::ST_TYPE_NONE; - myLoadNextEvent.reset(); - // save current image (set as current in playlist) - if(myPlayList.getCurrentFile(aFileToLoad, aFileParams)) { - saveImage(aFileToLoad, aFileParams, anImgType); + switch(myAction) { + case Action_Quit: { + // exit the loop + return; } - } else { - // load next image (set as current in playlist) - myLoadNextEvent.reset(); - if(myPlayList.getCurrentFile(aFileToLoad, aFileParams)) { - loadImage(aFileToLoad, aFileParams); + case Action_SaveJPEG: + case Action_SavePNG: { + StImageFile::ImageType anImgType = (myAction == Action_SaveJPEG) + ? StImageFile::ST_TYPE_JPEG + : StImageFile::ST_TYPE_PNG; + myAction = Action_NONE; + myLoadNextEvent.reset(); + // save current image (set as current in playlist) + if(myPlayList.getCurrentFile(aFileToLoad, aFileParams)) { + saveImage(aFileToLoad, aFileParams, anImgType); + } + break; + } + case Action_SaveInfo: { + myLock.lock(); + StHandle anInfo = myInfoToSave; + myInfoToSave.nullify(); + myAction = Action_NONE; + myLock.unlock(); + myLoadNextEvent.reset(); + if(!saveImageInfo(anInfo)) { + break; + } + // re-load image file + } + case Action_NONE: + default: { + // load next image (set as current in playlist) + myLoadNextEvent.reset(); + if(myPlayList.getCurrentFile(aFileToLoad, aFileParams)) { + loadImage(aFileToLoad, aFileParams); + } + break; } } } diff -Nru sview-13.10/StImageViewer/StImageLoader.h sview-14.01/StImageViewer/StImageLoader.h --- sview-13.10/StImageViewer/StImageLoader.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageLoader.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,16 +24,23 @@ #include #include #include +#include #include +#include #include -class StLangMap; class StThread; struct StImageInfo { - StHandle myId; - StArgumentsMap myInfo; + StHandle Id; + StArgumentsMap Info; + StString Path; //!< file path + StImageFile::ImageType ImageType; //!< image type + StFormatEnum SrcFormat; //!< source format as stored in file metadata + bool IsSavable; //!< indicate that file can be saved without re-encoding + + StImageInfo() : ImageType(StImageFile::ST_TYPE_NONE), SrcFormat(ST_V_SRC_AUTODETECT), IsSavable(false) {} }; @@ -44,8 +51,20 @@ public: + enum Action { + Action_NONE, + Action_Quit, + Action_SaveJPEG, + Action_SavePNG, + Action_SaveInfo, + }; + + public: + static const char* ST_IMAGES_MIME_STRING; + public: + ST_LOCAL const StMIMEList& getMimeList() const { return myMimeList; } @@ -67,13 +86,34 @@ } ST_LOCAL void doSaveImageAs(const size_t theImgType) { - myToSave = StImageFile::ImageType(theImgType); + if(myAction == Action_Quit) { + return; + } + + if(theImgType == StImageFile::ST_TYPE_JPEG) { + myAction = Action_SaveJPEG; + } else if(theImgType == StImageFile::ST_TYPE_PNG) { + myAction = Action_SavePNG; + } else { + ST_ERROR_LOG("Attempt to save in unsupported image format " + theImgType); + return; + } + myLoadNextEvent.set(); + } + + ST_LOCAL void doSaveInfo(const StHandle& theInfo) { + myLock.lock(); + myInfoToSave = theInfo; + myAction = Action_SaveInfo; + myLock.unlock(); myLoadNextEvent.set(); } ST_LOCAL StHandle getFileInfo(const StHandle& theParams) const { + myLock.lock(); StHandle anInfo = myImgInfo; - return (!anInfo.isNull() && anInfo->myId == theParams) ? anInfo : NULL; + myLock.unlock(); + return (!anInfo.isNull() && anInfo->Id == theParams) ? anInfo : NULL; } ST_LOCAL StPlayList& getPlayList() { @@ -97,7 +137,7 @@ */ ST_LOCAL void setCompressMemory(const bool theToCompress); - public: //!< Signals + public: //! @name Signals struct { /** @@ -115,6 +155,8 @@ const StHandle& theParams, StImageFile::ImageType theImgType); + ST_LOCAL bool saveImageInfo(const StHandle& theInfo); + ST_LOCAL int getSnapshot(StImage* outDataLeft, StImage* outDataRight, bool isForce = false) { return myTextureQueue->getSnapshot(outDataLeft, outDataRight, isForce); } @@ -124,21 +166,32 @@ */ ST_LOCAL void processLoadFail(const StString& theErrorDesc); + /** + * Fill metadata map from EXIF. + */ + ST_LOCAL void metadataFromExif(const StHandle& theDir, + StHandle& theInfo); + + ST_LOCAL const StString& tr(const size_t theId) const { + return myLangMap->getValue(theId); + } + private: const StMIMEList myMimeList; StHandle myThread; //!< main loop thread StHandle myLangMap; //!< translations dictionary StPlayList myPlayList; //!< play list + mutable StMutex myLock; //!< lock to access not thread-safe properties StCondition myLoadNextEvent; StFormatEnum mySrcFormat; //!< target source format (auto-detect by default) StHandle myTextureQueue; //!< decoded frames queue StHandle myImgInfo; //!< info about currently loaded image + StHandle myInfoToSave; //!< modified info to be saved StHandle myMsgQueue; //!< messages queue volatile StImageFile::ImageClass myImageLib; - volatile StImageFile::ImageType myToSave; - volatile bool myToQuit; + volatile Action myAction; private: //! @name no copies, please diff -Nru sview-13.10/StImageViewer/StImageViewer.cbp sview-14.01/StImageViewer/StImageViewer.cbp --- sview-13.10/StImageViewer/StImageViewer.cbp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewer.cbp 2014-01-30 10:45:03.000000000 +0000 @@ -33,12 +33,7 @@ - - - - - - + @@ -69,12 +64,7 @@ - - - - - - + @@ -111,12 +101,7 @@ - - - - - - + @@ -148,12 +133,7 @@ - - - - - - + @@ -181,12 +161,7 @@ - - - - - - + @@ -213,12 +188,7 @@ - - - - - - + @@ -241,12 +211,7 @@ - - - - - - + @@ -271,12 +236,7 @@ - - - - - - + @@ -332,9 +292,18 @@ + + + + + + diff -Nru sview-13.10/StImageViewer/StImageViewer.cpp sview-14.01/StImageViewer/StImageViewer.cpp --- sview-13.10/StImageViewer/StImageViewer.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewer.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -164,6 +164,12 @@ anAction = new StActionIntValue(stCString("DoSrcSideBySide"), params.srcFormat, ST_V_SRC_SIDE_BY_SIDE); addAction(Action_SrcSideBySideRL, anAction, ST_VK_S); + anAction = new StActionIntSlot(stCString("DoFileInfo"), stSlot(this, &StImageViewer::doAboutImage), 0); + addAction(Action_FileInfo, anAction, ST_VK_I); + + anAction = new StActionIntSlot(stCString("DoSaveFileInfo"), stSlot(this, &StImageViewer::doSaveImageInfoBegin), 0); + addAction(Action_SaveFileInfo, anAction, ST_VK_I | ST_VF_SHIFT); + anAction = new StActionIntSlot(stCString("DoListFirst"), stSlot(this, &StImageViewer::doListFirst), 0); addAction(Action_ListFirst, anAction, ST_VK_HOME); @@ -462,6 +468,40 @@ myGUI->myImage->params.saturation->reset(); } +void StImageViewer::doSaveImageInfoBegin(const size_t ) { + if(!myFileInfo.isNull()) { + return; // already opened + } + + StHandle aFileNode; + StHandle aParams; + if(!getCurrentFile(aFileNode, aParams, myFileInfo) + || myFileInfo.isNull()) { + myMsgQueue->pushInfo(myLangMap->getValue(StImageViewerStrings::DIALOG_FILE_NOINFO)); + myFileInfo.nullify(); + return; + } else if(!myFileInfo->IsSavable) { + myMsgQueue->pushInfo(myLangMap->getValue(StImageViewerStrings::DIALOG_SAVE_INFO_UNSUPPORTED)); + myFileInfo.nullify(); + return; + } + + const StString aText = myLangMap->getValue(StImageViewerStrings::DIALOG_SAVE_INFO_QUESTION) + + "\n" + myFileInfo->Path; + + StInfoDialog* aDialog = new StInfoDialog(this, myGUI.access(), myLangMap->getValue(StImageViewerStrings::DIALOG_SAVE_INFO_TITLE), + myGUI->scale(512), myGUI->scale(256)); + aDialog->setText(aText); + + StGLButton* aSaveBtn = aDialog->addButton(myLangMap->getValue(StImageViewerStrings::BUTTON_SAVE_METADATA), true); + aSaveBtn->setUserData(1); + aSaveBtn->signals.onBtnClick += stSlot(this, &StImageViewer::doSaveImageInfo); + + aDialog->addButton(myLangMap->getValue(StImageViewerStrings::BUTTON_CANCEL)); + aDialog->setVisibility(true, true); + aDialog->stglInit(); +} + void StImageViewer::doDeleteFileBegin(const size_t ) { //if(!myFileToDelete.isNull()) { // return; @@ -474,12 +514,13 @@ return; } - const StString aText = StString("Do you really want to completely remove this file?\n") - + myFileToDelete->getPath() + ""; + const StString aText = myLangMap->getValue(StImageViewerStrings::DIALOG_DELETE_FILE_QUESTION) + + "\n" + myFileToDelete->getPath(); - StGLMessageBox* aDialog = new StGLMessageBox(myGUI.access(), aText, 512, 256); - aDialog->addButton("Delete", true, 96)->signals.onBtnClick += stSlot(this, &StImageViewer::doDeleteFileEnd); - aDialog->addButton("Cancel", false, 96); + StGLMessageBox* aDialog = new StGLMessageBox(myGUI.access(), myLangMap->getValue(StImageViewerStrings::DIALOG_DELETE_FILE_TITLE), + aText, myGUI->scale(512), myGUI->scale(256)); + aDialog->addButton(myLangMap->getValue(StImageViewerStrings::BUTTON_DELETE), true)->signals.onBtnClick += stSlot(this, &StImageViewer::doDeleteFileEnd); + aDialog->addButton(myLangMap->getValue(StImageViewerStrings::BUTTON_CANCEL), false); aDialog->setVisibility(true, true); aDialog->stglInit(); } @@ -520,9 +561,6 @@ } // file walk - case ST_VK_I: - myGUI->doAboutImage(0); - return; case ST_VK_MEDIA_PREV_TRACK: case ST_VK_BROWSER_BACK: doListPrev(); @@ -818,6 +856,21 @@ } } +void StImageViewer::doAboutImage(const size_t ) { + if(!myGUI.isNull()) { + myGUI->doAboutImage(0); + } +} + +void StImageViewer::doSaveImageInfo(const size_t theToSave) { + if(!myGUI.isNull() + && !myFileInfo.isNull() + && theToSave == 1) { + myLoader->doSaveInfo(myFileInfo); + } + myFileInfo.nullify(); +} + void StImageViewer::doListFirst(const size_t ) { if(myLoader->getPlayList().walkToFirst()) { myLoader->doLoadNext(); diff -Nru sview-13.10/StImageViewer/StImageViewer.h sview-14.01/StImageViewer/StImageViewer.h --- sview-13.10/StImageViewer/StImageViewer.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewer.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -93,6 +93,9 @@ ST_LOCAL void doOpen2FilesDialog(const size_t dummy = 0); ST_LOCAL void doSaveImageAs(const size_t theImgType) { myLoader->doSaveImageAs(theImgType); } + ST_LOCAL void doSaveImageInfo(const size_t theToSave); + ST_LOCAL void doSaveImageInfoBegin(const size_t dummy = 0); + ST_LOCAL void doAboutImage(const size_t dummy = 0); ST_LOCAL void doListFirst(const size_t dummy = 0); ST_LOCAL void doListPrev(const size_t dummy = 0); ST_LOCAL void doListNext(const size_t dummy = 0); @@ -159,6 +162,7 @@ Action_SrcMono, Action_SrcOverUnderLR, Action_SrcSideBySideRL, + Action_FileInfo, Action_ListFirst, Action_ListLast, Action_ListPrev, @@ -166,6 +170,7 @@ Action_SlideShow, Action_SavePng, Action_SaveJpeg, + Action_SaveFileInfo, Action_DeleteFile, Action_ImageAdjustReset, Action_StereoParamsBegin, @@ -200,6 +205,7 @@ StHandle myLoader; //!< main image loader class StHandle myUpdates; //!< check updates utility StHandle myFileToDelete; //!< file node for removal + StHandle myFileInfo; //!< file info for opened dialog StCondition myEventDialog; //!< event to prevent showing multiple open/save file dialogs StCondition myEventLoaded; //!< indicate that new file was open diff -Nru sview-13.10/StImageViewer/StImageViewer.rc sview-14.01/StImageViewer/StImageViewer.rc --- sview-13.10/StImageViewer/StImageViewer.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewer.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "Stereoscopic Image Viewer\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2007-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2007-2014 Kirill Gavrilov\000" VALUE "ProductName", "StImageViewer\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StImageViewer/StImageViewerGUI.cpp sview-14.01/StImageViewer/StImageViewerGUI.cpp --- sview-13.10/StImageViewer/StImageViewerGUI.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewerGUI.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,15 +26,17 @@ #include #include +#include #include #include #include #include #include -#include #include #include +#include #include +#include #include #include @@ -54,6 +56,10 @@ static const int ICON_WIDTH = 64; }; +StInfoDialog::~StInfoDialog() { + myPlugin->doSaveImageInfo(0); +} + /** * Create upper toolbar */ @@ -128,6 +134,7 @@ aMenuMedia->addItem(tr(MENU_MEDIA_OPEN_IMAGE), aMenuOpenImage); aMenuMedia->addItem(tr(MENU_MEDIA_SAVE_IMAGE_AS), aMenuSaveImage); aMenuMedia->addItem(tr(MENU_MEDIA_SRC_FORMAT), aMenuSrcFormat); + aMenuMedia->addItem(tr(MENU_MEDIA_FILE_INFO), myPlugin->getAction(StImageViewer::Action_FileInfo)); aMenuMedia->addItem("First File in folder") ->signals.onItemClick.connect(myPlugin, &StImageViewer::doListFirst); @@ -372,13 +379,13 @@ } void StImageViewerGUI::doAboutProgram(const size_t ) { - StGLMessageBox* aDialog = new StGLMessageBox(this, + StGLMessageBox* aDialog = new StGLMessageBox(this, "", tr(ABOUT_DPLUGIN_NAME) + '\n' + tr(ABOUT_VERSION) + ": " + StVersionInfo::getSDKVersionString() + " " + StThread::getArchString() + "\n \n" + tr(ABOUT_DESCRIPTION), scale(512), scale(300)); - aDialog->addButton("Close"); + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } @@ -388,38 +395,102 @@ } void StImageViewerGUI::doAboutSystem(const size_t ) { - StString aTitle = "System Info"; - StString anInfo = getContext().stglFullInfo(); - StString aString = aTitle + "\n\n \n" + anInfo; - StGLMessageBox* aDialog = new StGLMessageBox(this, aString, scale(512), scale(256)); - aDialog->addButton("Close"); + const StString aTitle = tr(ABOUT_SYSTEM); + StGLMessageBox* aDialog = new StGLMessageBox(this, aTitle, "", scale(512), scale(256)); + + StArgumentsMap anInfo; + getContext().stglFullInfo(anInfo); + StGLTable* aTable = new StGLTable(aDialog->getContent(), 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + aTable->setVisibility(true, true); + aTable->fillFromMap(anInfo, StGLVec3(1.0f, 1.0f, 1.0f), aDialog->getContent()->getRectPx().width(), aDialog->getContent()->getRectPx().width() / 2); + + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } void StImageViewerGUI::doAboutImage(const size_t ) { + StHandle& anExtraInfo = myPlugin->myFileInfo; + if(!anExtraInfo.isNull()) { + return; // already opened + } + StHandle aFileNode; StHandle aParams; - StHandle anExtraInfo; - StArrayList anInfoList(10); - if(myPlugin->getCurrentFile(aFileNode, aParams, anExtraInfo) && !anExtraInfo.isNull()) { - for(size_t aKeyIter = 0; aKeyIter < anExtraInfo->myInfo.size(); ++aKeyIter) { - const StArgument& aPair = anExtraInfo->myInfo.getFromIndex(aKeyIter); - anInfoList.add(aPair.getKey() + ": " + aPair.getValue() + "\n"); - } + if(!myPlugin->getCurrentFile(aFileNode, aParams, anExtraInfo) + || anExtraInfo.isNull()) { + StHandle aQueue = myPlugin->getMessagesQueue(); + aQueue->pushInfo(tr(DIALOG_FILE_NOINFO)); + anExtraInfo.nullify(); + return; } - StString aTitle = "Image Info"; - StString anInfo; - for(size_t anIter = 0; anIter < anInfoList.size(); ++anIter) { - anInfo += anInfoList[anIter]; - } - StString aString = aTitle + "\n\n \n" + anInfo; - StGLMessageBox* aDialog = new StGLMessageBox(this, aString, scale(512), scale(300)); - aDialog->addButton("Close"); + const StString aTitle = tr(DIALOG_FILE_INFO); + StInfoDialog* aDialog = new StInfoDialog(myPlugin, this, aTitle, scale(512), scale(300)); + + // translate known metadata tag names + for(size_t aMapIter = 0; aMapIter < anExtraInfo->Info.size(); ++aMapIter) { + StDictEntry& anEntry = anExtraInfo->Info.changeValue(aMapIter); + anEntry.changeName() = myLangMap->getValue(anEntry.getKey()); + } + const int aWidthMax = aDialog->getContent()->getRectPx().width(); + int aRowLast = (int )anExtraInfo->Info.size(); + const int aNbRowsMax = aRowLast + 2; + + StGLTable* aTable = new StGLTable(aDialog->getContent(), 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + aTable->setupTable(aNbRowsMax, 2); + aTable->setVisibility(true, true); + aTable->fillFromMap(anExtraInfo->Info, StGLVec3(1.0f, 1.0f, 1.0f), aWidthMax, aWidthMax / 2); + + // add stereoscopic format info + const StFormatEnum anActiveSrcFormat = aParams->isSwapLR() + ? st::formatReversed(aParams->getSrcFormat()) + : aParams->getSrcFormat(); + const int aTextMaxWidth = aWidthMax - (aTable->getMarginLeft() + aTable->getMarginRight()); + StGLTableItem& aSrcFormatItem = aTable->changeElement(aRowLast++, 0); aSrcFormatItem.setColSpan(2); + StGLTextArea* aSrcFormatText = new StGLTextArea(&aSrcFormatItem, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER)); + aSrcFormatText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aSrcFormatText->setText(StString("\n") + tr(BTN_SRC_FORMAT) + " " + trSrcFormat(anActiveSrcFormat)); + aSrcFormatText->setTextColor(StGLVec3(1.0f, 1.0f, 1.0f)); + aSrcFormatText->setVisibility(true, true); + aSrcFormatText->stglInitAutoHeightWidth(aTextMaxWidth); + + // warn about wrong/missing stereoscopic format information + StString aSrcInfo; + StGLVec3 anExtraColor(1.0f, 1.0f, 1.0f); + if(anExtraInfo->SrcFormat == ST_V_SRC_AUTODETECT + && anActiveSrcFormat != ST_V_SRC_MONO + && anActiveSrcFormat != ST_V_SRC_SEPARATE_FRAMES) { + aSrcInfo = tr(INFO_NO_SRCFORMAT); + anExtraColor = StGLVec3(1.0f, 1.0f, 0.8f); + } else if(anExtraInfo->SrcFormat != ST_V_SRC_AUTODETECT + && anExtraInfo->SrcFormat != anActiveSrcFormat) { + aSrcInfo = tr(INFO_WRONG_SRCFORMAT); + anExtraColor = StGLVec3(1.0f, 0.0f, 0.0f); + } + if(!aSrcInfo.isEmpty()) { + StGLTableItem& aTabItem = aTable->changeElement(aRowLast++, 0); aTabItem.setColSpan(2); + StGLTextArea* aText = new StGLTextArea(&aTabItem, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER)); + aText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aText->setText(aSrcInfo); + aText->setTextColor(anExtraColor); + aText->setVisibility(true, true); + aText->stglInitAutoHeightWidth(aTextMaxWidth); + } + aTable->updateLayout(); + + if(anExtraInfo->IsSavable + && !aSrcInfo.isEmpty()) { + StGLButton* aSaveBtn = aDialog->addButton(tr(BUTTON_SAVE_METADATA)); + aSaveBtn->setUserData(1); + aSaveBtn->signals.onBtnClick += stSlot(myPlugin, &StImageViewer::doSaveImageInfo); + } + + aDialog->addButton(tr(BUTTON_CLOSE), true); aDialog->setVisibility(true, true); aDialog->stglInit(); - } void StImageViewerGUI::doCheckUpdates(const size_t ) { @@ -515,6 +586,7 @@ myBtnNext(NULL), myBtnSwapLR(NULL), myBtnSrcFrmt(NULL), + myBtnPlayList(NULL), myBtnFull(NULL), // myFpsWidget(NULL), @@ -536,6 +608,10 @@ createUpperToolbar(); + myBtnPlayList = new StGLTextureButton(this, -aMargins.right() - scale(8 + 8 + 32), -aMargins.bottom() - scale(8), + StGLCorner(ST_VCORNER_BOTTOM, ST_HCORNER_RIGHT)); + myBtnPlayList->setTexturePath(iconTexture(stCString("playList"), scaleIcon(32))); + // fullscreen button myBtnFull = new StGLTextureButton(this, -aMargins.right() - scale(8), -aMargins.bottom() - scale(8), StGLCorner(ST_VCORNER_BOTTOM, ST_HCORNER_RIGHT)); @@ -575,6 +651,23 @@ }; +size_t StImageViewerGUI::trSrcFormatId(const StFormatEnum theSrcFormat) { + switch(theSrcFormat) { + case ST_V_SRC_MONO: return MENU_SRC_FORMAT_MONO; + case ST_V_SRC_SIDE_BY_SIDE: return MENU_SRC_FORMAT_CROSS_EYED; + case ST_V_SRC_PARALLEL_PAIR: return MENU_SRC_FORMAT_PARALLEL; + case ST_V_SRC_OVER_UNDER_RL: return MENU_SRC_FORMAT_OVERUNDER_RL; + case ST_V_SRC_OVER_UNDER_LR: return MENU_SRC_FORMAT_OVERUNDER_LR; + case ST_V_SRC_ROW_INTERLACE: return MENU_SRC_FORMAT_INTERLACED; + case ST_V_SRC_ANAGLYPH_G_RB: return MENU_SRC_FORMAT_ANA_RB; + case ST_V_SRC_ANAGLYPH_RED_CYAN: return MENU_SRC_FORMAT_ANA_RC; + case ST_V_SRC_ANAGLYPH_YELLOW_BLUE: return MENU_SRC_FORMAT_ANA_YB; + case ST_V_SRC_SEPARATE_FRAMES: return MENU_SRC_FORMAT_SEPARATE; + default: + case ST_V_SRC_AUTODETECT: return MENU_SRC_FORMAT_AUTO; + } +} + void StImageViewerGUI::setVisibility(const StPointD_t& theCursor, bool isMouseActive) { myIsVisibleGUI = isMouseActive @@ -591,7 +684,6 @@ myImage->setVisibility(true, true); if(myMenuRoot != NULL) { - ///myMenuRoot->setVisibility(myIsVisibleGUI, false); myMenuRoot->setVisibility(toShowAll, false); } @@ -602,6 +694,9 @@ child->setVisibility(toShowAll); } } + if(myBtnPlayList != NULL) { + //myBtnPlayList->setVisibility(myIsMinimalGUI || toShowAll); + } if(myBtnFull != NULL) { myBtnFull->setVisibility(myIsMinimalGUI || toShowAll); } @@ -617,30 +712,21 @@ } else if(::isPointIn(myBtnSwapLR, theCursor)) { size_t aLngId = myImage->params.swapLR->getValue() ? SWAP_LR_ON : SWAP_LR_OFF; myDescr->setText(tr(aLngId)); + } else if(::isPointIn(myBtnPlayList, theCursor)) { + myDescr->setText(tr(PLAYLIST)); } else if(::isPointIn(myBtnFull, theCursor)) { myDescr->setText(tr(FULLSCREEN)); } else if(::isPointIn(myBtnSrcFrmt, theCursor)) { - size_t aLngId = MENU_SRC_FORMAT_AUTO; StFormatEnum aSrcFormat = (StFormatEnum )myPlugin->params.srcFormat->getValue(); if(aSrcFormat == ST_V_SRC_AUTODETECT && !myImage->params.stereoFile.isNull()) { aSrcFormat = myImage->params.stereoFile->getSrcFormat(); } - switch(aSrcFormat) { - case ST_V_SRC_MONO: aLngId = MENU_SRC_FORMAT_MONO; break; - case ST_V_SRC_SIDE_BY_SIDE: aLngId = MENU_SRC_FORMAT_CROSS_EYED; break; - case ST_V_SRC_PARALLEL_PAIR: aLngId = MENU_SRC_FORMAT_PARALLEL; break; - case ST_V_SRC_OVER_UNDER_RL: aLngId = MENU_SRC_FORMAT_OVERUNDER_RL; break; - case ST_V_SRC_OVER_UNDER_LR: aLngId = MENU_SRC_FORMAT_OVERUNDER_LR; break; - case ST_V_SRC_ROW_INTERLACE: aLngId = MENU_SRC_FORMAT_INTERLACED; break; - case ST_V_SRC_ANAGLYPH_G_RB: aLngId = MENU_SRC_FORMAT_ANA_RB; break; - case ST_V_SRC_ANAGLYPH_RED_CYAN: aLngId = MENU_SRC_FORMAT_ANA_RC; break; - case ST_V_SRC_ANAGLYPH_YELLOW_BLUE: aLngId = MENU_SRC_FORMAT_ANA_YB; break; - case ST_V_SRC_SEPARATE_FRAMES: aLngId = MENU_SRC_FORMAT_SEPARATE; break; - default: - case ST_V_SRC_AUTODETECT: aLngId = MENU_SRC_FORMAT_AUTO; break; + if(!myImage->params.stereoFile.isNull() + && myImage->params.swapLR->getValue()) { + aSrcFormat = st::formatReversed(aSrcFormat); } - myDescr->setText(tr(BTN_SRC_FORMAT) + tr(aLngId)); + myDescr->setText(tr(BTN_SRC_FORMAT) + "\n" + trSrcFormat(aSrcFormat)); } else { myDescr->setVisibility(false, true); } @@ -718,15 +804,15 @@ anAboutText = StString() + "Plugin '" + myPlugin->getMainWindow()->getRendererId() + "' doesn't provide description"; } - StGLMessageBox* aDialog = new StGLMessageBox(this, anAboutText, scale(512), scale(300)); - aDialog->addButton("Close"); + StGLMessageBox* aDialog = new StGLMessageBox(this, "", anAboutText, scale(512), scale(300)); + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } void StImageViewerGUI::showUpdatesNotify() { - StGLMessageBox* aDialog = new StGLMessageBox(this, tr(UPDATES_NOTIFY)); - aDialog->addButton("Close"); + StGLMessageBox* aDialog = new StGLMessageBox(this, "", tr(UPDATES_NOTIFY)); + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } diff -Nru sview-13.10/StImageViewer/StImageViewerGUI.h sview-14.01/StImageViewer/StImageViewerGUI.h --- sview-13.10/StImageViewer/StImageViewerGUI.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewerGUI.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #ifndef __StImageViewerGUI_h_ #define __StImageViewerGUI_h_ -#include +#include #include #include #include @@ -38,6 +38,29 @@ class StWindow; /** + * Customized message box. + */ +class ST_LOCAL StInfoDialog : public StGLMessageBox { + + public: + + ST_LOCAL StInfoDialog(StImageViewer* thePlugin, + StGLWidget* theParent, + const StString& theTitle, + const int theWidth, + const int theHeight) + : StGLMessageBox(theParent, theTitle, "", theWidth, theHeight), + myPlugin(thePlugin) {} + + ST_LOCAL virtual ~StInfoDialog(); + + private: + + StImageViewer* myPlugin; + +}; + +/** * Root GUI widget for Image Viewer plugin. */ class StImageViewerGUI : public StGLRootWidget { @@ -79,6 +102,8 @@ ST_LOCAL void doAboutImage(const size_t ); + ST_LOCAL static size_t trSrcFormatId(const StFormatEnum theSrcFormat); + private: ST_LOCAL void createUpperToolbar(); @@ -87,6 +112,10 @@ return myLangMap->getValue(theId); } + ST_LOCAL const StString& trSrcFormat(const StFormatEnum theSrcFormat) const { + return tr(trSrcFormatId(theSrcFormat)); + } + private: //! @name menus creation routines ST_LOCAL void createMainMenu(); // Root (Main menu) @@ -136,6 +165,7 @@ StGLTextureButton* myBtnNext; StGLTextureButton* myBtnSwapLR; StGLWidget* myBtnSrcFrmt; + StGLTextureButton* myBtnPlayList; StGLTextureButton* myBtnFull; StGLFpsLabel* myFpsWidget; diff -Nru sview-13.10/StImageViewer/StImageViewerStrings.cpp sview-14.01/StImageViewer/StImageViewerStrings.cpp --- sview-13.10/StImageViewer/StImageViewerStrings.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewerStrings.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2013 Kirill Gavrilov + * Copyright © 2013-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,14 @@ namespace StImageViewerStrings { void loadDefaults(StLangMap& theStrings) { + theStrings(BUTTON_CLOSE, + "Close"); + theStrings(BUTTON_CANCEL, + "Cancel"); + theStrings(BUTTON_SAVE_METADATA, + "Save"); + theStrings(BUTTON_DELETE, + "Delete"); theStrings(MENU_MEDIA, "Media"); theStrings(MENU_VIEW, @@ -35,6 +43,8 @@ "Save Image As..."); theStrings(MENU_MEDIA_SRC_FORMAT, "Source stereo format"); + theStrings(MENU_MEDIA_FILE_INFO, + "File info"); theStrings(MENU_MEDIA_QUIT, "Quit"); theStrings(MENU_MEDIA_OPEN_IMAGE_1, @@ -124,11 +134,13 @@ theStrings(ABOUT_VERSION, "version"); theStrings(ABOUT_DESCRIPTION, - "Image viewer allows you to open stereoscopic images in formats JPEG, PNG, MPO and a lot of others.\n" - "(C) 2007-2013 Kirill Gavrilov \n" + "Image viewer allows you to open stereoscopic images in formats JPEG, PNG, MPO and others.\n" + "(C) 2007-2014 Kirill Gavrilov \n" "Official site: www.sview.ru\n" "\n" "This program distributed under GPL3.0"); + theStrings(ABOUT_SYSTEM, + "System Info"); theStrings(MENU_HELP_ABOUT, "About..."); theStrings(MENU_HELP_USERTIPS, @@ -167,11 +179,13 @@ "Previous image"); theStrings(IMAGE_NEXT, "Next image"); + theStrings(PLAYLIST, + "PlayList"); theStrings(FULLSCREEN, "Switch\n" "fullscreen/windowed"); theStrings(BTN_SRC_FORMAT, - "Source format:\n"); + "Source format:"); theStrings(UPDATES_NOTIFY, "A new version of sView is available on the official site www.sview.ru.\n" "Please update your program."); @@ -181,12 +195,69 @@ "Choose LEFT image file to open"); theStrings(DIALOG_OPEN_RIGHT, "Choose RIGHT image file to open"); + theStrings(DIALOG_FILE_INFO, + "Image Info"); + theStrings(DIALOG_FILE_NOINFO, + "Information is unavailable"); + theStrings(DIALOG_DELETE_FILE_TITLE, + "File deletion"); + theStrings(DIALOG_DELETE_FILE_QUESTION, + "Do you really want to completely remove this file?"); + theStrings(DIALOG_SAVE_INFO_TITLE, + "File metadata saving"); + theStrings(DIALOG_SAVE_INFO_QUESTION, + "Do you really want to save metadata to the file?"); + theStrings(DIALOG_SAVE_INFO_UNSUPPORTED, + "Metadata can be saved only into JPEG files."); theStrings(DIALOG_NOTHING_TO_SAVE, "Nothing to save!"); theStrings(DIALOG_NO_SNAPSHOT, "Snapshot not available!"); theStrings(DIALOG_SAVE_SNAPSHOT, "Choose location to save snapshot"); + + theStrings(INFO_LEFT, + "[left]"); + theStrings(INFO_RIGHT, + "[right]"); + theStrings(INFO_FILE_NAME, + "File name"); + theStrings(INFO_DIMENSIONS, + "Dimensions"); + theStrings(INFO_LOAD_TIME, + "Load time"); + theStrings(INFO_TIME_MSEC, + "msec"); + theStrings(INFO_PIXEL_RATIO, + "Pixel ratio"); + theStrings(INFO_COLOR_MODEL, + "Color model"); + theStrings(INFO_NO_SRCFORMAT, + "(does not stored in metadata)"); + theStrings(INFO_WRONG_SRCFORMAT, + "(does not match metadata)"); + + theStrings(METADATA_JPEG_COMMENT, + "JPEG comment"); + theStrings(METADATA_JPEG_JPSCOMMENT, + "JPS comment"); + + theStrings(METADATA_EXIF_MAKER, + "Camera maker"); + theStrings(METADATA_EXIF_MODEL, + "Camera model"); + theStrings(METADATA_EXIF_USERCOMMENT, + "User comment"); + theStrings(METADATA_EXIF_DATETIME, + "Image timestamp"); + + // define metadata keys + theStrings.addAlias("Jpeg.Comment", METADATA_JPEG_COMMENT); + theStrings.addAlias("Jpeg.JpsComment", METADATA_JPEG_JPSCOMMENT); + theStrings.addAlias("Exif.Image.Make", METADATA_EXIF_MAKER); + theStrings.addAlias("Exif.Image.Model", METADATA_EXIF_MODEL); + theStrings.addAlias("Exif.UserComment", METADATA_EXIF_USERCOMMENT); + theStrings.addAlias("Exif.Image.DateTime", METADATA_EXIF_DATETIME); } }; diff -Nru sview-13.10/StImageViewer/StImageViewerStrings.h sview-14.01/StImageViewer/StImageViewerStrings.h --- sview-13.10/StImageViewer/StImageViewerStrings.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/StImageViewerStrings.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StImageViewer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #ifndef __StImageViewerStrings_h_ #define __StImageViewerStrings_h_ -#include +#include class StLangMap; @@ -33,6 +33,7 @@ SWAP_LR_ON = 1003, BTN_SRC_FORMAT = 1004, IMAGE_OPEN = 1015, + PLAYLIST = 1028, FULLSCREEN = 1029, // Root -> Media menu @@ -40,6 +41,7 @@ MENU_MEDIA_OPEN_IMAGE = 1101, MENU_MEDIA_SAVE_IMAGE_AS = 1102, MENU_MEDIA_SRC_FORMAT = 1103, + MENU_MEDIA_FILE_INFO = 1104, MENU_MEDIA_QUIT = 1109, // Root -> Media menu -> Open File menu @@ -123,6 +125,13 @@ DIALOG_OPEN_FILE = 2000, DIALOG_OPEN_LEFT = 2001, DIALOG_OPEN_RIGHT = 2002, + DIALOG_FILE_INFO = 2003, + DIALOG_FILE_NOINFO = 2004, + DIALOG_DELETE_FILE_TITLE = 2005, + DIALOG_DELETE_FILE_QUESTION = 2006, + DIALOG_SAVE_INFO_TITLE = 2007, + DIALOG_SAVE_INFO_QUESTION = 2008, + DIALOG_SAVE_INFO_UNSUPPORTED = 2009, DIALOG_SAVE_SNAPSHOT = 2010, DIALOG_NOTHING_TO_SAVE = 2011, @@ -133,6 +142,32 @@ ABOUT_VERSION = 3001, ABOUT_DESCRIPTION = 3002, UPDATES_NOTIFY = 3003, + ABOUT_SYSTEM = 3004, + + BUTTON_CLOSE = 4000, + BUTTON_CANCEL = 4001, + BUTTON_SAVE_METADATA = 4006, + BUTTON_DELETE = 4007, + + // metadata keys + INFO_LEFT = 5000, + INFO_RIGHT = 5001, + INFO_FILE_NAME = 5002, + INFO_DIMENSIONS = 5003, + INFO_LOAD_TIME = 5004, + INFO_TIME_MSEC = 5005, + INFO_PIXEL_RATIO = 5006, + INFO_COLOR_MODEL = 5007, + INFO_NO_SRCFORMAT = 5008, + INFO_WRONG_SRCFORMAT = 5009, + + // metadata keys + METADATA_JPEG_COMMENT = 5100, + METADATA_JPEG_JPSCOMMENT = 5101, + METADATA_EXIF_MAKER = 5200, + METADATA_EXIF_MODEL = 5201, + METADATA_EXIF_USERCOMMENT = 5202, + METADATA_EXIF_DATETIME = 5203, }; diff -Nru sview-13.10/StImageViewer/lang/english/StImageViewer.lng sview-14.01/StImageViewer/lang/english/StImageViewer.lng --- sview-13.10/StImageViewer/lang/english/StImageViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/lang/english/StImageViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -6,13 +6,15 @@ 1001="Next image" 1002="Swap Left/Right" 1003="UnSwap Left/Right" -1004="Source format:\n" +1004="Stereoscopic format:" 1015="Open another image" +1028="Show/Hide playlist" 1029="Switch\nfullscreen/windowed" 1100="Media" 1101="Open Image..." 1102="Save Image As..." -1103="Source stereo format" +1103="Stereoscopic format" +1104="Image info" 1109="Quit" 1110="From One file" 1111="Left+Right files" @@ -63,7 +65,7 @@ 1504="Language" 1506="User tips" 1508="About system" -1509="Scale Interface" +1509="Interface Scale" 1520="Now" 1521="Each day" 1522="Each week" @@ -75,11 +77,39 @@ 1593="Force HiDPI 2X" 2000="Choose the image file to open" 2001="Choose LEFT image file to open" -2001="Choose RIGHT image file to open" +2002="Choose RIGHT image file to open" +2003="Image Info" +2004="Information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="File metadata saving" +2008="Do you really want to save metadata to the file?" +2009="Metadata can be saved only into JPEG files." 2010="Choose location to save snapshot" 2011="Nothing to save!" 2012="Snapshot not available!" 3000="sView - Image Viewer" 3001="version" -3002="Image viewer allows you to open stereoscopic images in formats JPEG, PNG, MPO and a lot of others.\n © 2007-2013 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" +3002="Image viewer allows you to open stereoscopic images in formats JPEG, PNG, MPO and a lot of others.\n © 2007-2014 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" 3003="A new version of sView is available on the official site www.sview.ru.\nPlease update your program." +3004="System Info" +4000="Close" +4001="Cancel" +4006="Save" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name" +5003="Dimensions" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Color model" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5100="JPEG Comment" +5101="JPS Comment" +5200="Camera Maker" +5201="Camera Model" +5202="User Comment" +5203="Image timestamp" diff -Nru sview-13.10/StImageViewer/lang/french/StImageViewer.lng sview-14.01/StImageViewer/lang/french/StImageViewer.lng --- sview-13.10/StImageViewer/lang/french/StImageViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/lang/french/StImageViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -6,13 +6,15 @@ 1001="Image Suivante" 1002="Gauche/Droite inversé" 1003="Gauche/Droite normal" -1004="Format entrée:\n" +1004="Format entrée:" 1015="Ouvre autre image" +1028="Affiche/Masque playlist" 1029="Bascule\Plein écran/fenêtre" 1100="Media" 1101="Ouvre Image..." 1102="Enregistre Image Sous.." 1103="Format Stéréo d'entrée" +1104="Image info" 1109="Quitter" 1110="Depuis un fichier" 1111="2 Fichiers Gauche+Droit" @@ -75,11 +77,39 @@ 1593="Force HiDPI 2X" 2000="Choix du fichier image à ouvrir" 2001="Choix du fichier image Gauche à ouvrir" -2001="Choix du fichier image Droite à ouvrir" +2002="Choix du fichier image Droite à ouvrir" +2003="Info de l'image" +2004="Information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="File metadata saving" +2008="Do you really want to save metadata to the file?" +2009="Metadata can be saved only into JPEG files." 2010="Emplacement de la capture écran" 2011="Rien à enregistrer!" 2012="Capture non disponible!" 3000="sView - Image Viewer" 3001="version" -3002="Image viewer vous permet d'ouvrir des images stéréoscopiques en formats JPEG, PNG, MPO.\n © 2007-2013 Kirill Gavrilov \nSite Officiel: www.sview.ru\n\nThis program distributed under GPL3.0" +3002="Image viewer vous permet d'ouvrir des images stéréoscopiques en formats JPEG, PNG, MPO.\n © 2007-2014 Kirill Gavrilov \nSite Officiel: www.sview.ru\n\nThis program distributed under GPL3.0" 3003="Une nouvelle version de sView est disponible sur le site officiel www.sview.ru.\nSVP, mettez votre programme à jour." +3004="System Info" +4000="Fermer" +4001="Cancel" +4006="Enregistre" +4007="Delete" +5000="[left]" +5001="[right]" +5002="Nom de fichier" +5003="Résolution" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Color model" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5100="JPEG commentaire" +5101="JPS commentaire" +5200="Camera Maker" +5201="Camera Model" +5202="User Comment" +5203="Image timestamp" diff -Nru sview-13.10/StImageViewer/lang/german/StImageViewer.lng sview-14.01/StImageViewer/lang/german/StImageViewer.lng --- sview-13.10/StImageViewer/lang/german/StImageViewer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StImageViewer/lang/german/StImageViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,115 @@ +German translation file for StImageViewer program +@author Kirill Gavrilov + +-------- +1000="Vorheriges Bild" +1001="Nachstes Bild" +1002="L&R Vertauschen An" +1003="L&R Vertauschen Aus" +1004="Quellformat:" +1015="Öffnen anderen Bild" +1028="Anzeigen/ausblenden Wiedergabeliste" +1029="Vollbildmodus/Fenstermodus" +1100="Medien" +1101="Bild öffnen..." +1102="Bild speichern als..." +1103="Quelle Stereo-Format" +1104="Bildinfo" +1109="Beenden" +1110="Einer Datei" +1111="Zwei Dateien" +1130="Autoerkennung" +1131="Mono" +1132="Schielend" +1133="Parallel Pair" +1134="Over/Under (R/L)" +1135="Over/Under (L/R)" +1136="Interlaced" +1137="Anaglyph Rot/Cyan" +1138="Anaglyph Grün/Rot+Blau" +1139="Anaglyph Gelb/Blau" +1142="2 Streams" +1200="Ansicht" +1201="Stereo-Ausgang" +1202="Vollbild" +1203="Zurückstellen" +1204="L&R Vertauschen" +1205="Seitenverhältnis" +1206="Glatte Filter" +1207="Bildeinstellung" +1208="Oberfläche" +1210="Stereo" +1211="Linke Bild" +1212="Rechte Bild" +1213="Parallel Pair" +1214="Cross-eyed" +1250="Quelle" +1251="Halten beim Neustart" +1260="Nearest" +1261="Linear" +1270="Auf Standardwerte zurücksetzen" +1271="Helligkeit" +1272="Sättigung" +1273="Gamma" +1280="Fläche" +1281="Kugel" +1282="Zylinder" +1400="Gerät ändern" +1401="Über Plugin..." +1402="FPS anzeigen" +1403="VSync" +1500="Hilfe" +1501="Über..." +1502="Nach Updates suchen" +1503="Lizenztext" +1504="Language" +1506="Anwendertipps" +1508="Über System" +1509="Maßstab Benutzeroberfläche" +1520="jetzt" +1521="täglich" +1522="wöchentlich" +1523="jährlich" +1524="nie" +1590="klein" +1591="normal" +1592="groß" +1593="erzwingen HiDPI 2X" +2000="Wählen die Bilddatei zu öffnen" +2001="Wählen die linke Bilddatei zu öffnen" +2002="Wählen die rechte Bilddatei zu öffnen" +2003="Bildinfo" +2004="Information is unavailable" +2005="Löschen von Dateien" +2006="Do you really want to completely remove this file?" +2007="File metadata saving" +2008="Do you really want to save metadata to the file?" +2009="Metadata can be saved only into JPEG files." +2010="Wählen einen Speicherort für Schnappschuss" +2011="Nichts zu speichern!" +2012="Schnappschuss ist nicht verfügbar!" +3000="sView - Image Viewer" +3001="Version" +3002="Image viewer allows you to open stereoscopic images in formats JPEG, PNG, MPO and a lot of others.\n © 2007-2014 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" +3003="A new version of sView is available on the official site www.sview.ru.\nPlease update your program." +3004="System Info" +4000="Schließen" +4001="Cancel" +4006="Speichern" +4007="Delete" +5000="[linke]" +5001="[rechte]" +5002="Dateiname" +5003="Auflösung" +5004="Ladezeit" +5005="ms" +5006="Pixel ratio" +5007="Color model" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5100="JPEG Kommentar" +5101="JPS Kommentar" +5200="Camera Maker" +5201="Camera Model" +5202="User Kommentar" +5203="Image timestamp" diff -Nru sview-13.10/StImageViewer/lang/korean/StImageViewer.lng sview-14.01/StImageViewer/lang/korean/StImageViewer.lng --- sview-13.10/StImageViewer/lang/korean/StImageViewer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StImageViewer/lang/korean/StImageViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,116 @@ +Korean translation file for StImageViewer program +@author Kirill Gavrilov +@translator Kwon Daesuk + +-------- +1000="앞 이미지" +1001="다음 이미지" +1002="좌/우 변경" +1003="좌/우 복원" +1004="소스 포맷:" +1015="다른 이미지 열기" +1028="재생 목록 보이기/감추기" +1029="전환\n전체화면/윈도화면" +1100="미디어" +1101="이미지 열기..." +1102="다른 이름으로 저장..." +1103="입력 스테레오 형식" +1104="Image info" +1109="나가기" +1110="한 파일에서 열기" +1111="좌+우 파일들에서 열기" +1130="자동감지" +1131="모노" +1132="크로스-아이" +1133="패럴렐 페어" +1134="상/하(R/L)" +1135="상/하(L/R)" +1136="Interlaced" +1137="Anaglyph Red/Cyan" +1138="Anaglyph Green/Red+Blue" +1139="Anaglyph Yellow/Blue" +1142="2 Streams" +1200="View" +1201="입체 출력" +1202="전체 화면" +1203="리셋" +1204="좌/우 변경" +1205="화면 비율" +1206="Smooth Filter" +1207="이미지 보정" +1208="Surface" +1210="Stereo" +1211="Left View" +1212="Right View" +1213="패럴렐 페어" +1214="크로스-아이" +1250="Source" +1251="재시작시 유지" +1260="Nearest" +1261="Linear" +1270="기본값으로 리셋" +1271="밝기" +1272="Saturation" +1273="감마" +1280="Plane" +1281="Sphere" +1282="Cylinder" +1400="장치 변경" +1401="About Plugin..." +1402="Show FPS" +1403="VSync" +1500="도움말" +1501="이 프로그램에 대해..." +1502="업데이트 확인" +1503="라이선스 문서" +1504="언어 (Language)" +1506="사용자 팁" +1508="About system" +1509="Interface Scale" +1520="지금" +1521="매일Each day" +1522="매주Each week" +1523="매년Each year" +1524="하지 않음Never" +1590="작게 Small" +1591="보통Normal" +1592="크게 Big" +1593="HiDPI 2X로" +2000="열어볼 이미지 파일 선택" +2001="왼쪽 이미지 선택" +2002="오른쪽 이미지 선택" +2003="Image Info" +2004="Information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="File metadata saving" +2008="Do you really want to save metadata to the file?" +2009="Metadata can be saved only into JPEG files." +2010="스냅샷 저장 위치 선택" +2011="저장할 수 없음!" +2012="스냅샷이 없음!" +3000="sView - 이미지 뷰어" +3001="version" +3002="이 이미지 뷰어로 JPEG, PNG, MPO 외 다양한 양식의 스테레오 이미지를 볼 수 있습니다.\n © 2007-2014 Kirill Gavrilov \n 공식 사이트: www.sview.ru\n\n이 프로그램은 GPL3.0 하에 배포됩니다." +3003="sView 최신 버전은 공식 사이트 www.sview.ru에서 구할 수 있습니다.\n프로그램을 업데이트하세요." +3004="System Info" +4000="닫기" +4001="Cancel" +4006="Save" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name" +5003="Dimensions" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Color model" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5100="JPEG Comment" +5101="JPS Comment" +5200="Camera Maker" +5201="Camera Model" +5202="User Comment" +5203="Image timestamp" diff -Nru sview-13.10/StImageViewer/lang/korean/language.lng sview-14.01/StImageViewer/lang/korean/language.lng --- sview-13.10/StImageViewer/lang/korean/language.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StImageViewer/lang/korean/language.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1 @@ +한국어 \ No newline at end of file diff -Nru sview-13.10/StImageViewer/lang/russian/StImageViewer.lng sview-14.01/StImageViewer/lang/russian/StImageViewer.lng --- sview-13.10/StImageViewer/lang/russian/StImageViewer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StImageViewer/lang/russian/StImageViewer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -6,13 +6,15 @@ 1001="Следующая картинка" 1002="Поменять ракурсы местами" 1003="Отключить реверс ракурсов" -1004="Исходный формат:\n" +1004="Исходный стереоформат:" 1015="Открыть изображение" +1028="Отобразить/Скрыть\nсписок файлов" 1029="Оконный/Полноэкранный\nрежим просмотра" 1100="Медиа" 1101="Открыть изображение..." 1102="Сохранить как..." 1103="Исходный стереоформат" +1104="Информация о файле" 1109="Выход" 1110="Из одного файла" 1111="Левый+Правый файлы" @@ -75,11 +77,39 @@ 1593="Форсировать HiDPI 2X" 2000="Выберите картинку" 2001="Выберите файл с ЛЕВЫМ ракурсом" -2001="Выберите файл с ПРАВЫМ ракурсом" +2002="Выберите файл с ПРАВЫМ ракурсом" +2003="Информация о файле" +2004="Информация не доступна" +2005="Удаление файла" +2006="Вы действительно хотите\nудалить файл минуя корзину?" +2007="Сохранение метаданных" +2008="Вы действительно хотите\nсохранить метаданные в файл?" +2009="Сохранение метаданных доступно только для файлов JPEG." 2010="Выберите путь для сохранения картинки" 2011="Ничего не открыто!" 2012="Изображение недоступно для сохранения!" 3000="sView - программа для просмотра изображений" 3001="версия" -3002="Программа отображает стереоскопические изображения в форматах JPEG, PNG, MPO.\n © 2007-2013 Гаврилов Кирилл \nОфициальный сайт: www.sview.ru\n\nПрограмма распространяется на условиях GPL3.0" +3002="Программа отображает стереоскопические изображения в форматах JPEG, PNG, MPO.\n © 2007-2014 Гаврилов Кирилл \nОфициальный сайт: www.sview.ru\n\nПрограмма распространяется на условиях GPL3.0" 3003="Доступно обновление программы.\nВы можете загрузить новую версию с официального сайта www.sview.ru." +3004="Информация о системе" +4000="Закрыть" +4001="Отменить" +4006="Сохранить" +4007="Удалить" +5000="[левый]" +5001="[правый]" +5002="Имя файла" +5003="Разрешение" +5004="Время загрузки" +5005="мс" +5006="Пропорции пикселя" +5007="Color model" +5008="(информация отсутствует в метаданных)" +5009="(не соответствует метаданным)" +5100="JPEG комментарий" +5101="JPS комментарий" +5200="Производитель камеры" +5201="Модель камеры" +5202="Комментарий" +5203="Дата создания" diff -Nru sview-13.10/StMoviePlayer/StMoviePlayer.cbp sview-14.01/StMoviePlayer/StMoviePlayer.cbp --- sview-13.10/StMoviePlayer/StMoviePlayer.cbp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayer.cbp 2014-01-30 10:45:03.000000000 +0000 @@ -37,14 +37,7 @@ - - - - - - - - + @@ -80,14 +73,7 @@ - - - - - - - - + @@ -129,14 +115,7 @@ - - - - - - - - + @@ -173,14 +152,7 @@ - - - - - - - - + @@ -214,14 +186,7 @@ - - - - - - - - + @@ -253,14 +218,7 @@ - - - - - - - - + @@ -287,14 +245,7 @@ - - - - - - - - + @@ -322,14 +273,7 @@ - - - - - - - - + @@ -406,9 +350,18 @@ + + + + + + diff -Nru sview-13.10/StMoviePlayer/StMoviePlayer.cpp sview-14.01/StMoviePlayer/StMoviePlayer.cpp --- sview-13.10/StMoviePlayer/StMoviePlayer.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayer.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,9 @@ #include #include +#include #include +#include #include #include #include @@ -69,6 +71,8 @@ static const char ST_SETTING_SCALE_FORCE2X[] = "scale2X"; static const char ST_SETTING_SUBTITLES_SIZE[]= "subsSize"; static const char ST_SETTING_SUBTITLES_PARALLAX[] = "subsParallax"; + static const char ST_SETTING_SUBTITLES_PARSER[] = "subsParser"; + static const char ST_SETTING_SEARCH_SUBS[] = "toSearchSubs"; static const char ST_SETTING_FULLSCREEN[] = "fullscreen"; static const char ST_SETTING_VIEWMODE[] = "viewMode"; static const char ST_SETTING_STEREO_MODE[] = "viewStereoMode"; @@ -168,6 +172,8 @@ myEventDialog(false), myEventLoaded(false), mySeekOnLoad(-1.0), + myAudioOnLoad(-1), + mySubsOnLoad(-1), // myWebCtx(NULL), // @@ -200,6 +206,10 @@ 0.0f, // default value 1.0f, // incremental step 0.1f); // equality tolerance + params.ToSearchSubs = new StBoolParam(true); + params.SubtitlesParser = new StEnumParam(1, tr(MENU_SUBTITLES_PARSER)); + params.SubtitlesParser->changeValues().add(tr(MENU_SUBTITLES_PLAIN_TEXT)); + params.SubtitlesParser->changeValues().add(tr(MENU_SUBTITLES_LITE_HTML)); params.alDevice = new StALDeviceParam(); params.AudioGain = new StFloat32Param( 0.0f, // sound is unattenuated @@ -257,6 +267,8 @@ mySettings->loadParam (ST_SETTING_SHOW_LIST, params.ToShowPlayList); mySettings->loadParam (ST_SETTING_SUBTITLES_SIZE, params.SubtitlesSize); mySettings->loadParam (ST_SETTING_SUBTITLES_PARALLAX, params.SubtitlesParallax); + mySettings->loadParam (ST_SETTING_SUBTITLES_PARSER, params.SubtitlesParser); + mySettings->loadParam (ST_SETTING_SEARCH_SUBS, params.ToSearchSubs); mySettings->loadParam (ST_SETTING_SHOW_FPS, params.ToShowFps); mySettings->loadParam (ST_SETTING_VSYNC, params.IsVSyncOn); @@ -322,6 +334,9 @@ anAction = new StActionIntValue(stCString("DoSrcSideBySide"), params.srcFormat, ST_V_SRC_SIDE_BY_SIDE); addAction(Action_SrcSideBySideRL, anAction, ST_VK_S); + anAction = new StActionIntSlot(stCString("DoFileInfo"), stSlot(this, &StMoviePlayer::doAboutFile), 0); + addAction(Action_FileInfo, anAction, ST_VK_I); + anAction = new StActionIntSlot(stCString("DoListFirst"), stSlot(this, &StMoviePlayer::doListFirst), 0); addAction(Action_ListFirst, anAction, ST_VK_HOME); @@ -358,9 +373,18 @@ anAction = new StActionIntSlot(stCString("DoSnapshot"), stSlot(this, &StMoviePlayer::doSnapshot), StImageFile::ST_TYPE_NONE); addAction(Action_SaveSnapshot, anAction, ST_VK_S | ST_VF_CONTROL); + anAction = new StActionIntSlot(stCString("DoDeleteFile"), stSlot(this, &StMoviePlayer::doDeleteFileBegin), 0); + addAction(Action_DeleteFile, anAction, ST_VK_DELETE | ST_VF_SHIFT); + anAction = new StActionBool(stCString("DoAudioMute"), params.AudioMute); addAction(Action_AudioMute, anAction); + anAction = new StActionIntSlot(stCString("DoAudioDecrease"), stSlot(this, &StMoviePlayer::doAudioVolume), (size_t )-1); + addAction(Action_AudioDecrease, anAction, ST_VK_DOWN); + + anAction = new StActionIntSlot(stCString("DoAudioIncrease"), stSlot(this, &StMoviePlayer::doAudioVolume), 1); + addAction(Action_AudioIncrease, anAction, ST_VK_UP); + anAction = new StActionIntSlot(stCString("DoAudioNext"), stSlot(this, &StMoviePlayer::doAudioNext), 1); addAction(Action_AudioNext, anAction, ST_VK_H, ST_VK_L); @@ -373,6 +397,9 @@ anAction = new StActionIntSlot(stCString("DoSubtitlesPrev"), stSlot(this, &StMoviePlayer::doSubtitlesNext), (size_t )-1); addAction(Action_SubsPrev, anAction, ST_VK_U | ST_VF_SHIFT, ST_VK_T | ST_VF_SHIFT); + anAction = new StActionIntSlot(stCString("DoSubtitlesCopy"), stSlot(this, &StMoviePlayer::doSubtitlesCopy), 0); + addAction(Action_CopyToClipboard, anAction, ST_VK_C | ST_VF_CONTROL, ST_VK_INSERT | ST_VF_CONTROL); + anAction = new StActionIntSlot(stCString("DoPlayListReverse"), stSlot(this, &StMoviePlayer::doPlayListReverse), 0); addAction(Action_ShowList, anAction, ST_VK_L | ST_VF_CONTROL); @@ -417,6 +444,8 @@ mySettings->saveParam (ST_SETTING_SCALE_FORCE2X, params.ScaleHiDPI2X); mySettings->saveParam (ST_SETTING_SUBTITLES_SIZE, params.SubtitlesSize); mySettings->saveParam (ST_SETTING_SUBTITLES_PARALLAX, params.SubtitlesParallax); + mySettings->saveParam (ST_SETTING_SUBTITLES_PARSER, params.SubtitlesParser); + mySettings->saveParam (ST_SETTING_SEARCH_SUBS, params.ToSearchSubs); mySettings->saveInt32 (ST_SETTING_FPSTARGET, params.TargetFps); mySettings->saveString(ST_SETTING_OPENAL_DEVICE, params.alDevice->getTitle()); mySettings->saveInt32 (ST_SETTING_UPDATES_LAST_CHECK, myLastUpdateDay); @@ -518,6 +547,40 @@ myGUI->myImage->params.saturation->reset(); } + +void StMoviePlayer::doDeleteFileBegin(const size_t ) { + //if(!myFileToDelete.isNull()) { + // return; + //} + + myFileToDelete = myVideo->getPlayList().getCurrentFile(); + if(myFileToDelete.isNull() + || myFileToDelete->size() != 0) { + myFileToDelete.nullify(); + return; + } + + const StString aText = myLangMap->getValue(StMoviePlayerStrings::DIALOG_DELETE_FILE_QUESTION) + + "\n" + myFileToDelete->getPath(); + + StGLMessageBox* aDialog = new StGLMessageBox(myGUI.access(), myLangMap->getValue(StMoviePlayerStrings::DIALOG_DELETE_FILE_TITLE), + aText, myGUI->scale(512), myGUI->scale(256)); + aDialog->addButton(myLangMap->getValue(StMoviePlayerStrings::BUTTON_DELETE), true)->signals.onBtnClick += stSlot(this, &StMoviePlayer::doDeleteFileEnd); + aDialog->addButton(myLangMap->getValue(StMoviePlayerStrings::BUTTON_CANCEL), false); + aDialog->setVisibility(true, true); + aDialog->stglInit(); +} + +void StMoviePlayer::doDeleteFileEnd(const size_t ) { + if(myFileToDelete.isNull() + || myVideo.isNull()) { + return; + } + + myVideo->doRemovePhysically(myFileToDelete); + myFileToDelete.nullify(); +} + void StMoviePlayer::doStopWebUI() { #ifdef ST_HAVE_MONGOOSE if(myWebCtx != NULL) { @@ -607,7 +670,8 @@ myVideo = new StVideo(params.alDevice->getTitle(), myLangMap, myPlayList, aTextureQueue, aSubQueue); myVideo->signals.onError = stSlot(myMsgQueue.access(), &StMsgQueue::doPushError); myVideo->signals.onLoaded = stSlot(this, &StMoviePlayer::doLoaded); - myVideo->params.UseGpu = params.UseGpu; + myVideo->params.UseGpu = params.UseGpu; + myVideo->params.ToSearchSubs = params.ToSearchSubs; #ifdef ST_HAVE_MONGOOSE doStartWebUI(); @@ -744,11 +808,6 @@ StApplication::doKeyDown(theEvent); switch(theEvent.VKey) { - // file walk - case ST_VK_I: - myGUI->doAboutFile(0); - return; - // post process keys case ST_VK_B: { if(theEvent.Flags == ST_VF_SHIFT) { @@ -841,6 +900,18 @@ } } +void StMoviePlayer::doAudioVolume(size_t theDirection) { + if(myVideo.isNull()) { + return; + } + + if(theDirection == 1) { + params.AudioGain->increment(); + } else { + params.AudioGain->decrement(); + } +} + void StMoviePlayer::doAudioNext(size_t theDirection) { if(myVideo.isNull()) { return; @@ -859,6 +930,20 @@ params.subtitlesStream->setValue(aValue); } +void StMoviePlayer::doSubtitlesCopy(size_t ) { + if(myVideo.isNull() + || myGUI.isNull() + || myGUI->mySubtitles == NULL) { + return; + } + + const StString& aText = myGUI->mySubtitles->getText(); + if(aText.isEmpty()) { + return; + } + myWindow->toClipboard(aText); +} + void StMoviePlayer::doFileNext() { if(myVideo.isNull()) { return; @@ -1125,6 +1210,22 @@ myVideo->pushPlayEvent(ST_PLAYEVENT_SEEK, mySeekOnLoad); mySeekOnLoad = -1.0; } + if(myAudioOnLoad >= 0) { + myVideo->params.activeAudio->setValue(myAudioOnLoad); + params.audioStream->setValue(myAudioOnLoad); + myAudioOnLoad = -1; + } + if(mySubsOnLoad >= 0) { + myVideo->params.activeSubtitles->setValue(mySubsOnLoad); + params.subtitlesStream->setValue(mySubsOnLoad); + mySubsOnLoad = -1; + } +} + +void StMoviePlayer::doAboutFile(const size_t ) { + if(!myGUI.isNull()) { + myGUI->doAboutFile(0); + } } void StMoviePlayer::doSwitchSrcFormat(const int32_t theSrcFormat) { @@ -1216,6 +1317,15 @@ doOpenFileThreaded(this, OPEN_FILE_2MOVIES); } +void StMoviePlayer::doSaveFileInfo(const size_t theToSave) { + if(!myGUI.isNull() + && !myFileInfo.isNull() + && theToSave == 1) { + //myLoader->doSaveInfo(myFileInfo); + } + myFileInfo.nullify(); +} + void StMoviePlayer::doOpenRecent(const size_t theItemId) { if(myVideo.isNull()) { return; @@ -1287,6 +1397,7 @@ myEventDialog.reset(); return; } + switch(theOpenType) { case OPEN_FILE_2MOVIES: { aTitle = tr(DIALOG_OPEN_RIGHT); @@ -1299,10 +1410,18 @@ } break; } - case OPEN_STREAM_AUDIO: + case OPEN_STREAM_AUDIO: { + myPlayList->addToNode(aCurrFile, aFilePath); + myAudioOnLoad = myVideo->params.activeAudio->getListSize(); + mySubsOnLoad = myVideo->params.activeSubtitles->getValue(); + mySeekOnLoad = myVideo->getPts(); + break; + } case OPEN_STREAM_SUBTITLES: { myPlayList->addToNode(aCurrFile, aFilePath); - mySeekOnLoad = myVideo->getPts(); + myAudioOnLoad = myVideo->params.activeAudio->getValue(); + mySubsOnLoad = myVideo->params.activeSubtitles->getListSize(); + mySeekOnLoad = myVideo->getPts(); break; } case OPEN_FILE_MOVIE: diff -Nru sview-13.10/StMoviePlayer/StMoviePlayer.h sview-14.01/StMoviePlayer/StMoviePlayer.h --- sview-13.10/StMoviePlayer/StMoviePlayer.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayer.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -158,14 +158,19 @@ ST_LOCAL void doListPrev(const size_t dummy = 0); ST_LOCAL void doListNext(const size_t dummy = 0); ST_LOCAL void doListLast(const size_t dummy = 0); + ST_LOCAL void doDeleteFileBegin(const size_t dummy = 0); + ST_LOCAL void doDeleteFileEnd (const size_t dummy = 0); + ST_LOCAL void doAudioVolume(size_t theDirection); ST_LOCAL void doAudioNext(size_t theDirection); ST_LOCAL void doSubtitlesNext(size_t theDirection); + ST_LOCAL void doSubtitlesCopy(size_t dummy = 0); ST_LOCAL void doQuit(const size_t dummy = 0); ST_LOCAL void doFileNext(); ST_LOCAL void doOpen1File(const size_t dummy = 0); ST_LOCAL void doOpen2Files(const size_t dummy = 0); + ST_LOCAL void doSaveFileInfo(const size_t theToSave); ST_LOCAL void doOpenRecent(const size_t theItemId); ST_LOCAL void doClearRecent(const size_t dummy = 0); ST_LOCAL void doUpdateOpenALDeviceList(const size_t dummy = 0); @@ -179,6 +184,7 @@ ST_LOCAL void doReset(const size_t dummy = 0); ST_LOCAL void doSnapshot(const size_t theImgType); + ST_LOCAL void doAboutFile(const size_t dummy = 0); public: //! @name Properties @@ -195,6 +201,8 @@ StHandle ScaleHiDPI2X; //!< option to set HiDPI resolution to 2.0 StHandle SubtitlesSize; //!< subtitles font size StHandle SubtitlesParallax;//!< subtitles parallax + StHandle ToSearchSubs; //!< automatically search for additional subtitles/audio track files nearby video file + StHandle SubtitlesParser; //!< subtitles parser StHandle alDevice; //!< active OpenAL device StHandle AudioGain; //!< volume factor StHandle AudioMute; //!< volume mute flag @@ -304,6 +312,7 @@ Action_SrcMono, Action_SrcOverUnderLR, Action_SrcSideBySideRL, + Action_FileInfo, Action_ListFirst, Action_ListLast, Action_ListPrev, @@ -316,11 +325,15 @@ Action_SeekRight5, Action_Open1File, Action_SaveSnapshot, + Action_DeleteFile, Action_AudioMute, + Action_AudioDecrease, + Action_AudioIncrease, Action_AudioPrev, Action_AudioNext, Action_SubsPrev, Action_SubsNext, + Action_CopyToClipboard, Action_ShowList, Action_ImageAdjustReset, Action_StereoParamsBegin, @@ -347,10 +360,14 @@ StHandle myGUI; //!< GUI root widget StHandle myVideo; //!< main video playback class StHandle myUpdates; //!< check updates utility + StHandle myFileToDelete; //!< file node for removal + StHandle myFileInfo; //!< file info for opened dialog StCondition myEventDialog; //!< event to prevent showing multiple open/save file dialogs StCondition myEventLoaded; //!< indicate that new file was open double mySeekOnLoad; //!< seeking target + int32_t myAudioOnLoad; //!< audio track on load + int32_t mySubsOnLoad; //!< subtitles track on load mg_context* myWebCtx; //!< web UI context diff -Nru sview-13.10/StMoviePlayer/StMoviePlayer.rc sview-14.01/StMoviePlayer/StMoviePlayer.rc --- sview-13.10/StMoviePlayer/StMoviePlayer.rc 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayer.rc 2014-01-30 10:45:03.000000000 +0000 @@ -15,7 +15,7 @@ BEGIN VALUE "FileDescription", "Stereoscopic Movie Player\000" VALUE "FileVersion", SVIEW_SDK_VER_STRING "\000" - VALUE "LegalCopyright", "\251 2007-2013 Kirill Gavrilov\000" + VALUE "LegalCopyright", "\251 2007-2014 Kirill Gavrilov\000" VALUE "ProductName", "StMoviePlayer\000" VALUE "ProductVersion", SVIEW_SDK_VER_STRING "\000" VALUE "OfficialSite", "www.sview.ru\000" diff -Nru sview-13.10/StMoviePlayer/StMoviePlayerGUI.cpp sview-14.01/StMoviePlayer/StMoviePlayerGUI.cpp --- sview-13.10/StMoviePlayer/StMoviePlayerGUI.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayerGUI.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -193,6 +194,7 @@ anItem->signals.onItemClick.connect(myPlugin, &StMoviePlayer::doOpenRecent); aMenuMedia->addItem(tr(MENU_MEDIA_SAVE_SNAPSHOT_AS), myPlugin->getAction(StMoviePlayer::Action_SaveSnapshot), aMenuSaveImage); aMenuMedia->addItem(tr(MENU_MEDIA_SRC_FORMAT), aMenuSrcFormat); + aMenuMedia->addItem(tr(MENU_MEDIA_FILE_INFO), myPlugin->getAction(StMoviePlayer::Action_FileInfo)); aMenuMedia->addItem(tr(MENU_MEDIA_AL_DEVICE), myMenuOpenAL); aMenuMedia->addItem("Audio Volume", aMenuVolume); @@ -537,13 +539,13 @@ /** * Dialog for Audio/Video synchronization control. */ -class StDelayControl : public StGLMessageBox { +class ST_LOCAL StDelayControl : public StGLMessageBox { public: StDelayControl(StMoviePlayerGUI* theParent, const StHandle& theTrackedValue) - : StGLMessageBox(theParent, "", theParent->scale(400), theParent->scale(260)), + : StGLMessageBox(theParent, theParent->tr(DIALOG_AUDIO_DELAY_TITLE), "", theParent->scale(400), theParent->scale(260)), myRange(NULL) { changeRectPx().moveX( myRoot->scale( 64)); changeRectPx().moveY(-myRoot->scale(128)); @@ -557,17 +559,9 @@ aContent->setVisibility(true, true); const StGLVec3 aWhite(1.0f, 1.0f, 1.0f); - StGLTextArea* aTitle = new StGLTextArea(aContent, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT), - aContent->getRectPx().width(), myRoot->scale(10)); - aTitle->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, StGLTextFormatter::ST_ALIGN_Y_TOP); - aTitle->setText(theParent->tr(DIALOG_AUDIO_DELAY_TITLE)); - aTitle->setTextColor(aWhite); - aTitle->setVisibility(true, true); - aTitle->stglInitAutoHeight(); - - StGLTextArea* aText = new StGLTextArea(aContent, 0, aTitle->getRectPx().bottom(), StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT), + StGLTextArea* aText = new StGLTextArea(aContent, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_LEFT), aContent->getRectPx().width(), myRoot->scale(10)); - aText->setText(StString("\n\n") + theParent->tr(DIALOG_AUDIO_DELAY_DESC) + "\n"); + aText->setText(theParent->tr(DIALOG_AUDIO_DELAY_DESC) + "\n"); aText->setTextColor(aWhite); aText->setVisibility(true, true); aText->stglInitAutoHeight(); @@ -691,7 +685,7 @@ } void StMoviePlayerGUI::doAboutProgram(const size_t ) { - StGLMessageBox* aDialog = new StGLMessageBox(this, + StGLMessageBox* aDialog = new StGLMessageBox(this, "", tr(ABOUT_DPLUGIN_NAME) + '\n' + tr(ABOUT_VERSION) + ": " + StVersionInfo::getSDKVersionString() + " "+ StThread::getArchString() @@ -707,43 +701,157 @@ } void StMoviePlayerGUI::doAboutSystem(const size_t ) { - StString aTitle = "System Info"; - StString anInfo = getContext().stglFullInfo(); - StString aString = aTitle + "\n\n \n" + anInfo; - StGLMessageBox* aDialog = new StGLMessageBox(this, aString, scale(512), scale(256)); + const StString aTitle = tr(ABOUT_SYSTEM); + StGLMessageBox* aDialog = new StGLMessageBox(this, aTitle, "", scale(512), scale(256)); + + StArgumentsMap anInfo; + getContext().stglFullInfo(anInfo); + StGLTable* aTable = new StGLTable(aDialog->getContent(), 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + aTable->setVisibility(true, true); + aTable->fillFromMap(anInfo, StGLVec3(1.0f, 1.0f, 1.0f), aDialog->getContent()->getRectPx().width(), aDialog->getContent()->getRectPx().width() / 2); + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } +/** + * Customized message box. + */ +class ST_LOCAL StInfoDialog : public StGLMessageBox { + + public: + + ST_LOCAL StInfoDialog(StMoviePlayer* thePlugin, + StGLWidget* theParent, + const StString& theTitle, + const int theWidth, + const int theHeight) + : StGLMessageBox(theParent, theTitle, "", theWidth, theHeight), + myPlugin(thePlugin) {} + + ST_LOCAL virtual ~StInfoDialog() { + myPlugin->doSaveFileInfo(0); + } + + private: + + StMoviePlayer* myPlugin; + +}; + void StMoviePlayerGUI::doAboutFile(const size_t ) { + StHandle& anExtraInfo = myPlugin->myFileInfo; + if(!anExtraInfo.isNull()) { + return; // already opened + } + StHandle aFileNode; StHandle aParams; - StHandle anExtraInfo; - StString aCodecsInfo; - StArrayList anInfoList(10); - if(myPlugin->getCurrentFile(aFileNode, aParams, anExtraInfo) && !anExtraInfo.isNull()) { - for(size_t aKeyIter = 0; aKeyIter < anExtraInfo->myInfo.size(); ++aKeyIter) { - const StArgument& aPair = anExtraInfo->myInfo.getFromIndex(aKeyIter); - anInfoList.add(aPair.getKey() + ": " + aPair.getValue() + "\n"); - } + if(!myPlugin->getCurrentFile(aFileNode, aParams, anExtraInfo) + || anExtraInfo.isNull()) { + StHandle aQueue = myPlugin->getMessagesQueue(); + aQueue->pushInfo(tr(DIALOG_FILE_NOINFO)); + anExtraInfo.nullify(); + return; + } - aCodecsInfo = "\nActive codecs\n \n"; - for(size_t aKeyIter = 0; aKeyIter < anExtraInfo->myCodecs.size(); ++aKeyIter) { - const StArgument& aPair = anExtraInfo->myCodecs.getFromIndex(aKeyIter); - if(!aPair.getValue().isEmpty()) { - aCodecsInfo += aPair.getValue() + "\n"; - } - } + const int THE_MIN_WIDTH = scale(512); + const StString aTitle = tr(DIALOG_FILE_INFO); + const int aWidth = stMax(int(double(getRectPx().width()) * 0.6), THE_MIN_WIDTH); + StInfoDialog* aDialog = new StInfoDialog(myPlugin, this, aTitle, aWidth, scale(300)); + + // translate known metadata tag names + for(size_t aMapIter = 0; aMapIter < anExtraInfo->Info.size(); ++aMapIter) { + StDictEntry& anEntry = anExtraInfo->Info.changeValue(aMapIter); + StString aKey = anEntry.getKey().lowerCased(); + anEntry.changeName() = myLangMap->getValue(aKey); + } + + const StGLVec3 aWhite(1.0f, 1.0f, 1.0f); + const int aWidthMax = aDialog->getContent()->getRectPx().width(); + StGLTable* aTable = new StGLTable(aDialog->getContent(), 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + int aRowLast = (int )anExtraInfo->Info.size(); + const int aNbRowsMax = aRowLast + (int )anExtraInfo->Codecs.size() + 3; + aTable->setupTable((int )aNbRowsMax, 2); + aTable->setVisibility(true, true); + aTable->fillFromMap(anExtraInfo->Info, aWhite, + aWidthMax, aWidthMax / 2); + + const int aTextMaxWidth = aWidthMax - (aTable->getMarginLeft() + aTable->getMarginRight()); + + // add stereoscopic format info + const StFormatEnum anActiveSrcFormat = aParams->isSwapLR() + ? st::formatReversed(aParams->getSrcFormat()) + : aParams->getSrcFormat(); + StGLTableItem& aSrcFormatItem = aTable->changeElement(aRowLast++, 0); aSrcFormatItem.setColSpan(2); + StGLTextArea* aSrcFormatText = new StGLTextArea(&aSrcFormatItem, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER)); + aSrcFormatText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aSrcFormatText->setText(StString("\n") + tr(BTN_SRC_FORMAT) + " " + trSrcFormat(anActiveSrcFormat)); + aSrcFormatText->setTextColor(aWhite); + aSrcFormatText->setVisibility(true, true); + aSrcFormatText->stglInitAutoHeightWidth(aTextMaxWidth); + + // warn about wrong/missing stereoscopic format information + StString aSrcInfo; + StGLVec3 anExtraColor = aWhite; + if(anExtraInfo->SrcFormat == ST_V_SRC_AUTODETECT + && anActiveSrcFormat != ST_V_SRC_MONO + && anActiveSrcFormat != ST_V_SRC_SEPARATE_FRAMES) { + aSrcInfo = tr(INFO_NO_SRCFORMAT); + anExtraColor = StGLVec3(1.0f, 1.0f, 0.8f); + } else if(anExtraInfo->SrcFormat != ST_V_SRC_AUTODETECT + && anExtraInfo->SrcFormat != anActiveSrcFormat) { + aSrcInfo = tr(INFO_WRONG_SRCFORMAT); + anExtraColor = StGLVec3(1.0f, 0.0f, 0.0f); + } + if(!aSrcInfo.isEmpty()) { + StGLTableItem& aTabItem = aTable->changeElement(aRowLast++, 0); aTabItem.setColSpan(2); + StGLTextArea* aText = new StGLTextArea(&aTabItem, 0, 0, StGLCorner(ST_VCORNER_CENTER, ST_HCORNER_CENTER)); + aText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_CENTER, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aText->setText(aSrcInfo); + aText->setTextColor(anExtraColor); + aText->setVisibility(true, true); + aText->stglInitAutoHeightWidth(aTextMaxWidth); } - StString aTitle = "File Info"; - StString anInfo; - for(size_t anIter = 0; anIter < anInfoList.size(); ++anIter) { - anInfo += anInfoList[anIter]; + // append information about active decoders + StGLTableItem& aCodecItem = aTable->changeElement(aRowLast++, 0); + aCodecItem.setColSpan(2); + + StGLTextArea* aCodecsText = new StGLTextArea(&aCodecItem, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + aCodecsText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_LEFT, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aCodecsText->setText(StString('\n') + tr(DIALOG_FILE_DECODERS) + '\n'); + aCodecsText->setTextColor(aWhite); + aCodecsText->setVisibility(true, true); + aCodecsText->stglInitAutoHeightWidth(aTextMaxWidth); + for(size_t aKeyIter = 0; aKeyIter < anExtraInfo->Codecs.size(); ++aKeyIter) { + const StArgument& aPair = anExtraInfo->Codecs.getFromIndex(aKeyIter); + if(aPair.getValue().isEmpty()) { + continue; + } + + StGLTableItem& anItem = aTable->changeElement(aRowLast++, 0); + anItem.setColSpan(2); + + StGLTextArea* aText = new StGLTextArea(&anItem, 0, 0, StGLCorner(ST_VCORNER_TOP, ST_HCORNER_CENTER)); + aText->setupAlignment(StGLTextFormatter::ST_ALIGN_X_LEFT, + StGLTextFormatter::ST_ALIGN_Y_TOP); + aText->setText(aPair.getValue()); + aText->setTextColor(aWhite); + aText->setVisibility(true, true); + aText->stglInitAutoHeightWidth(aTextMaxWidth); + } + aTable->updateLayout(); + const int aWidthAdjust = aWidth - stMax(aTable->getRectPx().width() + aDialog->getMarginLeft() + aDialog->getMarginRight(), THE_MIN_WIDTH); + if(aWidthAdjust > 0) { + aDialog->changeRectPx().right() -= aWidthAdjust; + aDialog->getContent()->changeRectPx().right() -= aWidthAdjust; } - StString aString = aTitle + "\n\n \n" + anInfo + aCodecsInfo; - StGLMessageBox* aDialog = new StGLMessageBox(this, aString, scale(512), scale(300)); + aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); @@ -890,7 +998,8 @@ myImage = new StGLImageRegion(this, theTextureQueue, false); mySubtitles = new StGLSubtitles (this, theSubQueue, myPlugin->params.SubtitlesSize, - myPlugin->params.SubtitlesParallax); + myPlugin->params.SubtitlesParallax, + myPlugin->params.SubtitlesParser); createUpperToolbar(); @@ -998,6 +1107,26 @@ return child != NULL && !child->isVisible(); } +size_t StMoviePlayerGUI::trSrcFormatId(const StFormatEnum theSrcFormat) { + switch(theSrcFormat) { + case ST_V_SRC_MONO: return MENU_SRC_FORMAT_MONO; + case ST_V_SRC_SIDE_BY_SIDE: return MENU_SRC_FORMAT_CROSS_EYED; + case ST_V_SRC_PARALLEL_PAIR: return MENU_SRC_FORMAT_PARALLEL; + case ST_V_SRC_OVER_UNDER_RL: return MENU_SRC_FORMAT_OVERUNDER_RL; + case ST_V_SRC_OVER_UNDER_LR: return MENU_SRC_FORMAT_OVERUNDER_LR; + case ST_V_SRC_ROW_INTERLACE: return MENU_SRC_FORMAT_INTERLACED; + //case ST_V_SRC_VERTICAL_INTERLACE: + case ST_V_SRC_SEPARATE_FRAMES: return MENU_SRC_FORMAT_SEPARATE; + case ST_V_SRC_PAGE_FLIP: return MENU_SRC_FORMAT_PAGEFLIP; + case ST_V_SRC_ANAGLYPH_RED_CYAN: return MENU_SRC_FORMAT_ANA_RC; + case ST_V_SRC_ANAGLYPH_G_RB: return MENU_SRC_FORMAT_ANA_RB; + case ST_V_SRC_ANAGLYPH_YELLOW_BLUE: return MENU_SRC_FORMAT_ANA_YB; + case ST_V_SRC_TILED_4X: return MENU_SRC_FORMAT_TILED_4X; + default: + case ST_V_SRC_AUTODETECT: return MENU_SRC_FORMAT_AUTO; + } +} + void StMoviePlayerGUI::setVisibility(const StPointD_t& theCursor, bool theIsMouseMoved) { const bool toShowPlayList = myPlugin->params.ToShowPlayList->getValue(); @@ -1051,29 +1180,16 @@ size_t aLngId = myImage->params.swapLR->getValue() ? SWAP_LR_ON : SWAP_LR_OFF; myDescr->setText(tr(aLngId)); } else if(myBtnSrcFrmt->isPointIn(theCursor)) { - size_t aLngId = MENU_SRC_FORMAT_AUTO; StFormatEnum aSrcFormat = (StFormatEnum )myPlugin->params.srcFormat->getValue(); if(aSrcFormat == ST_V_SRC_AUTODETECT && !myImage->params.stereoFile.isNull()) { aSrcFormat = myImage->params.stereoFile->getSrcFormat(); } - switch(aSrcFormat) { - case ST_V_SRC_MONO: aLngId = MENU_SRC_FORMAT_MONO; break; - case ST_V_SRC_SIDE_BY_SIDE: aLngId = MENU_SRC_FORMAT_CROSS_EYED; break; - case ST_V_SRC_PARALLEL_PAIR: aLngId = MENU_SRC_FORMAT_PARALLEL; break; - case ST_V_SRC_OVER_UNDER_RL: aLngId = MENU_SRC_FORMAT_OVERUNDER_RL; break; - case ST_V_SRC_OVER_UNDER_LR: aLngId = MENU_SRC_FORMAT_OVERUNDER_LR; break; - case ST_V_SRC_ROW_INTERLACE: aLngId = MENU_SRC_FORMAT_INTERLACED; break; - case ST_V_SRC_ANAGLYPH_G_RB: aLngId = MENU_SRC_FORMAT_ANA_RB; break; - case ST_V_SRC_ANAGLYPH_RED_CYAN: aLngId = MENU_SRC_FORMAT_ANA_RC; break; - case ST_V_SRC_ANAGLYPH_YELLOW_BLUE: aLngId = MENU_SRC_FORMAT_ANA_YB; break; - case ST_V_SRC_PAGE_FLIP: aLngId = MENU_SRC_FORMAT_PAGEFLIP; break; - case ST_V_SRC_TILED_4X: aLngId = MENU_SRC_FORMAT_TILED_4X; break; - case ST_V_SRC_SEPARATE_FRAMES: aLngId = MENU_SRC_FORMAT_SEPARATE; break; - default: - case ST_V_SRC_AUTODETECT: aLngId = MENU_SRC_FORMAT_AUTO; break; + if(!myImage->params.stereoFile.isNull() + && myImage->params.swapLR->getValue()) { + aSrcFormat = st::formatReversed(aSrcFormat); } - myDescr->setText(tr(BTN_SRC_FORMAT) + tr(aLngId)); + myDescr->setText(tr(BTN_SRC_FORMAT) + "\n" + trSrcFormat(aSrcFormat)); } else if(myBtnPlay->isPointIn(theCursor)) { myDescr->setText(tr(VIDEO_PLAYPAUSE)); } else if(myBtnPrev->isPointIn(theCursor)) { @@ -1147,6 +1263,13 @@ delete anItem; } + // create text parser menu + StGLMenu* aParserMenu = new StGLMenu(this, 0, 0, StGLMenu::MENU_VERTICAL); + for(size_t anIter = 0; anIter < myPlugin->params.SubtitlesParser->getValues().size(); ++anIter) { + aParserMenu->addItem(myPlugin->params.SubtitlesParser->getValues()[anIter], myPlugin->params.SubtitlesParser, (int32_t )anIter); + } + aParserMenu->stglInit(); + myMenuSubtitles->addItem(tr(MENU_SUBTITLES_NONE), myPlugin->params.subtitlesStream, -1); if(!theStreamsList.isNull()) { for(size_t aStreamId = 0; aStreamId < theStreamsList->size(); ++aStreamId) { @@ -1180,6 +1303,7 @@ aRange->setVisibility(true, true); } + myMenuSubtitles->addItem(tr(MENU_SUBTITLES_PARSER), aParserMenu); if(theIsFilePlayed) { //myMenuSubtitles->addSplitter(); myMenuSubtitles->addItem(tr(MENU_SUBTITLES_ATTACH)) @@ -1221,14 +1345,14 @@ anAboutText = StString() + "Plugin '" + myPlugin->getMainWindow()->getRendererId() + "' doesn't provide description"; } - StGLMessageBox* aDialog = new StGLMessageBox(this, anAboutText, scale(512), scale(300)); + StGLMessageBox* aDialog = new StGLMessageBox(this, "", anAboutText, scale(512), scale(300)); aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); } void StMoviePlayerGUI::showUpdatesNotify() { - StGLMessageBox* aDialog = new StGLMessageBox(this, tr(UPDATES_NOTIFY)); + StGLMessageBox* aDialog = new StGLMessageBox(this, "", tr(UPDATES_NOTIFY)); aDialog->addButton(tr(BUTTON_CLOSE)); aDialog->setVisibility(true, true); aDialog->stglInit(); diff -Nru sview-13.10/StMoviePlayer/StMoviePlayerGUI.h sview-14.01/StMoviePlayer/StMoviePlayerGUI.h --- sview-13.10/StMoviePlayer/StMoviePlayerGUI.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayerGUI.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,6 +59,12 @@ return myLangMap->getValue(theId); } + ST_LOCAL const StString& trSrcFormat(const StFormatEnum theSrcFormat) const { + return tr(trSrcFormatId(theSrcFormat)); + } + + ST_LOCAL static size_t trSrcFormatId(const StFormatEnum theSrcFormat); + /** * @return absolute path to the texture */ diff -Nru sview-13.10/StMoviePlayer/StMoviePlayerInfo.h sview-14.01/StMoviePlayer/StMoviePlayerInfo.h --- sview-13.10/StMoviePlayer/StMoviePlayerInfo.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayerInfo.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2010 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -342,9 +342,27 @@ #define ST_SRT_MIME_STRING ST_SRT_MIME ":" ST_SRT_EXT ":" ST_SRT_DESC /** + *.smi + */ +#define ST_SMI_MIME "subtitles/x-smi" +#define ST_SMI_EXT "smi" +#define ST_SMI_DESC "SAMI - Utf-8 subtitles" +#define ST_SMI_MIME_STRING ST_SMI_MIME ":" ST_SMI_EXT ":" ST_SMI_DESC + +/** + *.sub + */ +#define ST_SUB_MIME "subtitles/x-sub" +#define ST_SUB_EXT "sub" +#define ST_SUB_DESC "SUB - Image-based subtitles" +#define ST_SUB_MIME_STRING ST_SUB_MIME ":" ST_SUB_EXT ":" ST_SUB_DESC + +/** * Define Subtitles MIME list. */ #define ST_VIDEO_PLUGIN_SUBTIT_MIME_CHAR ST_SRT_MIME_STRING ";" \ +ST_SMI_MIME_STRING ";" \ +ST_SUB_MIME_STRING ";" \ "\000" #endif // __StVideoPluginInfo_h_ diff -Nru sview-13.10/StMoviePlayer/StMoviePlayerStrings.cpp sview-14.01/StMoviePlayer/StMoviePlayerStrings.cpp --- sview-13.10/StMoviePlayer/StMoviePlayerStrings.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayerStrings.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2013 Kirill Gavrilov + * Copyright © 2013-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,14 @@ void loadDefaults(StLangMap& theStrings) { theStrings(BUTTON_CLOSE, "Close"); + theStrings(BUTTON_CANCEL, + "Cancel"); theStrings(BUTTON_RESET, "Reset"); + theStrings(BUTTON_SAVE_METADATA, + "Save"); + theStrings(BUTTON_DELETE, + "Delete"); theStrings(MENU_MEDIA, "Media"); theStrings(MENU_VIEW, @@ -57,6 +63,12 @@ "Font Size"); theStrings(MENU_SUBTITLES_PARALLAX, "Parallax"); + theStrings(MENU_SUBTITLES_PARSER, + "Parser"); + theStrings(MENU_SUBTITLES_PLAIN_TEXT, + "Plain text"); + theStrings(MENU_SUBTITLES_LITE_HTML, + "Lite HTML"); theStrings(MENU_HELP, "Help"); theStrings(MENU_MEDIA_OPEN_MOVIE, @@ -87,6 +99,8 @@ "Web UI can not be started on {0} port!"); theStrings(MENU_MEDIA_QUIT, "Quit"); + theStrings(MENU_MEDIA_FILE_INFO, + "File info"); theStrings(MENU_MEDIA_OPEN_MOVIE_1, "From One file"); theStrings(MENU_MEDIA_OPEN_MOVIE_2, @@ -188,10 +202,12 @@ "version"); theStrings(ABOUT_DESCRIPTION, "Movie player allows you to play stereoscopic video.\n" - "(C) 2007-2013 Kirill Gavrilov \n" + "(C) 2007-2014 Kirill Gavrilov \n" "Official site: www.sview.ru\n" "\n" "This program distributed under GPL3.0"); + theStrings(ABOUT_SYSTEM, + "System Info"); theStrings(MENU_HELP_ABOUT, "About..."); theStrings(MENU_HELP_USERTIPS, @@ -239,7 +255,7 @@ theStrings(FILE_VIDEO_OPEN, "Open another movie"); theStrings(BTN_SRC_FORMAT, - "Source format:\n"); + "Source format:"); theStrings(VIDEO_PLAYPAUSE, "Play/Pause"); theStrings(VIDEO_LIST_PREV, @@ -259,12 +275,132 @@ "Choose the video file to open"); theStrings(DIALOG_OPEN_RIGHT, "Choose RIGHT video file to open"); + theStrings(DIALOG_FILE_INFO, + "File Info"); + theStrings(DIALOG_FILE_NOINFO, + "Information is unavailable"); + theStrings(DIALOG_DELETE_FILE_TITLE, + "File deletion"); + theStrings(DIALOG_DELETE_FILE_QUESTION, + "Do you really want to completely remove this file?"); + theStrings(DIALOG_FILE_DECODERS, + "Active decoders:"); theStrings(DIALOG_NOTHING_TO_SAVE, "Nothing to save!"); theStrings(DIALOG_NO_SNAPSHOT, "Snapshot not available!"); theStrings(DIALOG_SAVE_SNAPSHOT, "Choose location to save snapshot"); + + theStrings(INFO_LEFT, + "[left]"); + theStrings(INFO_RIGHT, + "[right]"); + theStrings(INFO_FILE_NAME, + "File name(s)"); + theStrings(INFO_DIMENSIONS, + "Video dimensions"); + theStrings(INFO_LOAD_TIME, + "Load time"); + theStrings(INFO_TIME_MSEC, + "msec"); + theStrings(INFO_PIXEL_RATIO, + "Pixel ratio"); + theStrings(INFO_PIXEL_FORMAT, + "Pixel format"); + theStrings(INFO_NO_SRCFORMAT, + "(does not stored in metadata)"); + theStrings(INFO_WRONG_SRCFORMAT, + "(does not match metadata)"); + theStrings(INFO_DURATION, + "Duration"); + + theStrings(METADATA_TITLE, + "Title"); + theStrings(METADATA_COMPOSER, + "Composer"); + theStrings(METADATA_ARTIST, + "Artist"); + theStrings(METADATA_ALBUM_ARTIST, + "Album artist"); + theStrings(METADATA_ALBUM, + "Album"); + theStrings(METADATA_DISC, + "Disc"); + theStrings(METADATA_DISC_TOTAL, + "Nb. of discs"); + theStrings(METADATA_GENRE, + "Genre"); + theStrings(METADATA_COMMENT, + "Comment"); + theStrings(METADATA_NOTES, + "Notes"); + theStrings(METADATA_DESCRIPTION, + "Description"); + theStrings(METADATA_PUBLISHER, + "Publisher"); + theStrings(METADATA_COPYRIGHT, + "Copyright"); + theStrings(METADATA_ENCODER, + "Encoder"); + theStrings(METADATA_ENGINEER, + "Engineer"); + theStrings(METADATA_SOURCE, + "Source"); + theStrings(METADATA_CREATION_TIME, + "Creation time"); + theStrings(METADATA_DATE, + "Date"); + theStrings(METADATA_YEAR, + "Year"); + theStrings(METADATA_LANGUAGE, + "Language"); + theStrings(METADATA_TRACK, + "Track"); + theStrings(METADATA_TRACK_TOTAL, + "Nb. of tracks"); + theStrings(METADATA_TRACK_GAIN, + "Track gain"); + theStrings(METADATA_TRACK_PEAK, + "Track peak"); + theStrings(METADATA_ALBUM_GAIN, + "Album gain"); + theStrings(METADATA_ALBUM_PEAK, + "Album peak"); + + // define metadata keys, should be lower cased + theStrings.addAlias("title", METADATA_TITLE); + theStrings.addAlias("composer", METADATA_COMPOSER); + theStrings.addAlias("artist", METADATA_ARTIST); + theStrings.addAlias("album_artist", METADATA_ALBUM_ARTIST); + theStrings.addAlias("album artist", METADATA_ALBUM_ARTIST); + theStrings.addAlias("album", METADATA_ALBUM); + theStrings.addAlias("disc", METADATA_DISC); + theStrings.addAlias("disctotal", METADATA_DISC_TOTAL); + theStrings.addAlias("totaldiscs", METADATA_DISC_TOTAL); + theStrings.addAlias("genre", METADATA_GENRE); + theStrings.addAlias("comment", METADATA_COMMENT); + theStrings.addAlias("notes", METADATA_NOTES); + theStrings.addAlias("description", METADATA_DESCRIPTION); + theStrings.addAlias("publisher", METADATA_PUBLISHER); + theStrings.addAlias("copyright", METADATA_COPYRIGHT); + theStrings.addAlias("encoder", METADATA_ENCODER); + theStrings.addAlias("encoded_by", METADATA_ENCODER); + theStrings.addAlias("engineer", METADATA_ENGINEER); + theStrings.addAlias("source", METADATA_SOURCE); + theStrings.addAlias("creation_time", METADATA_CREATION_TIME); + theStrings.addAlias("date", METADATA_DATE); + theStrings.addAlias("year", METADATA_YEAR); + theStrings.addAlias("language", METADATA_LANGUAGE); + theStrings.addAlias("track", METADATA_TRACK); + theStrings.addAlias("tracktotal", METADATA_TRACK_TOTAL); + theStrings.addAlias("totaltracks", METADATA_TRACK_TOTAL); + theStrings.addAlias("replaygain_track_gain", METADATA_TRACK_GAIN); + theStrings.addAlias("replaygain_track_peak", METADATA_TRACK_PEAK); + theStrings.addAlias("replaygain_album_gain", METADATA_ALBUM_GAIN); + theStrings.addAlias("replaygain_album_peak", METADATA_ALBUM_PEAK); + //theStrings.addAlias("album dynamic range", METADATA_ALBUM_DYNAMIC_RANGE); + //theStrings.addAlias("dynamic range", METADATA_DYNAMIC_RANGE); } }; diff -Nru sview-13.10/StMoviePlayer/StMoviePlayerStrings.h sview-14.01/StMoviePlayer/StMoviePlayerStrings.h --- sview-13.10/StMoviePlayer/StMoviePlayerStrings.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StMoviePlayerStrings.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,6 +48,7 @@ MENU_MEDIA_GPU_DECODING = 1107, MENU_MEDIA_WEBUI = 1108, MENU_MEDIA_QUIT = 1109, + MENU_MEDIA_FILE_INFO = 1170, // Root -> Media menu -> Open File menu MENU_MEDIA_OPEN_MOVIE_1 = 1110, @@ -129,6 +130,10 @@ MENU_SUBTITLES_ATTACH = 1353, MENU_SUBTITLES_SIZE = 1354, MENU_SUBTITLES_PARALLAX = 1355, + MENU_SUBTITLES_PARSER = 1356, + + MENU_SUBTITLES_PLAIN_TEXT = 1360, + MENU_SUBTITLES_LITE_HTML = 1361, // Root -> Output menu MENU_CHANGE_DEVICE = 1400, // Root -> Output -> Change Device menu @@ -174,6 +179,11 @@ DIALOG_OPEN_FILE = 2000, DIALOG_OPEN_LEFT = 2001, DIALOG_OPEN_RIGHT = 2002, + DIALOG_FILE_INFO = 2003, + DIALOG_FILE_NOINFO = 2004, + DIALOG_DELETE_FILE_TITLE = 2005, + DIALOG_DELETE_FILE_QUESTION = 2006, + DIALOG_FILE_DECODERS = 2007, DIALOG_SAVE_SNAPSHOT = 2010, DIALOG_NOTHING_TO_SAVE = 2011, @@ -184,9 +194,54 @@ ABOUT_VERSION = 3001, ABOUT_DESCRIPTION = 3002, UPDATES_NOTIFY = 3003, + ABOUT_SYSTEM = 3004, - BUTTON_CLOSE = 4000, - BUTTON_RESET = 4005, + BUTTON_CLOSE = 4000, + BUTTON_CANCEL = 4001, + BUTTON_RESET = 4005, + BUTTON_SAVE_METADATA = 4006, + BUTTON_DELETE = 4007, + + // metadata keys + INFO_LEFT = 5000, + INFO_RIGHT = 5001, + INFO_FILE_NAME = 5002, + INFO_DIMENSIONS = 5003, + INFO_LOAD_TIME = 5004, + INFO_TIME_MSEC = 5005, + INFO_PIXEL_RATIO = 5006, + INFO_PIXEL_FORMAT = 5007, + INFO_NO_SRCFORMAT = 5008, + INFO_WRONG_SRCFORMAT = 5009, + INFO_DURATION = 5010, + + // metadata keys + METADATA_TITLE = 5300, + METADATA_COMPOSER = 5301, + METADATA_ARTIST = 5302, + METADATA_ALBUM_ARTIST = 5303, + METADATA_ALBUM = 5304, + METADATA_DISC = 5305, + METADATA_DISC_TOTAL = 5306, + METADATA_GENRE = 5307, + METADATA_COMMENT = 5308, + METADATA_NOTES = 5309, + METADATA_DESCRIPTION = 5310, + METADATA_PUBLISHER = 5311, + METADATA_COPYRIGHT = 5312, + METADATA_ENCODER = 5313, + METADATA_ENGINEER = 5314, + METADATA_SOURCE = 5315, + METADATA_CREATION_TIME = 5316, + METADATA_DATE = 5317, + METADATA_YEAR = 5318, + METADATA_LANGUAGE = 5319, + METADATA_TRACK = 5320, + METADATA_TRACK_TOTAL = 5321, + METADATA_TRACK_GAIN = 5322, + METADATA_TRACK_PEAK = 5323, + METADATA_ALBUM_GAIN = 5324, + METADATA_ALBUM_PEAK = 5325, }; diff -Nru sview-13.10/StMoviePlayer/StVideo/StAudioQueue.cpp sview-14.01/StMoviePlayer/StVideo/StAudioQueue.cpp --- sview-13.10/StMoviePlayer/StVideo/StAudioQueue.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StAudioQueue.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,7 +1,7 @@ /** * This source is a part of sView program. * - * Copyright © Kirill Gavrilov, 2009-2013 + * Copyright © Kirill Gavrilov, 2009-2014 */ #include "StAudioQueue.h" @@ -12,22 +12,23 @@ /** * Check OpenAL state. */ -void stalCheckErrors(const StString& ST_DEBUG_VAR(theProcedure)) { - ALenum error = alGetError(); - switch(error) { - case AL_NO_ERROR: return; // alright - case AL_INVALID_NAME: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_NAME"); return; - case AL_INVALID_ENUM: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_ENUM"); return; - case AL_INVALID_VALUE: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_VALUE"); return; - case AL_INVALID_OPERATION: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_OPERATION"); return; - case AL_OUT_OF_MEMORY: ST_DEBUG_LOG(theProcedure + ": AL_OUT_OF_MEMORY"); return; - default: ST_DEBUG_LOG(theProcedure + ": OpenAL unknown error"); return; +bool stalCheckErrors(const StString& ST_DEBUG_VAR(theProcedure)) { + ALenum anError = alGetError(); + switch(anError) { + case AL_NO_ERROR: return true; // alright + case AL_INVALID_NAME: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_NAME"); return false; + case AL_INVALID_ENUM: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_ENUM"); return false; + case AL_INVALID_VALUE: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_VALUE"); return false; + case AL_INVALID_OPERATION: ST_DEBUG_LOG(theProcedure + ": AL_INVALID_OPERATION"); return false; + case AL_OUT_OF_MEMORY: ST_DEBUG_LOG(theProcedure + ": AL_OUT_OF_MEMORY"); return false; + default: ST_DEBUG_LOG(theProcedure + ": OpenAL unknown error"); return false; } } -static const StGLVec3 POSITION_CENTER ( 0.0f, 0.0f, 0.0f); static const StGLVec3 POSITION_LEFT (-1.0f, 0.0f, 0.0f); static const StGLVec3 POSITION_RIGHT ( 1.0f, 0.0f, 0.0f); + +static const StGLVec3 POSITION_CENTER ( 0.0f, 0.0f, 0.0f); static const StGLVec3 POSITION_FRONT_LEFT (-1.0f, 0.0f, -1.0f); static const StGLVec3 POSITION_FRONT_CENTER ( 0.0f, 0.0f, -1.0f); static const StGLVec3 POSITION_FRONT_RIGHT ( 1.0f, 0.0f, -1.0f); @@ -35,6 +36,11 @@ static const StGLVec3 POSITION_REAR_LEFT (-1.0f, 0.0f, 1.0f); static const StGLVec3 POSITION_REAR_RIGHT ( 1.0f, 0.0f, 1.0f); +static const StGLVec3 POSITION_REAR_LEFT71 (-1.0f, 0.0f, 1.0f); +static const StGLVec3 POSITION_REAR_RIGHT71 ( 1.0f, 0.0f, 1.0f); +static const StGLVec3 POSITION_SIDE_LEFT71 (-1.0f, 0.0f, 0.0f); +static const StGLVec3 POSITION_SIDE_RIGHT71 ( 1.0f, 0.0f, 0.0f); + /** * Check if dynamically linked version of FFmpeg libraries * is old or not. @@ -76,6 +82,15 @@ stalCheckErrors("alSource*4.0"); } +void StAudioQueue::stalConfigureSources5_0() { + alSourcefv(myAlSources[0], AL_POSITION, POSITION_FRONT_LEFT); + alSourcefv(myAlSources[1], AL_POSITION, POSITION_FRONT_RIGHT); + alSourcefv(myAlSources[2], AL_POSITION, POSITION_FRONT_CENTER); + alSourcefv(myAlSources[3], AL_POSITION, POSITION_REAR_LEFT); + alSourcefv(myAlSources[4], AL_POSITION, POSITION_REAR_RIGHT); + stalCheckErrors("alSource*5.0"); +} + void StAudioQueue::stalConfigureSources5_1() { alSourcefv(myAlSources[0], AL_POSITION, POSITION_FRONT_LEFT); alSourcefv(myAlSources[1], AL_POSITION, POSITION_FRONT_RIGHT); @@ -86,6 +101,18 @@ stalCheckErrors("alSource*5.1"); } +void StAudioQueue::stalConfigureSources7_1() { + alSourcefv(myAlSources[0], AL_POSITION, POSITION_FRONT_LEFT); + alSourcefv(myAlSources[1], AL_POSITION, POSITION_FRONT_RIGHT); + alSourcefv(myAlSources[2], AL_POSITION, POSITION_FRONT_CENTER); + alSourcefv(myAlSources[3], AL_POSITION, POSITION_LFE); + alSourcefv(myAlSources[4], AL_POSITION, POSITION_REAR_LEFT71); + alSourcefv(myAlSources[5], AL_POSITION, POSITION_REAR_RIGHT71); + alSourcefv(myAlSources[6], AL_POSITION, POSITION_SIDE_LEFT71); + alSourcefv(myAlSources[7], AL_POSITION, POSITION_SIDE_RIGHT71); + stalCheckErrors("alSource*7.1"); +} + bool StAudioQueue::stalInit() { StHandle aDevName = myAlDeviceName; if(!myAlCtx.create(*aDevName)) { @@ -373,8 +400,10 @@ deinit(); return false; } - } else if(myCodecCtx->channels == 6) { - if(myAlCtx.hasExtMultiChannel) { + } else if(myCodecCtx->channels == 5 + || myCodecCtx->channels == 6) { + if(myAlCtx.hasExtMultiChannel + && myCodecCtx->channels != 5) { if(myBufferSrc.getFormat() == PCM8_UNSIGNED) { myAlFormat = alGetEnumValue("AL_FORMAT_51CHN8"); myBufferOut.setFormat(PCM8_UNSIGNED); @@ -425,22 +454,75 @@ myBufferOut.setFormat(PCM16_SIGNED); } - myBufferOut.setupChannels(StChannelMap::CH51, StChannelMap::PCM, 6); - if(isReoderingNeeded()) { - // workaround for old FFmpeg - if(myCodec->id == CODEC_ID_AC3) { - myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::AC3, 1); - } else if(myCodec->id == CODEC_ID_VORBIS) { - myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::OGG, 1); + + if(myCodecCtx->channels == 5) { + myBufferOut.setupChannels(StChannelMap::CH50, StChannelMap::PCM, 5); + myBufferSrc.setupChannels(StChannelMap::CH50, StChannelMap::PCM, isPlanar ? myCodecCtx->channels : 1); + stalConfigureSources5_0(); + } else { + myBufferOut.setupChannels(StChannelMap::CH51, StChannelMap::PCM, 6); + if(isReoderingNeeded()) { + // workaround for old FFmpeg + if(myCodec->id == CODEC_ID_AC3) { + myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::AC3, 1); + } else if(myCodec->id == CODEC_ID_VORBIS) { + myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::OGG, 1); + } else { + myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::PCM, 1); + } } else { - myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::PCM, 1); + myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::PCM, isPlanar ? myCodecCtx->channels : 1); } - } else { - myBufferSrc.setupChannels(StChannelMap::CH51, StChannelMap::PCM, isPlanar ? myCodecCtx->channels : 1); + stalConfigureSources5_1(); } - stalConfigureSources5_1(); ST_DEBUG_LOG("OpenAL: multichannel extension (AL_FORMAT_51CHN16) not available"); } + } else if(myCodecCtx->channels == 8) { + if(myAlCtx.hasExtMultiChannel) { + if(myBufferSrc.getFormat() == PCM8_UNSIGNED) { + myAlFormat = alGetEnumValue("AL_FORMAT_71CHN8"); + myBufferOut.setFormat(PCM8_UNSIGNED); + } else if(myBufferSrc.getFormat() == PCM32_SIGNED || + myBufferSrc.getFormat() == PCM32FLOAT || + myBufferSrc.getFormat() == PCM64FLOAT) { + // use float32 format to reduce quality degradation + myAlFormat = alGetEnumValue("AL_FORMAT_71CHN32"); + myBufferOut.setFormat(PCM32FLOAT); + } else { + // default - int16_t + myAlFormat = alGetEnumValue("AL_FORMAT_71CHN16"); + myBufferOut.setFormat(PCM16_SIGNED); + } + + myBufferSrc.setupChannels(StChannelMap::CH40, StChannelMap::PCM, isPlanar ? myCodecCtx->channels : 1); + myBufferOut.setupChannels(StChannelMap::CH40, StChannelMap::PCM, 1); + stalConfigureSources1(); + } else { + if(myBufferSrc.getFormat() == PCM8_UNSIGNED) { + myAlFormat = AL_FORMAT_MONO8; + myBufferOut.setFormat(PCM8_UNSIGNED); + } else if(myBufferSrc.getFormat() == PCM64FLOAT && myAlCtx.hasExtFloat64) { + // use float64 extension + myAlFormat = alGetEnumValue("AL_FORMAT_MONO_DOUBLE_EXT"); + myBufferOut.setFormat(PCM64FLOAT); + } else if((myBufferSrc.getFormat() == PCM32_SIGNED || + myBufferSrc.getFormat() == PCM32FLOAT || + myBufferSrc.getFormat() == PCM64FLOAT) + && myAlCtx.hasExtFloat32) { + // use float32 extension to reduce quality degradation + myAlFormat = alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); + myBufferOut.setFormat(PCM32FLOAT); + } else { + // default - int16_t + myAlFormat = AL_FORMAT_MONO16; + myBufferOut.setFormat(PCM16_SIGNED); + } + + myBufferOut.setupChannels(StChannelMap::CH71, StChannelMap::PCM, 8); + myBufferSrc.setupChannels(StChannelMap::CH71, StChannelMap::PCM, isPlanar ? myCodecCtx->channels : 1); + stalConfigureSources7_1(); + ST_DEBUG_LOG("OpenAL: multichannel extension (AL_FORMAT_71CHN16) not available"); + } } fillCodecInfo(myCodec); return true; @@ -546,6 +628,11 @@ bool toTryToPlay = false; bool isQueued = false; if(aProcessed == 0 && aQueued < NUM_AL_BUFFERS) { + if(myBufferOut.isEmpty()) { + ST_DEBUG_LOG(" EMPTY BUFFER "); + return true; + } + stalCheckErrors("reset state"); ///ST_DEBUG_LOG("AL, queue more buffers " + aQueued + " / " + NUM_AL_BUFFERS); myPrevFormat = myAlFormat; @@ -554,7 +641,7 @@ alBufferData(myAlBuffers[aSrcId][aQueued], myAlFormat, myBufferOut.getPlane(aSrcId), (ALsizei )myBufferOut.getPlaneSize(), myBufferOut.getFreq()); - stalCheckErrors("alBufferData"); + stalCheckErrors("alBufferData1"); alSourceQueueBuffers(myAlSources[aSrcId], 1, &myAlBuffers[aSrcId][aQueued]); stalCheckErrors("alSourceQueueBuffers"); } @@ -596,7 +683,7 @@ alBufferData(alBuffIdToFill, myAlFormat, myBufferOut.getPlane(aSrcId), (ALsizei )myBufferOut.getPlaneSize(), myBufferOut.getFreq()); - stalCheckErrors("alBufferData"); + stalCheckErrors("alBufferData2"); alSourceQueueBuffers(myAlSources[aSrcId], 1, &alBuffIdToFill); stalCheckErrors("alSourceQueueBuffers"); } else { diff -Nru sview-13.10/StMoviePlayer/StVideo/StAudioQueue.h sview-14.01/StMoviePlayer/StVideo/StAudioQueue.h --- sview-13.10/StMoviePlayer/StVideo/StAudioQueue.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StAudioQueue.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -126,7 +126,9 @@ ST_LOCAL void stalConfigureSources1(); ST_LOCAL void stalConfigureSources2_0(); ST_LOCAL void stalConfigureSources4_0(); + ST_LOCAL void stalConfigureSources5_0(); ST_LOCAL void stalConfigureSources5_1(); + ST_LOCAL void stalConfigureSources7_1(); ST_LOCAL bool stalQueue(const double thePts); diff -Nru sview-13.10/StMoviePlayer/StVideo/StPCMBuffer.cpp sview-14.01/StMoviePlayer/StVideo/StPCMBuffer.cpp --- sview-13.10/StMoviePlayer/StVideo/StPCMBuffer.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StPCMBuffer.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ */ #define ST_MAX_AUDIO_FRAME_SIZE 192000 -StChannelMap::StChannelMap(const StChannelMap::Channels theChannels, +StChannelMap::StChannelMap(const StChannelMap::Channels theChannels, const StChannelMap::OrderRules theRules) : count(0), channels(theChannels) { @@ -35,11 +35,11 @@ } switch(theChannels) { case StChannelMap::CH10: - FL = FR = FC = LFE = RL = RR = 0; + FL = FR = FC = LFE = RL = RR = SL = SR = 0; count = 1; return; case StChannelMap::CH20: - FL = FC = LFE = RL = RR = 0; FR = 1; + FL = FC = LFE = RL = RR = SL = SR = 0; FR = 1; count = 2; return; case StChannelMap::CH40: @@ -48,10 +48,20 @@ FR = 1; RL = 2; RR = 3; - FC = LFE = 0; + FC = LFE = SL = SR = 0; + return; + case StChannelMap::CH50: + count = 5; + FL = 0; + FR = 1; + FC = 2; + RL = 3; + RR = 4; + LFE = SL = SR = 0; return; case StChannelMap::CH51: count = 6; + SL = SR = 0; switch(theRules) { case StChannelMap::PCM: { FL = 0; @@ -79,6 +89,17 @@ return; } } + case StChannelMap::CH71: + count = 8; + FL = 0; + FR = 1; + FC = 2; + LFE = 3; + RL = 4; + RR = 5; + SL = 6; + SR = 7; + return; } } @@ -350,6 +371,17 @@ myPlaneSize += anAddedPlaneSize; return true; } + case StChannelMap::CH50: { + for(size_t sampleSrcId(0), sampleOutId(0); sampleSrcId < aSamplesSrcCount; sampleSrcId += aSmplSrcInc, sampleOutId += aSmplOutInc) { + sampleConv(aBuffersSrc[0][sampleSrcId], aBuffersOut[0][sampleOutId]); + sampleConv(aBuffersSrc[1][sampleSrcId], aBuffersOut[1][sampleOutId]); + sampleConv(aBuffersSrc[2][sampleSrcId], aBuffersOut[2][sampleOutId]); + sampleConv(aBuffersSrc[3][sampleSrcId], aBuffersOut[3][sampleOutId]); + sampleConv(aBuffersSrc[4][sampleSrcId], aBuffersOut[4][sampleOutId]); + } + myPlaneSize += anAddedPlaneSize; + return true; + } case StChannelMap::CH51: { for(size_t sampleSrcId(0), sampleOutId(0); sampleSrcId < aSamplesSrcCount; sampleSrcId += aSmplSrcInc, sampleOutId += aSmplOutInc) { sampleConv(aBuffersSrc[0][sampleSrcId], aBuffersOut[0][sampleOutId]); @@ -362,6 +394,20 @@ myPlaneSize += anAddedPlaneSize; return true; } + case StChannelMap::CH71: { + for(size_t sampleSrcId(0), sampleOutId(0); sampleSrcId < aSamplesSrcCount; sampleSrcId += aSmplSrcInc, sampleOutId += aSmplOutInc) { + sampleConv(aBuffersSrc[0][sampleSrcId], aBuffersOut[0][sampleOutId]); + sampleConv(aBuffersSrc[1][sampleSrcId], aBuffersOut[1][sampleOutId]); + sampleConv(aBuffersSrc[2][sampleSrcId], aBuffersOut[2][sampleOutId]); + sampleConv(aBuffersSrc[3][sampleSrcId], aBuffersOut[3][sampleOutId]); + sampleConv(aBuffersSrc[4][sampleSrcId], aBuffersOut[4][sampleOutId]); + sampleConv(aBuffersSrc[5][sampleSrcId], aBuffersOut[5][sampleOutId]); + sampleConv(aBuffersSrc[6][sampleSrcId], aBuffersOut[6][sampleOutId]); + sampleConv(aBuffersSrc[7][sampleSrcId], aBuffersOut[7][sampleOutId]); + } + myPlaneSize += anAddedPlaneSize; + return true; + } default: return false; } diff -Nru sview-13.10/StMoviePlayer/StVideo/StPCMBuffer.h sview-14.01/StMoviePlayer/StVideo/StPCMBuffer.h --- sview-13.10/StMoviePlayer/StVideo/StPCMBuffer.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StPCMBuffer.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -95,9 +95,10 @@ //CH31, CH40, //CH41, + CH50, // 3 Front + 2 Rear CH51, // 3 Front + 2 Rear + LFE //CH61, - //CH71 + CH71 } Channels; typedef enum tagOrderRules { @@ -119,6 +120,8 @@ size_t LFE; //!< Low Frequency size_t RL; //!< Rear Left size_t RR; //!< Rear Right + size_t SL; //!< Side Left + size_t SR; //!< Side Right public: @@ -131,7 +134,9 @@ && FC == theOther.FC && LFE == theOther.LFE && RL == theOther.RL - && RR == theOther.RR; + && RR == theOther.RR + && SL == theOther.SL + && SR == theOther.SR; } ST_LOCAL bool operator!=(const StChannelMap& theOther) const { diff -Nru sview-13.10/StMoviePlayer/StVideo/StParamActiveStream.cpp sview-14.01/StMoviePlayer/StVideo/StParamActiveStream.cpp --- sview-13.10/StMoviePlayer/StVideo/StParamActiveStream.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StParamActiveStream.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,13 @@ myMutex(), myIsChanged(false) {} +int32_t StParamActiveStream::getListSize() const { + myMutex.lock(); + const int32_t aSize = int32_t(myList->size()); + myMutex.unlock(); + return aSize; +} + StHandle< StArrayList > StParamActiveStream::getList() const { myMutex.lock(); StHandle< StArrayList > aList = myList; diff -Nru sview-13.10/StMoviePlayer/StVideo/StParamActiveStream.h sview-14.01/StMoviePlayer/StVideo/StParamActiveStream.h --- sview-13.10/StMoviePlayer/StVideo/StParamActiveStream.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StParamActiveStream.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -74,6 +74,11 @@ */ ST_LOCAL bool wasChanged() const; + /** + * @return list size + */ + ST_LOCAL int32_t getListSize() const; + private: StHandle< StArrayList > myList; diff -Nru sview-13.10/StMoviePlayer/StVideo/StSubtitleQueue.cpp sview-14.01/StMoviePlayer/StVideo/StSubtitleQueue.cpp --- sview-13.10/StMoviePlayer/StVideo/StSubtitleQueue.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StSubtitleQueue.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011-2013 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include #include +#include + namespace { static const StString ST_CRLF_REDUNDANT = "\x0D\x0A"; static const StString ST_CRLF_REPLACEMENT = " \x0A"; @@ -39,10 +41,8 @@ : StAVPacketQueue(512), myOutQueue(theSubtitlesQueue), myThread(NULL), - myASS(), evDowntime(true), toQuit(false) { - // myThread = new StThread(threadFunction, (void* )this); } @@ -166,14 +166,50 @@ switch(aRect->type) { case SUBTITLE_BITMAP: { - // unsupported yet + if(aDuration < 0.001) { + aDuration = 3.0; // duration is always zero here... + } + + StHandle aNewSubItem = new StSubItem(aPts, aPts + aDuration); + aNewSubItem->Image.initTrash(StImagePlane::ImgRGBA, aRect->w, aRect->h); + + SwsContext* aCtxToRgb = sws_getContext(aRect->w, aRect->h, stAV::PIX_FMT::PAL8, + aRect->w, aRect->h, stAV::PIX_FMT::RGBA32, + SWS_BICUBIC, NULL, NULL, NULL); + if(aCtxToRgb == NULL) { + break; + } + + uint8_t* aDstData[4] = { + (uint8_t* )aNewSubItem->Image.getData(), + NULL, + NULL, + NULL + }; + const int aDstLinesize[4] = { + (int )aNewSubItem->Image.getSizeRowBytes(), + 0, + 0, + 0 + }; + + sws_scale(aCtxToRgb, + aRect->pict.data, aRect->pict.linesize, + 0, aRect->h, + aDstData, aDstLinesize); + sws_freeContext(aCtxToRgb); + + /*ST_DEBUG_LOG(" |" + aRectId + "/" + aSubtitle.num_rects + "| " //+ aRect->x + "x" + aRect->y + " WH= " + + aRect->w + "x" + aRect->h + " c= " + aRect->nb_colors + + " pts= " + aPts + + " dur= " + aDuration);*/ + myOutQueue->push(aNewSubItem); break; } case SUBTITLE_TEXT: { - StString aText = aRect->text; - aText.replaceFast(ST_CRLF_REDUNDANT, ST_CRLF_REPLACEMENT); // remove redundant CR symbols - StHandle aNewSubItem = new StSubItem(aText, - aPts, aPts + aDuration); + StHandle aNewSubItem = new StSubItem(aPts, aPts + aDuration); + aNewSubItem->Text = aRect->text; + aNewSubItem->Text.replaceFast(ST_CRLF_REDUNDANT, ST_CRLF_REPLACEMENT); // remove redundant CR symbols myOutQueue->push(aNewSubItem); break; } @@ -208,10 +244,9 @@ #endif } else { // just plain text - StString aText = (const char* )aPacket->getData(); - aText.replaceFast(ST_CRLF_REDUNDANT, ST_CRLF_REPLACEMENT); // remove redundant CR symbols - StHandle aNewSubItem = new StSubItem(aText, - aPts, aPts + aDuration); + StHandle aNewSubItem = new StSubItem(aPts, aPts + aDuration); + aNewSubItem->Text = (const char* )aPacket->getData(); + aNewSubItem->Text.replaceFast(ST_CRLF_REDUNDANT, ST_CRLF_REPLACEMENT); // remove redundant CR symbols myOutQueue->push(aNewSubItem); } diff -Nru sview-13.10/StMoviePlayer/StVideo/StSubtitleQueue.h sview-14.01/StMoviePlayer/StVideo/StSubtitleQueue.h --- sview-13.10/StMoviePlayer/StVideo/StSubtitleQueue.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StSubtitleQueue.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011-2013 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -Nru sview-13.10/StMoviePlayer/StVideo/StSubtitlesASS.cpp sview-14.01/StMoviePlayer/StVideo/StSubtitlesASS.cpp --- sview-13.10/StMoviePlayer/StVideo/StSubtitlesASS.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StSubtitlesASS.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -252,7 +252,7 @@ aText.replaceFast(ST_CRLF_REDUNDANT, ST_CRLF_REPLACEMENT); aText.replaceFast(ST_ASS_NEWLINE, ST_NEWLINE_REPLACEMENT); aText.replaceFast(ST_ASS_NEWLINE2, ST_NEWLINE_REPLACEMENT); - StHandle aNewSubItem = new StSubItem(aText, - thePts, thePts + aDuration); + StHandle aNewSubItem = new StSubItem(thePts, thePts + aDuration); + aNewSubItem->Text = aText; return aNewSubItem; } diff -Nru sview-13.10/StMoviePlayer/StVideo/StSubtitlesASS.h sview-14.01/StMoviePlayer/StVideo/StSubtitlesASS.h --- sview-13.10/StMoviePlayer/StVideo/StSubtitlesASS.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StSubtitlesASS.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2011 Kirill Gavrilov + * Copyright © 2011-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -94,4 +94,4 @@ }; -#endif //__StSubtitlesASS_h_ +#endif // __StSubtitlesASS_h_ diff -Nru sview-13.10/StMoviePlayer/StVideo/StVideo.cpp sview-14.01/StMoviePlayer/StVideo/StVideo.cpp --- sview-13.10/StMoviePlayer/StVideo/StVideo.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StVideo.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,8 @@ #include "../StMoviePlayerStrings.h" #include -#include + +using namespace StMoviePlayerStrings; namespace { static const char ST_AUDIOS_MIME_STRING[] = ST_VIDEO_PLUGIN_AUDIO_MIME_CHAR; @@ -63,6 +64,11 @@ stAV::init(); myPlayList->setExtensions(myMimesVideo.getExtensionsList()); + myTracksExt = myMimesSubs.getExtensionsList(); + StArrayList anAudioExt = myMimesAudio.getExtensionsList(); + for(size_t anExtIter = 0; anExtIter < anAudioExt.size(); ++anExtIter) { + myTracksExt.add(anAudioExt.getValue(anExtIter)); + } params.UseGpu = new StBoolParam(false); params.activeAudio = new StParamActiveStream(); @@ -273,13 +279,20 @@ StString aTitleString, aFolder; StFileNode::getFolderAndFile(theFileToLoad, aFolder, aTitleString); - myFileInfoTmp->myInfo.add(StArgument("File name", aTitleString)); + if(myFileInfoTmp->Path.isEmpty()) { + myFileInfoTmp->Path = theFileToLoad; + } + StDictEntry& aFileNamePair = myFileInfoTmp->Info.addChange(tr(INFO_FILE_NAME)); + if(!aFileNamePair.getValue().isEmpty()) { + aFileNamePair.changeValue() += "\n"; + } + aFileNamePair.changeValue() += aTitleString; // collect metadata for(stAV::meta::Tag* aTag = stAV::meta::findTag(aFormatCtx->metadata, "", NULL, stAV::meta::SEARCH_IGNORE_SUFFIX); aTag != NULL; aTag = stAV::meta::findTag(aFormatCtx->metadata, "", aTag, stAV::meta::SEARCH_IGNORE_SUFFIX)) { - myFileInfoTmp->myInfo.add(StArgument(aTag->key, aTag->value)); + myFileInfoTmp->Info.add(StArgument(aTag->key, aTag->value)); } theMaxDuration = stMax(theMaxDuration, stAV::unitsToSeconds(aFormatCtx->duration)); @@ -296,19 +309,22 @@ if(myVideoMaster->isInitialized()) { const int aSizeX = myVideoMaster->sizeX(); const int aSizeY = myVideoMaster->sizeY(); - StString aDimsStr = StString() + aSizeX + " x " + aSizeY; const int aCodedSizeX = myVideoMaster->getCodedSizeX(); const int aCodedSizeY = myVideoMaster->getCodedSizeY(); + StString aDimsStr = StString() + aSizeX + " x " + aSizeY; if(aCodedSizeX != aSizeX || aCodedSizeY != aSizeY) { aDimsStr += StString(" (") + aCodedSizeX + " x " + aCodedSizeY + ")"; } - myFileInfoTmp->myInfo.add(StArgument("Video Dimensions", aDimsStr)); - myFileInfoTmp->myInfo.add(StArgument("Pixel Format", + + StDictEntry& aDimInfo = myFileInfoTmp->Info.addChange(tr(INFO_DIMENSIONS)); + aDimInfo.changeValue() = aDimsStr; + + myFileInfoTmp->Info.add(StArgument(tr(INFO_PIXEL_FORMAT), myVideoMaster->getPixelFormatString())); - myFileInfoTmp->myInfo.add(StArgument("Pixel Ratio", + myFileInfoTmp->Info.add(StArgument(tr(INFO_PIXEL_RATIO), StString() + myVideoMaster->getPixelRatio())); - myFileInfoTmp->myInfo.add(StArgument("Duration", + myFileInfoTmp->Info.add(StArgument(tr(INFO_DURATION), StFormatTime::formatSeconds(theMaxDuration))); } } else if(!myVideoSlave->isInitialized()) { @@ -319,14 +335,18 @@ const int aSizeX = myVideoSlave->sizeX(); const int aSizeY = myVideoSlave->sizeY(); - StString aDimsStr = StString() + aSizeX + " x " + aSizeY; const int aCodedSizeX = myVideoSlave->getCodedSizeX(); const int aCodedSizeY = myVideoSlave->getCodedSizeY(); + StString aDimsStr = StString() + aSizeX + " x " + aSizeY; if(aCodedSizeX != aSizeX || aCodedSizeY != aSizeY) { aDimsStr += StString(" (") + aCodedSizeX + " x " + aCodedSizeY + ")"; } - myFileInfoTmp->myInfo.add(StArgument("Video Dimensions (slave)", aDimsStr)); + aDimsStr += " [2]"; + + StDictEntry& aDimInfo = myFileInfoTmp->Info.addChange(tr(INFO_DIMENSIONS)); + aDimInfo.changeValue() += "\n"; + aDimInfo.changeValue() += aDimsStr; if(myVideoMaster->getSrcFormat() == ST_V_SRC_AUTODETECT) { myVideoMaster->setSlave(myVideoSlave); @@ -420,10 +440,40 @@ return false; } } else { - if(!addFile(theNewSource->getPath(), + const StString aFullPath = theNewSource->getPath(); + if(!addFile(aFullPath, aStreamsListA, aStreamsListS, aDuration)) { return false; } + + // search for additional tracks + if(params.ToSearchSubs->getValue() + && myVideoMaster->isInitialized() + && !StFileNode::isRemoteProtocolPath(aFullPath)) { + StString aFolder, aFileName; + StFileNode::getFolderAndFile(aFullPath, aFolder, aFileName); + if(aFileName.getLength() > 8) { // ignore too short names + StString aName, anExtension, aTrackName, aTrackExtension; + StFileNode::getNameAndExtension(aFileName, aName, anExtension); + if(myTracksFolder.getPath() != aFolder) { + // notice that playlist re-loading is not checked here... + myTracksFolder.setSubPath(aFolder); + myTracksFolder.init(myTracksExt, 1); + } + for(size_t aNodeIter = 0; aNodeIter < myTracksFolder.size(); ++aNodeIter) { + const StFileNode* aNode = myTracksFolder.getValue(aNodeIter); + const StString& aTrackFileName = aNode->getSubPath(); + StFileNode::getNameAndExtension(aTrackFileName, aTrackName, aTrackExtension); + if(aFileName != aTrackFileName + && aTrackName.isStartsWithIgnoreCase(aName)) { + //myPlayList->addToNode(aCurrFile, aFilePath); + //myPlayList->getCurrentFile(theNewSource, theNewParams) + addFile(aNode->getPath(), + aStreamsListA, aStreamsListS, aDuration); + } + } + } + } } if(!myVideoMaster->isInitialized() && !myAudio->isInitialized()) { @@ -431,19 +481,25 @@ return false; } - StArgument aTitle = myFileInfoTmp->myInfo["TITLE"]; + StArgument aTitle = myFileInfoTmp->Info["TITLE"]; if(!aTitle.isValid()) { - aTitle = myFileInfoTmp->myInfo["title"]; + aTitle = myFileInfoTmp->Info["title"]; } - StArgument anArtist = myFileInfoTmp->myInfo["ARTIST"]; + StArgument anArtist = myFileInfoTmp->Info["ARTIST"]; if(!anArtist.isValid()) { - anArtist = myFileInfoTmp->myInfo["artist"]; + anArtist = myFileInfoTmp->Info["artist"]; } if(anArtist.isValid() && aTitle.isValid()) { - myPlayList->setTitle(theNewParams, anArtist.getValue() + " - " + aTitle.getValue()); + StString anArtistStr = anArtist.getValue(); + StString aTitleStr = aTitle.getValue(); + anArtistStr.rightAdjust(); + aTitleStr .rightAdjust(); + myPlayList->setTitle(theNewParams, anArtistStr + " - " + aTitleStr); } else if(aTitle.isValid()) { - if(aTitle.getValue().getLength() < 20) { + StString aTitleStr = aTitle.getValue(); + aTitleStr.rightAdjust(); + if(aTitleStr.getLength() < 20) { // protection against messed title StString aFileName; StString aFolder; @@ -454,15 +510,15 @@ aPath = theNewSource->getPath(); } StFileNode::getFolderAndFile(aPath, aFolder, aFileName); - myPlayList->setTitle(theNewParams, aTitle.getValue() + " (" + aFileName + ")"); + myPlayList->setTitle(theNewParams, aTitleStr + " (" + aFileName + ")"); } else { - myPlayList->setTitle(theNewParams, aTitle.getValue()); + myPlayList->setTitle(theNewParams, aTitleStr); } } myCurrNode = theNewSource; myCurrParams = theNewParams; - myFileInfoTmp->myId = myCurrParams; + myFileInfoTmp->Id = myCurrParams; params.activeAudio->setList(aStreamsListA, aStreamsListA->isEmpty() ? -1 : 0); params.activeSubtitles->setList(aStreamsListS, -1); // do not show subtitles by default @@ -515,10 +571,13 @@ } if(!isSeekDone) { // at last - try to seek the format context itself... - int64_t aSeekTarget = stAV::secondsToUnits(theSeekPts); - isSeekDone = av_seek_frame(theFormatCtx, -1, aSeekTarget, 0) >= 0; + const int aFlags = toSeekBack ? AVSEEK_FLAG_BACKWARD : 0; + int64_t aSeekTarget = stAV::secondsToUnits(theSeekPts); + isSeekDone = av_seek_frame(theFormatCtx, -1, aSeekTarget, aFlags) >= 0; if(!isSeekDone) { - ST_DEBUG_LOG("Seeking disaster!"); + #ifdef __ST_DEBUG__ + ST_ERROR_LOG("Disaster! Seeking to " + theSeekPts + " [" + theFormatCtx->filename + "] has failed."); + #endif } } } @@ -527,16 +586,16 @@ const signed int theStreamId, const double theSeekPts, const bool toSeekBack) { + const int aFlags = toSeekBack ? AVSEEK_FLAG_BACKWARD : 0; AVStream* aStream = theFormatCtx->streams[theStreamId]; int64_t aSeekTarget = stAV::secondsToUnits(aStream, theSeekPts + stAV::unitsToSeconds(aStream, aStream->start_time)); - - bool isSeekDone = av_seek_frame(theFormatCtx, theStreamId, aSeekTarget, 0) >= 0; + bool isSeekDone = av_seek_frame(theFormatCtx, theStreamId, aSeekTarget, aFlags) >= 0; // try 10 more times in backward direction to work-around huge duration between key frames // will not work for some streams with undefined cur_dts (AV_NOPTS_VALUE)!!! for(int aTries = 10; isSeekDone && toSeekBack && aTries > 0 && (aStream->cur_dts > aSeekTarget); --aTries) { aSeekTarget -= stAV::secondsToUnits(aStream, 1.0); - isSeekDone = av_seek_frame(theFormatCtx, theStreamId, aSeekTarget, 0) >= 0; + isSeekDone = av_seek_frame(theFormatCtx, theStreamId, aSeekTarget, aFlags) >= 0; } if(!isSeekDone) { @@ -969,7 +1028,8 @@ fileToSave += StString('.') + saveExt; } ST_DEBUG_LOG("Save snapshot to the path '" + fileToSave + '\''); - if(!dataResult->save(fileToSave, theImgType)) { + if(!dataResult->save(fileToSave, theImgType, + toSaveStereo ? ST_V_SRC_SIDE_BY_SIDE : ST_V_SRC_AUTODETECT)) { // TODO (Kirill Gavrilov#7) signals.onError(dataResult->getState()); return false; @@ -985,19 +1045,42 @@ myEventMutex.lock(); StHandle anInfo = myFileInfo; myEventMutex.unlock(); - if(anInfo.isNull() || anInfo->myId != theParams) { + if(anInfo.isNull() || anInfo->Id != theParams) { return NULL; } - anInfo->myCodecs.clear(); - anInfo->myCodecs.add(StArgument("vcodec1", myVideoMaster->getCodecInfo())); - anInfo->myCodecs.add(StArgument("vcodec2", myVideoSlave->getCodecInfo())); - anInfo->myCodecs.add(StArgument("audio", myAudio->getCodecInfo())); - anInfo->myCodecs.add(StArgument("subtitles", mySubtitles->getCodecInfo())); + // continuously read source format since it can be stored in frame + anInfo->SrcFormat = myVideoMaster->getSrcFormatInfo(); + + anInfo->Codecs.clear(); + anInfo->Codecs.add(StArgument("vcodec1", myVideoMaster->getCodecInfo())); + anInfo->Codecs.add(StArgument("vcodec2", myVideoSlave->getCodecInfo())); + anInfo->Codecs.add(StArgument("audio", myAudio->getCodecInfo())); + anInfo->Codecs.add(StArgument("subtitles", mySubtitles->getCodecInfo())); return anInfo; } +void StVideo::doRemovePhysically(const StHandle& theFile) { + if(theFile.isNull() + || theFile->size() != 0) { + return; + } + + const StHandle aCurrent = myPlayList->getCurrentFile(); + const bool toPlayNext = !aCurrent.isNull() + && aCurrent->size() == 0 + && aCurrent->getPath().isEquals(theFile->getPath()); + + myEventMutex.lock(); + myFilesToDelete.add(theFile); + myEventMutex.unlock(); + + if(toPlayNext) { + doLoadNext(); + } +} + void StVideo::mainLoop() { bool isOpenSuccess = false; StHandle aFileToLoad; @@ -1044,14 +1127,30 @@ myVideoTimer.nullify(); - for(;;) { - if(popPlayEvent(aDummy, aDummyBool) == ST_PLAYEVENT_NEXT) { - if(toQuit) { - return; + myEventMutex.lock(); + if(!myFilesToDelete.isEmpty()) { + const StHandle aCurrent = myPlayList->getCurrentFile(); + if(!aCurrent.isNull() + && aCurrent->getPath().isEquals(myFilesToDelete[0]->getPath())) { + close(); + } + for(size_t anIter = 0; anIter < myFilesToDelete.size(); ++anIter) { + const StHandle& aNode = myFilesToDelete[anIter]; + if(!myPlayList->removePhysically(aNode)) { + signals.onError(StString("File can not be deleted!\n" + aNode->getPath())); } - } else { + } + myFilesToDelete.clear(); + } + myEventMutex.unlock(); + + for(;;) { + if(popPlayEvent(aDummy, aDummyBool) != ST_PLAYEVENT_NEXT) { myPlayList->walkToNext(false); } + if(toQuit) { + return; + } isOpenSuccess = false; if(myPlayList->getCurrentFile(aFileToLoad, aFileParams)) { isOpenSuccess = openSource(aFileToLoad, aFileParams); diff -Nru sview-13.10/StMoviePlayer/StVideo/StVideo.h sview-14.01/StMoviePlayer/StVideo/StVideo.h --- sview-13.10/StMoviePlayer/StVideo/StVideo.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StVideo.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2007-2013 Kirill Gavrilov + * Copyright © 2007-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ #include #include #include +#include // forward declarations class StSubQueue; @@ -37,12 +38,19 @@ struct StMovieInfo { - StHandle myId; - StArgumentsMap myInfo; - StArgumentsMap myCodecs; + StHandle Id; + StArgumentsMap Info; + StArgumentsMap Codecs; + StString Path; //!< file path + StFormatEnum SrcFormat; //!< source format as stored in file metadata + bool IsSavable; //!< indicate that file can be saved without re-encoding + + StMovieInfo() : SrcFormat(ST_V_SRC_AUTODETECT), IsSavable(false) {} }; +template<> inline void StArray< StHandle >::sort() {} + /** * Special class for video playback. */ @@ -140,7 +148,7 @@ */ ST_LOCAL StHandle getFileInfo(const StHandle& theParams) const; - public: //!< callback Slots + public: //! @name callback Slots /** * Interrupt the playback and load current position in playlist. @@ -150,6 +158,11 @@ } /** + * Add file node for removal. + */ + ST_LOCAL void doRemovePhysically(const StHandle& theFile); + + /** * Save current displayed frame. */ ST_LOCAL void doSaveSnapshotAs(const size_t theImgType) { @@ -180,17 +193,18 @@ ST_LOCAL void setAudioDelay(const float theDelaySec); - public: //!< Properties + public: //! @name Properties struct { StHandle UseGpu; //!< use video decoding on GPU when available + StHandle ToSearchSubs; //!< automatically search for additional subtitles/audio track files nearby video file StHandle activeAudio; //!< active Audio stream StHandle activeSubtitles; //!< active Subtitles stream } params; - public: //!< Signals + public: //! @name Signals /** * All callback handlers should be thread-safe. @@ -261,6 +275,10 @@ private: //! @name auxiliary methods + ST_LOCAL const StString& tr(const size_t theId) const { + return myLangMap->getValue(theId); + } + /** * Just redirect callback slot. */ @@ -358,6 +376,11 @@ StHandle myCurrParams; //!< paramters for active file node StHandle myTextureQueue; //!< decoded frames queue + StArrayList myTracksExt; //!< extra tracks extensions list + StFolder myTracksFolder; //!< cached list of subtitles/audio tracks in the current folder + StArrayList< StHandle > + myFilesToDelete;//!< file nodes for removal + StHandle myVideoTimer; //!< video refresh timer (Audio -> Video sync) mutable StMutex myEventMutex; //!< lock for thread-safety double myDuration; //!< active file duration in seconds @@ -372,4 +395,4 @@ }; -#endif //__StVideo_h_ +#endif // __StVideo_h_ diff -Nru sview-13.10/StMoviePlayer/StVideo/StVideoQueue.cpp sview-14.01/StMoviePlayer/StVideo/StVideoQueue.cpp --- sview-13.10/StMoviePlayer/StVideo/StVideoQueue.cpp 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StVideoQueue.cpp 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -691,6 +691,16 @@ } // we currently allow to override source format stored in metadata + #ifdef ST_AV_NEWSTEREO + AVFrameSideData* aSideData = av_frame_get_side_data(myFrame.Frame, AV_FRAME_DATA_STEREO3D); + if(aSideData != NULL) { + AVStereo3D* aStereo = (AVStereo3D* )aSideData->data; + mySrcFormatInfo = stAV::stereo3dAvToSt(aStereo->type); + if(aStereo->flags & AV_STEREO3D_FLAG_INVERT) { + mySrcFormatInfo = st::formatReversed(mySrcFormatInfo); + } + } + #endif if(stAV::meta::readTag(myFrame.Frame, THE_SRC_MODE_KEY, aTagValue)) { for(size_t aSrcId = 0;; ++aSrcId) { const StFFmpegStereoFormat& aFlag = STEREOFLAGS[aSrcId]; @@ -765,7 +775,7 @@ if(isOddNumber(myFramesCounter)) { myCachedFrame.fill(myDataAdp); } else { - pushFrame(myCachedFrame, myDataAdp, aPacket->getSource(), ST_V_SRC_SEPARATE_FRAMES, myFramePts); + pushFrame(myCachedFrame, myDataAdp, aPacket->getSource(), ST_V_SRC_PAGE_FLIP, myFramePts); } ++myFramesCounter; } else { diff -Nru sview-13.10/StMoviePlayer/StVideo/StVideoQueue.h sview-14.01/StMoviePlayer/StVideo/StVideoQueue.h --- sview-13.10/StMoviePlayer/StVideo/StVideoQueue.h 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/StVideo/StVideoQueue.h 2014-01-30 10:45:03.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright © 2009-2013 Kirill Gavrilov + * Copyright © 2009-2014 Kirill Gavrilov * * StMoviePlayer program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,6 +100,16 @@ return stAV::PIX_FMT::getString(myCodecCtx->pix_fmt); } + /** + * @return sterescopic information stored in file + */ + ST_LOCAL StFormatEnum getSrcFormatInfo() const { + return mySrcFormatInfo; + } + + /** + * @return source format detection rule + */ ST_LOCAL StFormatEnum getSrcFormat() const { return mySrcFormat; } diff -Nru sview-13.10/StMoviePlayer/lang/english/StMoviePlayer.lng sview-14.01/StMoviePlayer/lang/english/StMoviePlayer.lng --- sview-13.10/StMoviePlayer/lang/english/StMoviePlayer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/english/StMoviePlayer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -4,7 +4,7 @@ -------- 1002="Swap Left/Right" 1003="UnSwap Left/Right" -1004="Source format:\n" +1004="Stereoscopic format:" 1015="Open another movie" 1020="Play/Pause" 1021="Show/Hide playlist" @@ -14,7 +14,8 @@ 1100="Media" 1101="Open Movie..." 1102="Save Snapshot..." -1103="Source stereo format" +1103="Stereoscopic format" +1170="File info" 1104="Audio Device" 1105="Shuffle" 1106="Recent files" @@ -72,7 +73,7 @@ 1301="None" 1302="Audio/Video delay" 1303="Attach from file" -1320="Audio/Video syncronization" +1320="Audio/Video synchronization" 1321="Enter positive value if audio appears earlier than video and negative otherwise." 1322="Audio delay:" 1323="second(s)" @@ -81,6 +82,9 @@ 1353="Attach from file" 1354="Font Size" 1355="Parallax" +1356="Text parser" +1360="Plain text" +1361="Lite HTML" 1400="Change device" 1401="About Plugin..." 1402="FPS Control" @@ -96,7 +100,7 @@ 1506="User tips" 1507="Experimental features" 1508="About system" -1509="Scale Interface" +1509="Interface Scale" 1520="Now" 1521="Each day" 1522="Each week" @@ -112,13 +116,59 @@ 1593="Force HiDPI 2X" 2000="Choose the video file to open" 2001="Choose LEFT video file to open" -2001="Choose RIGHT video file to open" +2002="Choose RIGHT video file to open" +2003="File Info" +2004="File information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="Active decoders:" 2010="Choose location to save snapshot" 2011="Nothing to save!" 2012="Snapshot not available!" 3000="sView - Movie Player" 3001="version" -3002="Movie player allows you to play stereoscopic video.\n © 2007-2013 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" +3002="Movie player allows you to play stereoscopic video.\n © 2007-2014 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" 3003="A new version of sView is available on the official site www.sview.ru.\nPlease update your program." +3004="System Info" 4000="Close" +4001="Cancel" 4005="Reset" +4006="Save" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name(s)" +5003="Video dimensions" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Pixel format" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5010="Duration" +5300="Title" +5301="Composer" +5302="Artist" +5303="Album artist" +5304="Album" +5305="Disc" +5306="Nb. of discs" +5307="Genre" +5308="Comment" +5309="Notes" +5310="Description" +5311="Publisher" +5312="Copyright" +5313="Encoder" +5314="Engineer" +5315="Source" +5316="Creation time" +5317="Date" +5318="Year" +5319="Language" +5320="Track" +5321="Nb. of tracks" +5322="Track gain" +5323="Track peak" +5324="Album gain" +5325="Album peak" diff -Nru sview-13.10/StMoviePlayer/lang/french/StMoviePlayer.lng sview-14.01/StMoviePlayer/lang/french/StMoviePlayer.lng --- sview-13.10/StMoviePlayer/lang/french/StMoviePlayer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/french/StMoviePlayer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -4,7 +4,7 @@ -------- 1002="Gauche/Droite inversé" 1003="Gauche/Droite normal" -1004="Format entrée:\n" +1004="Format entrée:" 1015="Ouvre autre vidéo" 1020="Lecture/Pause" 1021="Affiche/Masque playlist" @@ -15,6 +15,7 @@ 1101="Ouvre Video..." 1102="Enregistre Capture..." 1103="Format Stéréo d'entrée" +1170="File info" 1104="Audio Device" 1105="Shuffle" 1106="Recent files" @@ -72,7 +73,7 @@ 1301="None" 1302="Audio/Video delay" 1303="Attach from file" -1320="Audio/Video syncronization" +1320="Audio/Video synchronization" 1321="Enter positive value if audio appears earlier than video and negative otherwise." 1322="Audio delay:" 1323="second(s)" @@ -81,6 +82,9 @@ 1353="Attach from file" 1354="Font Size" 1355="Parallax" +1356="Text parser" +1360="Plain text" +1361="Lite HTML" 1400="Changer la sortie" 1401="A propos du Plugin..." 1402="Contrôle de I/S" @@ -112,13 +116,59 @@ 1593="Force HiDPI 2X" 2000="Choix du fichier vidéo à ouvrir" 2001="Choix du fichier vidéo Gauche à ouvrir" -2001="Choix du fichier vidéo Droite à ouvrir" +2002="Choix du fichier vidéo Droite à ouvrir" +2003="File Info" +2004="File information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="Active decoders:" 2010="Emplacement de la capture écran" 2011="Rien à enregistrer!" 2012="Capture non disponible!" 3000="sView - Movie Player" 3001="version" -3002="Movie Player vous permet d'ouvrir des vidéo stéréoscopiques.\n © 2007-2013 Kirill Gavrilov \nSite Officiel: www.sview.ru\n\nThis program distributed under GPL3.0" +3002="Movie Player vous permet d'ouvrir des vidéo stéréoscopiques.\n © 2007-2014 Kirill Gavrilov \nSite Officiel: www.sview.ru\n\nThis program distributed under GPL3.0" 3003="Une nouvelle version de sView est disponible sur le site officiel www.sview.ru.\nSVP, mettez votre programme à jour." +3004="System Info" 4000="Fermer" +4001="Cancel" 4005="Reset" +4006="Enregistre" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name(s)" +5003="Résolution vidéo" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Pixel format" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5010="Durée" +5300="Title" +5301="Composer" +5302="Artist" +5303="Album artist" +5304="Album" +5305="Disc" +5306="Nb. of discs" +5307="Genre" +5308="Comment" +5309="Notes" +5310="Description" +5311="Publisher" +5312="Copyright" +5313="Encoder" +5314="Engineer" +5315="Source" +5316="Creation time" +5317="Date" +5318="Année" +5319="Language" +5320="Track" +5321="Nb. of tracks" +5322="Track gain" +5323="Track peak" +5324="Album gain" +5325="Album peak" diff -Nru sview-13.10/StMoviePlayer/lang/german/StMoviePlayer.lng sview-14.01/StMoviePlayer/lang/german/StMoviePlayer.lng --- sview-13.10/StMoviePlayer/lang/german/StMoviePlayer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/german/StMoviePlayer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,174 @@ +German translation file for StMoviePlayer program +@author Kirill Gavrilov + +-------- +1002="L&R Vertauschen An" +1003="L&R Vertauschen Aus" +1004="Quellformat:" +1015="Öffnen anderen Film" +1020="Wiedergabe/Pause" +1021="Anzeigen/ausblenden Wiedergabeliste" +1022="Vorheriges Stück" +1023="Nachfolgendes Stück" +1029="Vollbildmodus/Fenstermodus" +1100="Medien" +1101="Film öffnen..." +1102="Videoschnappschuss speichern..." +1103="Quelle Stereo-Format" +1170="File info" +1104="Audiogerät" +1105="Shuffle" +1106="Zuletzt geöffnete Medien öffnen" +1107="GPU-basierter Video-Dekodierung" +1108="Web UI" +1109="Beenden" +1110="Einer Datei" +1111="Zwei Dateien" +1130="Autoerkennung" +1131="Mono" +1132="Schielend" +1133="Parallel Pair" +1134="Over/Under (R/L)" +1135="Over/Under (L/R)" +1136="Interlaced" +1137="Anaglyph Rot/Cyan" +1138="Anaglyph Grün/Rot+Blau" +1139="Anaglyph Gelb/Blau" +1140="Rahmen-sequentielle" +1141="2x720p in 1080p tiled" +1142="2 Streams" +1160="Verlauf löschen" +1180="Turn Off" +1181="Starten einmal" +1182="Starten jedesmal" +1185="Show errors" +1186="Web UI can not be started on {0} port!" +1200="Ansicht" +1201="Stereo-Ausgang" +1202="Vollbild" +1203="Zurückstellen" +1204="L&R Vertauschen" +1205="Seitenverhältnis" +1206="Glatte Filter" +1207="Bildeinstellung" +1208="Oberfläche" +1210="Stereo" +1211="Linke Bild" +1212="Rechte Bild" +1213="Parallel Pair" +1214="Cross-eyed" +1250="Quelle" +1251="Halten beim Neustart" +1260="Nearest" +1261="Linear" +1262="Blend Deinterlace" +1270="Auf Standardwerte zurücksetzen" +1271="Helligkeit" +1272="Sättigung" +1273="Gamma" +1280="Fläche" +1281="Kugel" +1282="Zylinder" +1300="Audio" +1301="keiner" +1302="Audio/Video Verzögerung" +1303="Bringen aus der Datei" +1320="Audio/Video Synchronisierung" +1321="Enter positive value if audio appears earlier than video and negative otherwise." +1322="Audioverzögerung:" +1323="Sek." +1350="Untertitel" +1351="keiner" +1353="Bringen aus der Datei" +1354="Schriftgröße" +1355="Parallax" +1356="Text parser" +1360="Plain text" +1361="Lite HTML" +1400="Gerät ändern" +1401="Über Plugin..." +1402="FPS Kontrolle" +1420="VSync" +1421="Zeige Meter" +1422="Reduce CPU usage" +1500="Hilfe" +1501="Über..." +1502="Nach Updates suchen" +1503="Lizenztext" +1504="Language" +1505="Blockieren Schlaf" +1506="Anwendertipps" +1507="Experimentelle Funktionen" +1508="Über System" +1509="Maßstab Benutzeroberfläche" +1520="jetzt" +1521="täglich" +1522="wöchentlich" +1523="jährlich" +1524="nie" +1550="nie" +1551="immer" +1552="während der Wiedergabe" +1553="Wenn in Vollbild" +1590="klein" +1591="normal" +1592="groß" +1593="erzwingen HiDPI 2X" +2000="Wählen die Videodatei zu öffnen" +2001="Wählen die linke Videodatei zu öffnen" +2002="Wählen die rechte Videodatei zu öffnen" +2003="File Info" +2004="File information is unavailable" +2005="Löschen von Dateien" +2006="Do you really want to completely remove this file?" +2007="Active decoders:" +2010="Wählen einen Speicherort für Videoschnappschuss" +2011="Nichts zu speichern!" +2012="Schnappschuss ist nicht verfügbar!" +3000="sView - Movie Player" +3001="Version" +3002="Movie player allows you to play stereoscopic video.\n © 2007-2014 Kirill Gavrilov \nOfficial site: www.sview.ru\n\nThis program distributed under GPL3.0" +3003="A new version of sView is available on the official site www.sview.ru.\nPlease update your program." +3004="System Info" +4000="Schließen" +4001="Cancel" +4005="Reset" +4006="Speichern" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name(s)" +5003="Video dimensions" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Pixel format" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5010="Dauer" +5300="Titel" +5301="Composer" +5302="Artist" +5303="Album artist" +5304="Album" +5305="Disc" +5306="Nb. of discs" +5307="Genre" +5308="Kommentar" +5309="Notes" +5310="Beschreibung" +5311="Publisher" +5312="Copyright" +5313="Encoder" +5314="Engineer" +5315="Source" +5316="Creation time" +5317="Datum" +5318="Jahr" +5319="Sprache" +5320="Track" +5321="Nb. of tracks" +5322="Track gain" +5323="Track peak" +5324="Album gain" +5325="Album peak" diff -Nru sview-13.10/StMoviePlayer/lang/korean/StMoviePlayer.lng sview-14.01/StMoviePlayer/lang/korean/StMoviePlayer.lng --- sview-13.10/StMoviePlayer/lang/korean/StMoviePlayer.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/korean/StMoviePlayer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1,175 @@ +Korean translation file for StMoviePlayer program +@author Kirill Gavrilov +@translator Kwon Daesuk + +-------- +1002="좌/우 교체" +1003="좌/우 복원" +1004="소스 형식:" +1015="다른 영상 열기" +1020="재생/멈춤" +1021="재생 목록 보이기/감추기" +1022="앞 파일 재생" +1023="다음 파일 재생" +1029="전환\n전체화면/윈도우화면" +1100="미디어" +1101="파일 열기..." +1102="스냅샷 저장..." +1103="소스 스테레오 형식" +1170="File info" +1104="오디오 장치" +1105="셔플" +1106="최근 파일" +1107="GPU 비디오 디코딩" +1108="웹 UI" +1109="종료" +1110="파일 하나에서 재생" +1111="좌+우 각 파일에서 재생" +1130="자동감지" +1131="단일영상" +1132="크로스-아이" +1133="패럴렐 페어" +1134="상/하 (R/L)" +1135="상/하 (L/R)" +1136="인터레이스" +1137="red/cyan" +1138="green/red+blue" +1139="yellow/blue" +1140="순차 프레임" +1141="2x720p in 1080p tiled" +1142="2 스트림" +1160="재생 기록 소거" +1180="끄기" +1181="한번만 실행" +1182="매번 실행" +1185="오류 보이기" +1186="웹 UI {0} 포트에서 시작 불가!" +1200="보기" +1201="입체 출력" +1202="전체 화면" +1203="리셋" +1204="좌/우 교체" +1205="화면 비율" +1206="Smooth 필터" +1207="이미지 수정" +1208="Surface" +1210="Stereo" +1211="왼쪽 View" +1212="오른쪽 View" +1213="패럴렐 페어" +1214="크로스 아이" +1250="Source" +1251="재시작시 유지" +1260="Nearest" +1261="Linear" +1262="Blend Deinterlace" +1270="초기값 리셋" +1271="밝기" +1272="Saturation" +1273="감마" +1280="Plane" +1281="Sphere" +1282="Cylinder" +1300="Audio" +1301="없음" +1302="오디오/비디오 지연시간" +1303="파일에서 불러오기" +1320="오디오/비디오 동기화" +1321="오디오가 화면보다 먼저 들리면 +값을, 반대이면 -값을 넣으시오" +1322="오디오 지연:" +1323="초(s)" +1350="자막" +1351="없음" +1353="파일에서 불러오기" +1354="폰트 크기" +1355="Parallax" +1356="Text parser" +1360="Plain text" +1361="Lite HTML" +1400="장치 변경" +1401="About Plugin..." +1402="FPS 제어" +1420="VSync" +1421="Show Meter" +1422="CPU 부하 감소" +1500="도움말" +1501="이 프로그램에 대해..." +1502="업데이트 확인" +1503="라이선스 문서" +1504="언어 (Language)" +1505="블록 슬리핑" +1506="사용자 팁" +1507="실험적 기능" +1508="시스템에 대해" +1509="Interface Scale" +1520="지금" +1521="매일" +1522="매주" +1523="매년" +1524="하지않음" +1550="하지않음" +1551="항상" +1552="재생중" +1553="전체화면중" +1590="작게" +1591="보통" +1592="크게" +1593="HiDPI 2X 로" +2000="재생할 비디오 파일을 선택하시오" +2001="좌안 비디오 파일을 선택하시오" +2002="우안 비디오 파일을 선택하시오" +2003="File Info" +2004="File information is unavailable" +2005="File deletion" +2006="Do you really want to completely remove this file?" +2007="Active decoders:" +2010="스냅샷을 저장할 위치를 선택하시오" +2011="저장할 수 없음!" +2012="스냅샷 없음!" +3000="sView - 3D 동영상 플레이어" +3001="version" +3002="이 동영상 플레이어는 스테레오스코픽 동영상을 재생할 수 있습니다.\n © 2007-2014 Kirill Gavrilov \nOfficial site: www.sview.ru\n\n이 프로그램은 GPL3.0 하에 배포됩니다." +3003="sView 새 버전은 www.sview.ru 에서 얻으실 수 있습니다.\n 프로그램을 업데이트 하세요." +3004="System Info" +4000="닫기" +4001="Cancel" +4005="리셋" +4006="Save" +4007="Delete" +5000="[left]" +5001="[right]" +5002="File name(s)" +5003="Video dimensions" +5004="Load time" +5005="ms" +5006="Pixel ratio" +5007="Pixel format" +5008="(does not stored in metadata)" +5009="(does not match metadata)" +5010="Duration" +5300="Title" +5301="Composer" +5302="Artist" +5303="Album artist" +5304="Album" +5305="Disc" +5306="Nb. of discs" +5307="Genre" +5308="Comment" +5309="Notes" +5310="Description" +5311="Publisher" +5312="Copyright" +5313="Encoder" +5314="Engineer" +5315="Source" +5316="Creation time" +5317="Date" +5318="Year" +5319="Language" +5320="Track" +5321="Nb. of tracks" +5322="Track gain" +5323="Track peak" +5324="Album gain" +5325="Album peak" diff -Nru sview-13.10/StMoviePlayer/lang/korean/language.lng sview-14.01/StMoviePlayer/lang/korean/language.lng --- sview-13.10/StMoviePlayer/lang/korean/language.lng 1970-01-01 00:00:00.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/korean/language.lng 2014-01-30 10:45:03.000000000 +0000 @@ -0,0 +1 @@ +한국어 \ No newline at end of file diff -Nru sview-13.10/StMoviePlayer/lang/russian/StMoviePlayer.lng sview-14.01/StMoviePlayer/lang/russian/StMoviePlayer.lng --- sview-13.10/StMoviePlayer/lang/russian/StMoviePlayer.lng 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/lang/russian/StMoviePlayer.lng 2014-01-30 10:45:03.000000000 +0000 @@ -4,7 +4,7 @@ -------- 1002="Поменять ракурсы местами" 1003="Отключить реверс ракурсов" -1004="Исходный формат:\n" +1004="Исходный стереоформат:" 1015="Открыть видеофайл" 1020="Пауза/Воспроизведение" 1021="Отобразить/Скрыть\nсписок файлов" @@ -15,6 +15,7 @@ 1101="Открыть видео..." 1102="Сохранить кадр..." 1103="Исходный стереоформат" +1170="Информация о файле" 1104="Аудио устройство" 1105="Случайный порядок" 1106="Последние файлы" @@ -107,19 +108,68 @@ 1553="В полноэкранном режиме" 1354="Размер текста" 1355="Параллакс" +1356="Text parser" +1360="Plain text" +1361="Lite HTML" 1590="Мелко" 1591="Нормально" 1592="Крупно" 1593="Форсировать HiDPI 2X" 2000="Выберите видеофайл" 2001="Выберите видеофайл с ЛЕВЫМ ракурсом" -2001="Выберите видеофайл с ПРАВЫМ ракурсом" +2002="Выберите видеофайл с ПРАВЫМ ракурсом" +2003="Информация о файле" +2004="Информация о файле недоступна" +2005="Удаление файла" +2006="Вы действительно хотите\nудалить файл минуя корзину?" +2007="Активные декодеры:" 2010="Выберите путь для сохранения картинки" 2011="Ничего не открыто!" 2012="Изображение недоступно для сохранения!" 3000="sView - программа для воспроизведения видео" 3001="версия" -3002="Программа воспроизводит стереоскопическое видео.\n © 2007-2013 Гаврилов Кирилл \nОфициальный сайт: www.sview.ru\n\nПрограмма распространяется на условиях GPL3.0" +3002="Программа воспроизводит стереоскопическое видео.\n © 2007-2014 Гаврилов Кирилл \nОфициальный сайт: www.sview.ru\n\nПрограмма распространяется на условиях GPL3.0" 3003="Доступно обновление программы.\nВы можете загрузить новую версию с официального сайта www.sview.ru." +3004="Информация о системе" 4000="Закрыть" +4001="Отменить" 4005="Сбросить" +4006="Сохранить" +4007="Удалить" +5000="[левый]" +5001="[правый]" +5002="Имена файлов" +5003="Разрешение видео" +5004="Время загрузки" +5005="мс" +5006="Пропорции пикселя" +5007="Пиксельный формат" +5008="(информация отсутствует в метаданных)" +5009="(не соответствует метаданным)" +5010="Продолжительность" +5300="Заголовок" +5301="Композитор" +5302="Artist" +5303="Album artist" +5304="Альбом" +5305="Диск" +5306="Количество дисков" +5307="Жанр" +5308="Комментарий" +5309="Примечания" +5310="Описание" +5311="Publisher" +5312="Copyright" +5313="Encoder" +5314="Engineer" +5315="Источник" +5316="Дата создания" +5317="Дата" +5318="Год" +5319="Язык" +5320="Track" +5321="Nb. of tracks" +5322="Track gain" +5323="Track peak" +5324="Album gain" +5325="Album peak" Binary files /tmp/MNWssWIMVS/sview-13.10/StMoviePlayer/web/favicon.ico and /tmp/OrsWdxfD0p/sview-14.01/StMoviePlayer/web/favicon.ico differ diff -Nru sview-13.10/StMoviePlayer/web/index.htm sview-14.01/StMoviePlayer/web/index.htm --- sview-13.10/StMoviePlayer/web/index.htm 2013-10-05 08:32:22.000000000 +0000 +++ sview-14.01/StMoviePlayer/web/index.htm 2014-01-30 10:45:03.000000000 +0000 @@ -1,6 +1,7 @@ - + + sView Web UI