5835 lines
227 KiB
C++
5835 lines
227 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
********************************************************************************/
|
|
|
|
|
|
#include "Main.h"
|
|
#include "LogUploader.h"
|
|
#include <shellapi.h>
|
|
#include <ShlObj.h>
|
|
#include <uxtheme.h>
|
|
#include <vsstyle.h>
|
|
#include <MMSystem.h>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
|
|
//hello, you've come into the file I hate the most.
|
|
|
|
#define FREEZE_WND(hwnd) SendMessage(hwnd, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM) 0);
|
|
#define THAW_WND(hwnd) {SendMessage(hwnd, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM) 0); RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);}
|
|
|
|
extern WNDPROC listboxProc;
|
|
extern WNDPROC listviewProc;
|
|
|
|
void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown);
|
|
|
|
enum
|
|
{
|
|
ID_LISTBOX_REMOVE = 1,
|
|
ID_LISTBOX_MOVEUP,
|
|
ID_LISTBOX_MOVEDOWN,
|
|
ID_LISTBOX_MOVETOTOP,
|
|
ID_LISTBOX_MOVETOBOTTOM,
|
|
ID_LISTBOX_CENTER,
|
|
ID_LISTBOX_CENTERHOR,
|
|
ID_LISTBOX_CENTERVER,
|
|
ID_LISTBOX_MOVELEFT,
|
|
ID_LISTBOX_MOVETOP,
|
|
ID_LISTBOX_MOVERIGHT,
|
|
ID_LISTBOX_MOVEBOTTOM,
|
|
ID_LISTBOX_FITTOSCREEN,
|
|
ID_LISTBOX_RESETSIZE,
|
|
ID_LISTBOX_RESETCROP,
|
|
ID_LISTBOX_RENAME,
|
|
ID_LISTBOX_COPY,
|
|
ID_LISTBOX_HOTKEY,
|
|
ID_LISTBOX_CONFIG,
|
|
|
|
// Render frame related.
|
|
ID_TOGGLERENDERVIEW,
|
|
ID_TOGGLEPANEL,
|
|
ID_TOGGLEFULLSCREEN,
|
|
ID_PREVIEWSCALETOFITMODE,
|
|
ID_PREVIEW1TO1MODE,
|
|
|
|
ID_LISTBOX_ADD,
|
|
|
|
ID_LISTBOX_GLOBALSOURCE=5000,
|
|
ID_PROJECTOR=6000,
|
|
ID_LISTBOX_COPYTO=7000,
|
|
};
|
|
|
|
INT_PTR CALLBACK OBS::EnterSceneCollectionDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
SetWindowText(GetDlgItem(hwnd, IDC_NAME), strOut);
|
|
|
|
return true;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
String str;
|
|
str.SetLength((UINT)SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXTLENGTH, 0, 0));
|
|
if (!str.Length())
|
|
{
|
|
OBSMessageBox(hwnd, Str("EnterName"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXT, str.Length()+1, (LPARAM)str.Array());
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
String strSceneCollectionPath;
|
|
strSceneCollectionPath << lpAppDataPath << TEXT("\\sceneCollection\\") << str << TEXT(".xconfig");
|
|
|
|
if (OSFileExists(strSceneCollectionPath))
|
|
{
|
|
String strExists = Str("NameExists");
|
|
strExists.FindReplace(TEXT("$1"), str);
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
strOut = str;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::EnterProfileDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
SetWindowText(GetDlgItem(hwnd, IDC_NAME), strOut);
|
|
|
|
return true;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
String str;
|
|
str.SetLength((UINT)SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXTLENGTH, 0, 0));
|
|
if (!str.Length())
|
|
{
|
|
OBSMessageBox(hwnd, Str("EnterName"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXT, str.Length()+1, (LPARAM)str.Array());
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
String strProfilePath;
|
|
strProfilePath << lpAppDataPath << TEXT("\\profiles\\") << str << TEXT(".ini");
|
|
|
|
if (OSFileExists(strProfilePath))
|
|
{
|
|
String strExists = Str("NameExists");
|
|
strExists.FindReplace(TEXT("$1"), str);
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
strOut = str;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::EnterSourceNameDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
SetWindowText(GetDlgItem(hwnd, IDC_NAME), strOut);
|
|
|
|
//SetFocus(GetDlgItem(hwnd, IDC_NAME));
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
String str;
|
|
str.SetLength((UINT)SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXTLENGTH, 0, 0));
|
|
if(!str.Length())
|
|
{
|
|
OBSMessageBox(hwnd, Str("EnterName"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXT, str.Length()+1, (LPARAM)str.Array());
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
if(App->sceneElement)
|
|
{
|
|
XElement *sources = App->sceneElement->GetElement(TEXT("sources"));
|
|
if(!sources)
|
|
sources = App->sceneElement->CreateElement(TEXT("sources"));
|
|
|
|
XElement *foundSource = sources->GetElement(str);
|
|
if(foundSource != NULL && strOut != foundSource->GetName())
|
|
{
|
|
String strExists = Str("NameExists");
|
|
strExists.FindReplace(TEXT("$1"), str);
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
strOut = str;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::SceneHotkeyDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
SceneHotkeyInfo *hotkeyInfo = (SceneHotkeyInfo*)lParam;
|
|
SendMessage(GetDlgItem(hwnd, IDC_HOTKEY), HKM_SETHOTKEY, hotkeyInfo->hotkey, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_CLEAR:
|
|
if(HIWORD(wParam) == BN_CLICKED)
|
|
SendMessage(GetDlgItem(hwnd, IDC_HOTKEY), HKM_SETHOTKEY, 0, 0);
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
SceneHotkeyInfo *hotkeyInfo = (SceneHotkeyInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
DWORD hotkey = (DWORD)SendMessage(GetDlgItem(hwnd, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0);
|
|
|
|
if(hotkey == hotkeyInfo->hotkey)
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
|
|
if(hotkey)
|
|
{
|
|
XElement *scenes = API->GetSceneListElement();
|
|
UINT numScenes = scenes->NumElements();
|
|
for(UINT i=0; i<numScenes; i++)
|
|
{
|
|
XElement *sceneElement = scenes->GetElementByID(i);
|
|
if(sceneElement->GetInt(TEXT("hotkey")) == hotkey)
|
|
{
|
|
OBSMessageBox(hwnd, Str("Scene.Hotkey.AlreadyInUse"), NULL, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
hotkeyInfo->hotkey = hotkey;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::EnterSceneNameDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
SetWindowText(GetDlgItem(hwnd, IDC_NAME), strOut);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
String str;
|
|
str.SetLength((UINT)SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXTLENGTH, 0, 0));
|
|
if(!str.Length())
|
|
{
|
|
OBSMessageBox(hwnd, Str("EnterName"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXT, str.Length()+1, (LPARAM)str.Array());
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
XElement *foundScene = scenes->GetElement(str);
|
|
if(foundScene != NULL && strOut != foundScene->GetName())
|
|
{
|
|
String strExists = Str("NameExists");
|
|
strExists.FindReplace(TEXT("$1"), str);
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
strOut = str;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void OBS::GetNewSceneName(String &strScene)
|
|
{
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
if(scenes)
|
|
{
|
|
String strTestName = strScene;
|
|
|
|
UINT num = 1;
|
|
while(scenes->GetElement(strTestName) != NULL)
|
|
strTestName.Clear() << strScene << FormattedString(TEXT(" %u"), ++num);
|
|
|
|
strScene = strTestName;
|
|
}
|
|
}
|
|
|
|
void OBS::GetNewSourceName(String &strSource)
|
|
{
|
|
XElement *sceneElement = API->GetSceneElement();
|
|
if(sceneElement)
|
|
{
|
|
XElement *sources = sceneElement->GetElement(TEXT("sources"));
|
|
if(!sources)
|
|
sources = sceneElement->CreateElement(TEXT("sources"));
|
|
|
|
String strTestName = strSource;
|
|
|
|
UINT num = 1;
|
|
while(sources->GetElement(strTestName) != NULL)
|
|
strTestName.Clear() << strSource << FormattedString(TEXT(" %u"), ++num);
|
|
|
|
strSource = strTestName;
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK OBS::ListboxHook(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
UINT id = (UINT)GetWindowLongPtr(hwnd, GWL_ID);
|
|
|
|
if(message == WM_RBUTTONDOWN)
|
|
{
|
|
int numItems = 0;
|
|
if(id == ID_SCENES)
|
|
{
|
|
CallWindowProc(listboxProc, hwnd, WM_LBUTTONDOWN, wParam, lParam);
|
|
numItems = (int)SendMessage(hwnd, LB_GETCOUNT, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
LVHITTESTINFO htInfo;
|
|
int index;
|
|
|
|
// Default behaviour of left/right click is to check/uncheck, we do not want to toggle items when right clicking above checkbox.
|
|
|
|
numItems = ListView_GetItemCount(hwnd);
|
|
|
|
GetCursorPos(&htInfo.pt);
|
|
ScreenToClient(hwnd, &htInfo.pt);
|
|
|
|
index = ListView_HitTestEx(hwnd, &htInfo);
|
|
|
|
if(index != -1)
|
|
{
|
|
// Focus our control
|
|
if(GetFocus() != hwnd)
|
|
SetFocus(hwnd);
|
|
|
|
// Clear all selected items state and select/focus the item we've right-clicked if it wasn't previously selected.
|
|
if(!(ListView_GetItemState(hwnd, index, LVIS_SELECTED) & LVIS_SELECTED))
|
|
{
|
|
ListView_SetItemState(hwnd , -1 , 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
|
|
ListView_SetItemState(hwnd, index, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
|
|
|
|
ListView_SetSelectionMark(hwnd, index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ListView_SetItemState(hwnd , -1 , 0, LVIS_SELECTED | LVIS_FOCUSED)
|
|
CallWindowProc(listviewProc, hwnd, WM_RBUTTONDOWN, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
HMENU hMenu = CreatePopupMenu();
|
|
|
|
bool bSelected = true;
|
|
|
|
if(id == ID_SCENES)
|
|
{
|
|
SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)GetDlgItem(hwndMain, ID_SCENES));
|
|
|
|
for(UINT i=0; i<App->sceneClasses.Num(); i++)
|
|
{
|
|
if (App->sceneClasses[i].bDeprecated)
|
|
continue;
|
|
|
|
String strAdd = Str("Listbox.Add");
|
|
strAdd.FindReplace(TEXT("$1"), App->sceneClasses[i].strName);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_ADD+i, strAdd.Array());
|
|
}
|
|
}
|
|
else if(id == ID_SOURCES)
|
|
{
|
|
if(!App->sceneElement)
|
|
return 0;
|
|
|
|
bSelected = ListView_GetSelectedCount(hwnd) != 0;
|
|
}
|
|
|
|
App->AppendModifyListbox(hwnd, hMenu, id, numItems, bSelected);
|
|
|
|
POINT p;
|
|
GetCursorPos(&p);
|
|
|
|
int curSel = (id== ID_SOURCES)?(ListView_GetNextItem(hwnd, -1, LVNI_SELECTED)):((int)SendMessage(hwnd, LB_GETCURSEL, 0, 0));
|
|
|
|
XElement *curSceneElement = App->sceneElement;
|
|
|
|
if(id == ID_SCENES)
|
|
{
|
|
XElement *item = App->sceneElement;
|
|
if(!item && numItems)
|
|
return 0;
|
|
|
|
ClassInfo *curClassInfo = NULL;
|
|
if(numItems)
|
|
{
|
|
curClassInfo = App->GetSceneClass(item->GetString(TEXT("class")));
|
|
if(!curClassInfo)
|
|
{
|
|
curSceneElement->AddString(TEXT("class"), TEXT("Scene"));
|
|
curClassInfo = App->GetSceneClass(item->GetString(TEXT("class")));
|
|
}
|
|
|
|
if(curClassInfo && curClassInfo->configProc)
|
|
{
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_CONFIG, Str("Listbox.Config"));
|
|
}
|
|
}
|
|
|
|
bool bDelete = false;
|
|
|
|
int ret = (int)TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, hwndMain, NULL);
|
|
switch(ret)
|
|
{
|
|
default:
|
|
if(ret >= ID_LISTBOX_ADD && ret < ID_LISTBOX_COPYTO)
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
String strName = Str("Scene");
|
|
GetNewSceneName(strName);
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSceneNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
UINT classID = ret-ID_LISTBOX_ADD;
|
|
ClassInfo &ci = App->sceneClasses[classID];
|
|
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
XElement *newSceneElement = scenes->CreateElement(strName);
|
|
|
|
newSceneElement->SetString(TEXT("class"), ci.strClass);
|
|
if(ci.configProc)
|
|
{
|
|
if(!ci.configProc(newSceneElement, true))
|
|
{
|
|
scenes->RemoveElement(newSceneElement);
|
|
break;
|
|
}
|
|
}
|
|
|
|
UINT newID = (UINT)SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)strName.Array());
|
|
PostMessage(hwnd, LB_SETCURSEL, newID, 0);
|
|
PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)hwnd);
|
|
App->ReportScenesChanged();
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
}
|
|
|
|
if(ret >= ID_LISTBOX_COPYTO)
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
StringList sceneCollectionList;
|
|
App->GetSceneCollection(sceneCollectionList);
|
|
|
|
for(UINT i = 0; i < sceneCollectionList.Num(); i++)
|
|
{
|
|
if(sceneCollectionList[i] == App->GetCurrentSceneCollection())
|
|
{
|
|
sceneCollectionList.Remove(i);
|
|
}
|
|
}
|
|
|
|
UINT classID = ret - ID_LISTBOX_COPYTO;
|
|
|
|
String strScenesCopyToConfig;
|
|
strScenesCopyToConfig = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, sceneCollectionList[classID].Array());
|
|
|
|
if(!App->scenesCopyToConfig.Open(strScenesCopyToConfig))
|
|
CrashError(TEXT("Could not open '%s"), strScenesCopyToConfig.Array());
|
|
|
|
XElement *currentSceneCollection = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
|
|
XElement *selectedScene = currentSceneCollection->GetElementByID(curSel);
|
|
XElement *copyToSceneCollection = App->scenesCopyToConfig.GetElement(TEXT("scenes"));
|
|
|
|
if(!copyToSceneCollection)
|
|
copyToSceneCollection = App->scenesCopyToConfig.CreateElement(TEXT("scenes"));
|
|
|
|
if(copyToSceneCollection)
|
|
{
|
|
XElement *foundGlobalSource = copyToSceneCollection->GetElement(selectedScene->GetName());
|
|
if(foundGlobalSource != NULL && selectedScene->GetName() != foundGlobalSource->GetName())
|
|
{
|
|
App->scenesCopyToConfig.Close();
|
|
App->EnableSceneSwitching(true);
|
|
String strExists = Str("CopyTo.SceneNameExists");
|
|
strExists.FindReplace(TEXT("$1"), selectedScene->GetName());
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XElement *newSceneElement = copyToSceneCollection->CopyElement(selectedScene, selectedScene->GetName());
|
|
newSceneElement->SetString(TEXT("class"), selectedScene->GetString(TEXT("class")));
|
|
|
|
bool globalSourceRefCheck = false;
|
|
|
|
XElement *newSceneSourcesGsRefCheck = newSceneElement->GetElement(TEXT("sources"));
|
|
|
|
if(newSceneSourcesGsRefCheck)
|
|
{
|
|
UINT numSources = newSceneSourcesGsRefCheck->NumElements();
|
|
|
|
for(int i = int(numSources - 1); i >= 0; i--)
|
|
{
|
|
XElement *sourceElement = newSceneSourcesGsRefCheck->GetElementByID(i);
|
|
String sourceClassName = sourceElement->GetString(TEXT("class"));
|
|
|
|
if(sourceClassName == "GlobalSource")
|
|
{
|
|
globalSourceRefCheck = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(globalSourceRefCheck)
|
|
{
|
|
if(OBSMessageBox(hwndMain, Str("CopyTo.CopyGlobalSourcesReferences"), Str("CopyTo.CopyGlobalSourcesReferences.Title"), MB_YESNO) == IDYES)
|
|
{
|
|
XElement *newSceneSources = newSceneElement->GetElement(TEXT("sources"));
|
|
|
|
if(newSceneSources)
|
|
{
|
|
UINT numSources = newSceneSources->NumElements();
|
|
|
|
for(int i = int(numSources - 1); i >= 0; i--)
|
|
{
|
|
XElement *sourceElement = newSceneSources->GetElementByID(i);
|
|
String sourceClassName = sourceElement->GetString(TEXT("class"));
|
|
|
|
if(sourceClassName == "GlobalSource")
|
|
{
|
|
XElement *data = sourceElement->GetElement(TEXT("data"));
|
|
if(data)
|
|
{
|
|
CTSTR lpName = data->GetString(TEXT("name"));
|
|
XElement *globalSourceName = App->GetGlobalSourceElement(lpName);
|
|
if(globalSourceName)
|
|
{
|
|
XElement *importGlobalSources = App->scenesCopyToConfig.GetElement(TEXT("global sources"));
|
|
|
|
if(!importGlobalSources)
|
|
importGlobalSources = App->scenesCopyToConfig.CreateElement(TEXT("global sources"));
|
|
|
|
if(importGlobalSources)
|
|
{
|
|
XElement *foundGlobalSource = importGlobalSources->GetElement(globalSourceName->GetName());
|
|
if(foundGlobalSource != NULL && globalSourceName->GetName() != foundGlobalSource->GetName())
|
|
{
|
|
String strGsExists = Str("CopyTo.GlobalSourcesExists");
|
|
strGsExists.FindReplace(TEXT("$1"), globalSourceName->GetName());
|
|
OBSMessageBox(hwnd, strGsExists, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
XElement *newGlobalSources = importGlobalSources->CopyElement(globalSourceName, globalSourceName->GetName());
|
|
newGlobalSources->SetString(TEXT("class"), globalSourceName->GetString(TEXT("class")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XElement *newSceneSources = newSceneElement->GetElement(TEXT("sources"));
|
|
|
|
if(newSceneSources)
|
|
{
|
|
UINT numSources = newSceneSources->NumElements();
|
|
|
|
for(int i = int(numSources - 1); i >= 0; i--)
|
|
{
|
|
XElement *sourceElement = newSceneSources->GetElementByID(i);
|
|
String sourceClassName = sourceElement->GetString(TEXT("class"));
|
|
|
|
if(sourceClassName == "GlobalSource")
|
|
{
|
|
newSceneSources->RemoveElement(sourceElement);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
String strCopied = Str("CopyTo.Success.Text");
|
|
strCopied.FindReplace(TEXT("$1"), selectedScene->GetName());
|
|
OBSMessageBox(hwnd, strCopied, Str("CopyTo.Success.Title"), 0);
|
|
|
|
App->EnableSceneSwitching(true);
|
|
App->scenesCopyToConfig.Close(true);
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_REMOVE:
|
|
App->EnableSceneSwitching(false);
|
|
|
|
if(OBSMessageBox(hwndMain, Str("DeleteConfirm"), Str("DeleteConfirm.Title"), MB_YESNO) == IDYES)
|
|
{
|
|
DWORD hotkey = item->GetInt(TEXT("hotkey"));
|
|
if(hotkey)
|
|
App->RemoveSceneHotkey(hotkey);
|
|
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
if(--numItems)
|
|
{
|
|
if(curSel == numItems)
|
|
curSel--;
|
|
}
|
|
else
|
|
curSel = LB_ERR;
|
|
|
|
App->ReportScenesChanged();
|
|
bDelete = true;
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
|
|
case ID_LISTBOX_RENAME:
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
String strName = item->GetName();
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSceneNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
SendMessage(hwnd, LB_INSERTSTRING, curSel, (LPARAM)strName.Array());
|
|
SendMessage(hwnd, LB_SETCURSEL, curSel, 0);
|
|
|
|
item->SetName(strName);
|
|
App->ReportScenesChanged();
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
}
|
|
case ID_LISTBOX_COPY:
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
String strName = Str("Scene");
|
|
GetNewSceneName(strName);
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSceneNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
UINT classID = 0; // ID_LISTBOX_ADD - ID_LISTBOX_ADD
|
|
ClassInfo &ci = App->sceneClasses[classID];
|
|
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
XElement *newSceneElement = scenes->CopyElement(item, strName);
|
|
|
|
newSceneElement->SetString(TEXT("class"), ci.strClass);
|
|
newSceneElement->SetInt(TEXT("hotkey"), 0);
|
|
|
|
if(ci.configProc)
|
|
{
|
|
if(!ci.configProc(newSceneElement, true))
|
|
{
|
|
scenes->RemoveElement(newSceneElement);
|
|
break;
|
|
}
|
|
}
|
|
|
|
UINT newID = (UINT)SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)strName.Array());
|
|
PostMessage(hwnd, LB_SETCURSEL, newID, 0);
|
|
PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)hwnd);
|
|
App->ReportScenesChanged();
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
|
|
break;
|
|
}
|
|
case ID_LISTBOX_CONFIG:
|
|
App->EnableSceneSwitching(false);
|
|
|
|
if(curClassInfo && curClassInfo->configProc(item, false))
|
|
{
|
|
if(App->bRunning)
|
|
App->scene->UpdateSettings();
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
|
|
case ID_LISTBOX_HOTKEY:
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
DWORD prevHotkey = item->GetInt(TEXT("hotkey"));
|
|
|
|
SceneHotkeyInfo hotkeyInfo;
|
|
hotkeyInfo.hotkey = prevHotkey;
|
|
hotkeyInfo.scene = item;
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_SCENEHOTKEY), hwndMain, OBS::SceneHotkeyDialogProc, (LPARAM)&hotkeyInfo) == IDOK)
|
|
{
|
|
if(hotkeyInfo.hotkey)
|
|
hotkeyInfo.hotkeyID = API->CreateHotkey(hotkeyInfo.hotkey, SceneHotkey, 0);
|
|
|
|
item->SetInt(TEXT("hotkey"), hotkeyInfo.hotkey);
|
|
|
|
if(prevHotkey)
|
|
App->RemoveSceneHotkey(prevHotkey);
|
|
|
|
if(hotkeyInfo.hotkeyID)
|
|
App->sceneHotkeys << hotkeyInfo;
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
}
|
|
|
|
case ID_LISTBOX_MOVEUP:
|
|
if(curSel > 0)
|
|
{
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
SendMessage(hwnd, LB_INSERTSTRING, curSel-1, (LPARAM)item->GetName());
|
|
SendMessage(hwnd, LB_SETCURSEL, curSel-1, 0);
|
|
|
|
curSel--;
|
|
item->MoveUp();
|
|
App->ReportScenesChanged();
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVEDOWN:
|
|
if(curSel != (numItems-1))
|
|
{
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
SendMessage(hwnd, LB_INSERTSTRING, curSel+1, (LPARAM)item->GetName());
|
|
SendMessage(hwnd, LB_SETCURSEL, curSel+1, 0);
|
|
|
|
curSel++;
|
|
item->MoveDown();
|
|
App->ReportScenesChanged();
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVETOTOP:
|
|
if(curSel != 0)
|
|
{
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
SendMessage(hwnd, LB_INSERTSTRING, 0, (LPARAM)item->GetName());
|
|
SendMessage(hwnd, LB_SETCURSEL, 0, 0);
|
|
|
|
curSel = 0;
|
|
item->MoveToTop();
|
|
App->ReportScenesChanged();
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVETOBOTTOM:
|
|
if(curSel != numItems-1)
|
|
{
|
|
SendMessage(hwnd, LB_DELETESTRING, curSel, 0);
|
|
SendMessage(hwnd, LB_INSERTSTRING, numItems-1, (LPARAM)item->GetName());
|
|
SendMessage(hwnd, LB_SETCURSEL, numItems-1, 0);
|
|
|
|
curSel = numItems-1;
|
|
item->MoveToBottom();
|
|
App->ReportScenesChanged();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(curSel != LB_ERR)
|
|
{
|
|
SendMessage(hwnd, LB_SETCURSEL, curSel, 0);
|
|
SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)GetDlgItem(hwndMain, ID_SCENES));
|
|
|
|
if(bDelete)
|
|
item->GetParent()->RemoveElement(item);
|
|
}
|
|
else if(bDelete)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
OSEnterMutex(App->hSceneMutex);
|
|
delete App->scene;
|
|
App->scene = NULL;
|
|
OSLeaveMutex(App->hSceneMutex);
|
|
}
|
|
|
|
App->bChangingSources = true;
|
|
ListView_DeleteAllItems(GetDlgItem(hwndMain, ID_SOURCES));
|
|
App->bChangingSources = false;
|
|
|
|
item->GetParent()->RemoveElement(item);
|
|
App->sceneElement = NULL;
|
|
}
|
|
}
|
|
else if(id == ID_SOURCES)
|
|
{
|
|
if(!App->sceneElement && numItems)
|
|
return 0;
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
if(selectedSceneItems.Num() < 1)
|
|
nop();
|
|
|
|
int ret = (int)TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, hwndMain, NULL);
|
|
App->TrackModifyListbox(hwnd, ret);
|
|
}
|
|
|
|
DestroyMenu(hMenu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if(id == ID_SOURCES)
|
|
{
|
|
return CallWindowProc(listviewProc, hwnd, message, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
return CallWindowProc(listboxProc, hwnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
void OBS::TrackModifyListbox(HWND hwnd, int ret)
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
UINT numSelected = (ListView_GetSelectedCount(hwndSources));
|
|
XElement *selectedElement = NULL;
|
|
ClassInfo *curClassInfo = NULL;
|
|
if(numSelected == 1)
|
|
{
|
|
UINT selectedID = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
XElement *sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
selectedElement = sourcesElement->GetElementByID(selectedID);
|
|
curClassInfo = App->GetImageSourceClass(selectedElement->GetString(TEXT("class")));
|
|
}
|
|
|
|
switch(ret)
|
|
{
|
|
// General render frame stuff above here
|
|
case ID_TOGGLERENDERVIEW:
|
|
App->bRenderViewEnabled = !App->bRenderViewEnabled;
|
|
App->bForceRenderViewErase = !App->bRenderViewEnabled;
|
|
App->UpdateRenderViewMessage();
|
|
break;
|
|
case ID_TOGGLEPANEL:
|
|
if (App->bFullscreenMode)
|
|
App->bPanelVisibleFullscreen = !App->bPanelVisibleFullscreen;
|
|
else
|
|
App->bPanelVisibleWindowed = !App->bPanelVisibleWindowed;
|
|
App->bPanelVisible = App->bFullscreenMode ? App->bPanelVisibleFullscreen : App->bPanelVisibleWindowed;
|
|
App->bPanelVisibleProcessed = false;
|
|
App->ResizeWindow(true);
|
|
break;
|
|
case ID_TOGGLEFULLSCREEN:
|
|
App->SetFullscreenMode(!App->bFullscreenMode);
|
|
break;
|
|
case ID_PREVIEWSCALETOFITMODE:
|
|
App->renderFrameIn1To1Mode = false;
|
|
App->ResizeRenderFrame(true);
|
|
break;
|
|
case ID_PREVIEW1TO1MODE:
|
|
App->renderFrameIn1To1Mode = true;
|
|
App->ResizeRenderFrame(true);
|
|
break;
|
|
|
|
// Sources below here
|
|
default:
|
|
if (ret >= ID_PROJECTOR)
|
|
{
|
|
UINT monitorID = ret-ID_PROJECTOR;
|
|
if (monitorID == 0)
|
|
App->bPleaseDisableProjector = true;
|
|
else
|
|
EnableProjector(monitorID-1);
|
|
}
|
|
else if(ret >= ID_LISTBOX_ADD)
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
ClassInfo *ci;
|
|
if(ret >= ID_LISTBOX_GLOBALSOURCE)
|
|
ci = App->GetImageSourceClass(TEXT("GlobalSource"));
|
|
else
|
|
{
|
|
UINT classID = ret-ID_LISTBOX_ADD;
|
|
ci = App->imageSourceClasses+classID;
|
|
}
|
|
|
|
String strName;
|
|
if(ret >= ID_LISTBOX_GLOBALSOURCE)
|
|
{
|
|
List<CTSTR> sourceNames;
|
|
App->GetGlobalSourceNames(sourceNames);
|
|
strName = sourceNames[ret-ID_LISTBOX_GLOBALSOURCE];
|
|
}
|
|
else
|
|
strName = ci->strName;
|
|
|
|
GetNewSourceName(strName);
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSourceNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
XElement *curSceneElement = App->sceneElement;
|
|
XElement *sources = curSceneElement->GetElement(TEXT("sources"));
|
|
if(!sources)
|
|
sources = curSceneElement->CreateElement(TEXT("sources"));
|
|
XElement *newSourceElement = sources->InsertElement(0, strName);
|
|
newSourceElement->SetInt(TEXT("render"), 1);
|
|
|
|
if(ret >= ID_LISTBOX_GLOBALSOURCE)
|
|
{
|
|
newSourceElement->SetString(TEXT("class"), TEXT("GlobalSource"));
|
|
|
|
List<CTSTR> sourceNames;
|
|
App->GetGlobalSourceNames(sourceNames);
|
|
|
|
CTSTR lpName = sourceNames[ret-ID_LISTBOX_GLOBALSOURCE];
|
|
|
|
XElement *data = newSourceElement->CreateElement(TEXT("data"));
|
|
data->SetString(TEXT("name"), lpName);
|
|
|
|
XElement *globalElement = App->GetGlobalSourceElement(lpName);
|
|
if(globalElement)
|
|
{
|
|
newSourceElement->SetInt(TEXT("cx"), globalElement->GetInt(TEXT("cx"), 100));
|
|
newSourceElement->SetInt(TEXT("cy"), globalElement->GetInt(TEXT("cy"), 100));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newSourceElement->SetString(TEXT("class"), ci->strClass);
|
|
if(ci->configProc)
|
|
{
|
|
if(!ci->configProc(newSourceElement, true))
|
|
{
|
|
sources->RemoveElement(newSourceElement);
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(App->sceneElement == curSceneElement)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
App->EnterSceneMutex();
|
|
App->scene->InsertImageSource(0, newSourceElement);
|
|
App->LeaveSceneMutex();
|
|
}
|
|
|
|
UINT numSources = sources->NumElements();
|
|
|
|
// clear selection/focus for all items before adding the new item
|
|
ListView_SetItemState(hwndSources , -1 , 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
ListView_SetItemCount(hwndSources, numSources);
|
|
|
|
App->bChangingSources = true;
|
|
App->InsertSourceItem(0, (LPWSTR)strName.Array(), true);
|
|
App->bChangingSources = false;
|
|
|
|
SetFocus(hwndSources);
|
|
|
|
// make sure the added item is visible/selected/focused and selection mark moved to it.
|
|
ListView_EnsureVisible(hwndSources, 0, false);
|
|
ListView_SetItemState(hwndSources, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
|
|
ListView_SetSelectionMark(hwndSources, 0);
|
|
|
|
App->ReportSourcesAddedOrRemoved();
|
|
}
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_REMOVE:
|
|
App->DeleteItems();
|
|
break;
|
|
|
|
case ID_LISTBOX_RENAME:
|
|
{
|
|
if (!selectedElement)
|
|
break;
|
|
|
|
App->EnableSceneSwitching(false);
|
|
|
|
String strName = selectedElement->GetName();
|
|
TSTR oldStrName = sdup(strName.Array());
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSourceNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
int curSel = (int)SendMessage(hwndSources, LB_GETCURSEL, 0, 0);
|
|
ListView_SetItemText(hwndSources, curSel, 0, strName.Array());
|
|
selectedElement->SetName(strName);
|
|
|
|
App->ReportSourceChanged(oldStrName, selectedElement);
|
|
Free((void*)oldStrName);
|
|
|
|
ListView_SetColumnWidth(hwndSources, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
ListView_SetColumnWidth(hwndSources, 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
break;
|
|
}
|
|
|
|
case ID_LISTBOX_CONFIG:
|
|
{
|
|
App->EnableSceneSwitching(false);
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
ImageSource *source = NULL;
|
|
Vect2 multiple;
|
|
|
|
if(App->bRunning && selectedSceneItems.Num())
|
|
{
|
|
source = selectedSceneItems[0]->GetSource();
|
|
if(source)
|
|
{
|
|
Vect2 curSize = Vect2(selectedElement->GetFloat(TEXT("cx"), 32.0f), selectedElement->GetFloat(TEXT("cy"), 32.0f));
|
|
Vect2 baseSize = source->GetSize();
|
|
|
|
multiple = curSize/baseSize;
|
|
}
|
|
}
|
|
|
|
if(curClassInfo && curClassInfo->configProc && curClassInfo->configProc(selectedElement, false))
|
|
{
|
|
|
|
if(App->bRunning && selectedSceneItems.Num())
|
|
{
|
|
App->EnterSceneMutex();
|
|
|
|
if(source)
|
|
{
|
|
Vect2 newSize = Vect2(selectedElement->GetFloat(TEXT("cx"), 32.0f), selectedElement->GetFloat(TEXT("cy"), 32.0f));
|
|
newSize *= multiple;
|
|
|
|
selectedElement->SetFloat(TEXT("cx"), newSize.x);
|
|
selectedElement->SetFloat(TEXT("cy"), newSize.y);
|
|
|
|
selectedSceneItems[0]->GetSource()->UpdateSettings();
|
|
}
|
|
|
|
selectedSceneItems[0]->Update();
|
|
|
|
App->LeaveSceneMutex();
|
|
}
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
}
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVEUP:
|
|
App->MoveSourcesUp();
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVEDOWN:
|
|
App->MoveSourcesDown();
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVETOTOP:
|
|
App->MoveSourcesToTop();
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVETOBOTTOM:
|
|
App->MoveSourcesToBottom();
|
|
break;
|
|
|
|
case ID_LISTBOX_CENTER:
|
|
App->CenterItems(true, true);
|
|
break;
|
|
|
|
case ID_LISTBOX_CENTERHOR:
|
|
App->CenterItems(true, false);
|
|
break;
|
|
|
|
case ID_LISTBOX_CENTERVER:
|
|
App->CenterItems(false, true);
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVELEFT:
|
|
App->MoveItemsToEdge(-1, 0);
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVETOP:
|
|
App->MoveItemsToEdge(0, -1);
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVERIGHT:
|
|
App->MoveItemsToEdge(1, 0);
|
|
break;
|
|
|
|
case ID_LISTBOX_MOVEBOTTOM:
|
|
App->MoveItemsToEdge(0, 1);
|
|
break;
|
|
|
|
case ID_LISTBOX_FITTOSCREEN:
|
|
App->FitItemsToScreen();
|
|
break;
|
|
|
|
case ID_LISTBOX_RESETSIZE:
|
|
App->ResetItemSizes();
|
|
break;
|
|
|
|
case ID_LISTBOX_RESETCROP:
|
|
App->ResetItemCrops();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OBS::AppendModifyListbox(HWND hwnd, HMENU hMenu, int id, int numItems, bool bSelected)
|
|
{
|
|
if(GetMenuItemCount(hMenu) > 0 && id == ID_SOURCES)
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
|
|
if(id == ID_SOURCES)
|
|
{
|
|
HMENU hmenuAdd = CreatePopupMenu();
|
|
|
|
for(UINT i=0; i<App->imageSourceClasses.Num(); i++)
|
|
{
|
|
if (App->imageSourceClasses[i].bDeprecated)
|
|
continue;
|
|
|
|
String strAdd = App->imageSourceClasses[i].strName;
|
|
|
|
if(App->imageSourceClasses[i].strClass == TEXT("GlobalSource"))
|
|
{
|
|
List<CTSTR> sourceNames;
|
|
App->GetGlobalSourceNames(sourceNames);
|
|
|
|
if(!sourceNames.Num())
|
|
continue;
|
|
|
|
HMENU hmenuGlobals = CreatePopupMenu();
|
|
|
|
for(UINT j=0; j<sourceNames.Num(); j++)
|
|
AppendMenu(hmenuGlobals, MF_STRING, ID_LISTBOX_GLOBALSOURCE+j, sourceNames[j]);
|
|
|
|
AppendMenu(hmenuAdd, MF_STRING|MF_POPUP, (UINT_PTR)hmenuGlobals, strAdd.Array());
|
|
}
|
|
else
|
|
AppendMenu(hmenuAdd, MF_STRING, ID_LISTBOX_ADD+i, strAdd.Array());
|
|
}
|
|
|
|
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT_PTR)hmenuAdd, Str("Add"));
|
|
}
|
|
|
|
if(numItems && bSelected)
|
|
{
|
|
String strRemove = Str("Remove");
|
|
String strRename = Str("Rename");
|
|
String strCopy = Str("Copy");
|
|
String strCopyTo = Str("CopyTo");
|
|
String strMoveUp = Str("MoveUp");
|
|
String strMoveDown = Str("MoveDown");
|
|
String strMoveTop = Str("MoveToTop");
|
|
String strMoveToBottom = Str("MoveToBottom");
|
|
String strPositionSize = Str("Listbox.Positioning");
|
|
String strCenter = Str("Listbox.Center");
|
|
String strCenterHor = Str("Listbox.CenterHorizontally");
|
|
String strCenterVer = Str("Listbox.CenterVertically");
|
|
String strMoveLeftOfCanvas = Str("Listbox.MoveLeft");
|
|
String strMoveTopOfCanvas = Str("Listbox.MoveTop");
|
|
String strMoveRightOfCanvas = Str("Listbox.MoveRight");
|
|
String strMoveBottomOfCanvas = Str("Listbox.MoveBottom");
|
|
String strFitToScreen = Str("Listbox.FitToScreen");
|
|
String strResize = Str("Listbox.ResetSize");
|
|
String strResetCrop = Str("Listbox.ResetCrop");
|
|
String strConfig = Str("Listbox.Config");
|
|
|
|
if(id == ID_SOURCES)
|
|
{
|
|
strRemove << TEXT("\tDel");
|
|
strMoveUp << TEXT("\tCtrl-Up");
|
|
strMoveDown << TEXT("\tCtrl-Down");
|
|
strMoveTop << TEXT("\tCtrl-Home");
|
|
strMoveToBottom << TEXT("\tCtrl-End");
|
|
strCenter << TEXT("\tCtrl-C");
|
|
strCenterVer << TEXT("\tCtrl-Shift-C");
|
|
strCenterHor << TEXT("\tCtrl-Alt-C");
|
|
strFitToScreen << TEXT("\tCtrl-F");
|
|
strResize << TEXT("\tCtrl-R");
|
|
strMoveLeftOfCanvas << TEXT("\tCtrl-Alt-Left");
|
|
strMoveTopOfCanvas << TEXT("\tCtrl-Alt-Up");
|
|
strMoveRightOfCanvas << TEXT("\tCtrl-Alt-Right");
|
|
strMoveBottomOfCanvas << TEXT("\tCtrl-Alt-Down");
|
|
strResetCrop << TEXT("\tCtrl-Alt-R");
|
|
}
|
|
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
|
|
HMENU hMenuOrder = CreatePopupMenu();
|
|
|
|
AppendMenu(hMenuOrder, MF_STRING, ID_LISTBOX_MOVEUP, strMoveUp);
|
|
AppendMenu(hMenuOrder, MF_STRING, ID_LISTBOX_MOVEDOWN, strMoveDown);
|
|
AppendMenu(hMenuOrder, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenuOrder, MF_STRING, ID_LISTBOX_MOVETOTOP, strMoveTop);
|
|
AppendMenu(hMenuOrder, MF_STRING, ID_LISTBOX_MOVETOBOTTOM, strMoveToBottom);
|
|
|
|
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT_PTR)hMenuOrder, Str("Order"));
|
|
|
|
|
|
if (id == ID_SOURCES)
|
|
{
|
|
HMENU hmenuPositioning = CreatePopupMenu();
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_FITTOSCREEN, strFitToScreen);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_RESETSIZE, strResize);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_RESETCROP, strResetCrop);
|
|
AppendMenu(hmenuPositioning, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_CENTER, strCenter);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_CENTERHOR, strCenterHor);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_CENTERVER, strCenterVer);
|
|
AppendMenu(hmenuPositioning, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_MOVELEFT, strMoveLeftOfCanvas);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_MOVETOP, strMoveTopOfCanvas);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_MOVERIGHT, strMoveRightOfCanvas);
|
|
AppendMenu(hmenuPositioning, MF_STRING, ID_LISTBOX_MOVEBOTTOM, strMoveBottomOfCanvas);
|
|
|
|
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT_PTR)hmenuPositioning, strPositionSize.Array());
|
|
}
|
|
|
|
/////AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_REMOVE, strRemove);
|
|
|
|
|
|
UINT numSelected = (id==ID_SOURCES)?(ListView_GetSelectedCount(hwnd)):((UINT)SendMessage(hwnd, LB_GETSELCOUNT, 0, 0));
|
|
if(id == ID_SCENES || numSelected == 1)
|
|
{
|
|
/////AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_RENAME, strRename);
|
|
}
|
|
|
|
if(id == ID_SCENES && numSelected)
|
|
{
|
|
HMENU hMenuCopyTo = CreatePopupMenu();
|
|
StringList sceneCollectionList;
|
|
GetSceneCollection(sceneCollectionList);
|
|
|
|
for(UINT k = 0; k < sceneCollectionList.Num(); k++)
|
|
{
|
|
if(sceneCollectionList[k] == App->GetCurrentSceneCollection())
|
|
{
|
|
sceneCollectionList.Remove(k);
|
|
}
|
|
}
|
|
|
|
for(UINT i = 0; i < sceneCollectionList.Num(); i++)
|
|
{
|
|
AppendMenu(hMenuCopyTo, MF_STRING, ID_LISTBOX_COPYTO + i, sceneCollectionList[i]);
|
|
}
|
|
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_COPY, strCopy);
|
|
AppendMenu(hMenu, MF_STRING|MF_POPUP,(UINT_PTR)hMenuCopyTo, strCopyTo);
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_HOTKEY, Str("Listbox.SetHotkey"));
|
|
}
|
|
|
|
if(id == ID_SOURCES)
|
|
{
|
|
UINT numSelected = (ListView_GetSelectedCount(hwnd));
|
|
XElement *selectedElement = NULL;
|
|
ClassInfo *curClassInfo = NULL;
|
|
if(numSelected == 1)
|
|
{
|
|
UINT selectedID = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
|
|
|
|
XElement *sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
selectedElement = sourcesElement->GetElementByID(selectedID);
|
|
|
|
curClassInfo = App->GetImageSourceClass(selectedElement->GetString(TEXT("class")));
|
|
if(curClassInfo && curClassInfo->configProc)
|
|
{
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_CONFIG, Str("Listbox.Config"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::DeleteItems()
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
List<UINT> selectedIDs;
|
|
|
|
//get selected items
|
|
int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
while (iPos != -1)
|
|
{
|
|
selectedIDs.Add((UINT) iPos);
|
|
iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED);
|
|
}
|
|
|
|
XElement *sourcesElement = NULL;
|
|
List<XElement*> selectedElements;
|
|
if(numItems)
|
|
{
|
|
sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
selectedElements << sourcesElement->GetElementByID(selectedIDs[i]);
|
|
}
|
|
|
|
if(selectedIDs.Num())
|
|
{
|
|
if(OBSMessageBox(hwndMain, Str("DeleteConfirm"), Str("DeleteConfirm.Title"), MB_YESNO) == IDYES)
|
|
{
|
|
if(selectedSceneItems.Num())
|
|
{
|
|
App->EnterSceneMutex();
|
|
|
|
for(UINT i=0; i<selectedSceneItems.Num(); i++)
|
|
{
|
|
bool globalSourcesRemain = false;
|
|
|
|
SceneItem *item = selectedSceneItems[i];
|
|
XElement *source = selectedSceneItems[i]->GetElement();
|
|
String className = source->GetString(TEXT("class"));
|
|
|
|
if(className == "GlobalSource") {
|
|
String globalSourceName = source->GetElement(TEXT("data"))->GetString(TEXT("name"));
|
|
if (App->GetGlobalSource(globalSourceName) != NULL)
|
|
App->GetGlobalSource(globalSourceName)->GlobalSourceLeaveScene();
|
|
}
|
|
|
|
App->scene->RemoveImageSource(item);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(UINT i=0; i<selectedElements.Num(); i++)
|
|
sourcesElement->RemoveElement(selectedElements[i]);
|
|
}
|
|
|
|
while(selectedIDs.Num())
|
|
{
|
|
UINT id = selectedIDs[0];
|
|
selectedIDs.Remove(0);
|
|
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
{
|
|
if(selectedIDs[i] > id)
|
|
selectedIDs[i]--;
|
|
}
|
|
App->bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, id);
|
|
ListView_SetColumnWidth(hwndSources, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
ListView_SetColumnWidth(hwndSources, 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
App->bChangingSources = false;
|
|
}
|
|
|
|
if(selectedSceneItems.Num())
|
|
App->LeaveSceneMutex();
|
|
ReportSourcesAddedOrRemoved();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::SetSourceOrder(StringList &sourceNames)
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
|
|
if(numItems == 0)
|
|
return;
|
|
|
|
XElement* sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
|
|
FREEZE_WND(hwndSources);
|
|
|
|
for(UINT i=0; i<sourceNames.Num(); i++)
|
|
{
|
|
|
|
/* find id of source by name */
|
|
int id = -1;
|
|
for(UINT j = 0; j < sourcesElement->NumElements(); j++)
|
|
{
|
|
XElement* source = sourcesElement->GetElementByID(j);
|
|
if(scmp(source->GetName(), sourceNames[i].Array()) == 0)
|
|
{
|
|
id = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(id >= 0)
|
|
{
|
|
if(App->scene)
|
|
{
|
|
App->scene->GetSceneItem(id)->MoveToBottom();
|
|
}
|
|
else
|
|
{
|
|
sourcesElement->GetElementByID(id)->MoveToBottom();
|
|
}
|
|
|
|
String strName = GetLVText(hwndSources, id);
|
|
bool checkState = ListView_GetCheckState(hwndSources, id) > 0;
|
|
|
|
bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, id);
|
|
InsertSourceItem(numItems-1, (LPWSTR)strName.Array(), checkState);
|
|
bChangingSources = false;
|
|
}
|
|
}
|
|
|
|
THAW_WND(hwndSources);
|
|
|
|
ReportSourceOrderChanged();
|
|
}
|
|
|
|
|
|
void OBS::MoveSourcesUp()
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
UINT focusedItem = -1, selectionMark;
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
List<UINT> selectedIDs;
|
|
|
|
selectionMark = ListView_GetSelectionMark(hwndSources);
|
|
|
|
//get selected items
|
|
int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
while (iPos != -1)
|
|
{
|
|
selectedIDs.Add((UINT) iPos);
|
|
if(ListView_GetItemState(hwndSources, iPos, LVIS_FOCUSED) & LVIS_FOCUSED)
|
|
focusedItem = iPos;
|
|
iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED);
|
|
}
|
|
|
|
if (!selectedSceneItems.Num() && !selectedIDs.Num())
|
|
return;
|
|
|
|
XElement *sourcesElement = NULL;
|
|
List<XElement*> selectedElements;
|
|
if(numItems)
|
|
{
|
|
sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
selectedElements << sourcesElement->GetElementByID(selectedIDs[i]);
|
|
}
|
|
|
|
UINT stateFlags;
|
|
FREEZE_WND(hwndSources);
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
{
|
|
if( (i == 0 && selectedIDs[i] > 0) ||
|
|
(i != 0 && selectedIDs[i-1] != selectedIDs[i]-1) )
|
|
{
|
|
if(App->scene)
|
|
selectedSceneItems[i]->MoveUp();
|
|
else
|
|
selectedElements[i]->MoveUp();
|
|
|
|
String strName = GetLVText(hwndSources, selectedIDs[i]);
|
|
bool checkState = ListView_GetCheckState(hwndSources, selectedIDs[i]) > 0;
|
|
|
|
bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, selectedIDs[i]);
|
|
InsertSourceItem(--selectedIDs[i], (LPWSTR)strName.Array(), checkState);
|
|
|
|
if(focusedItem == selectedIDs[i]+1)
|
|
stateFlags = LVIS_SELECTED | LVIS_FOCUSED;
|
|
else
|
|
stateFlags = LVIS_SELECTED;
|
|
if(selectionMark == selectedIDs[i]+1)
|
|
ListView_SetSelectionMark(hwndSources, selectedIDs[i]);
|
|
|
|
ListView_SetItemState(hwndSources, selectedIDs[i], stateFlags, stateFlags);
|
|
bChangingSources = false;
|
|
|
|
}
|
|
}
|
|
THAW_WND(hwndSources);
|
|
|
|
ReportSourceOrderChanged();
|
|
}
|
|
|
|
void OBS::MoveSourcesDown()
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
UINT numSelected = ListView_GetSelectedCount(hwndSources);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
int focusedItem = -1, selectionMark;
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
List<UINT> selectedIDs;
|
|
|
|
selectionMark = ListView_GetSelectionMark(hwndSources);
|
|
|
|
//get selected items
|
|
int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
while (iPos != -1)
|
|
{
|
|
selectedIDs.Add((UINT) iPos);
|
|
if(ListView_GetItemState(hwndSources, iPos, LVIS_FOCUSED) & LVIS_FOCUSED)
|
|
focusedItem = iPos;
|
|
iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED);
|
|
}
|
|
|
|
if (!selectedSceneItems.Num() && !selectedIDs.Num())
|
|
return;
|
|
|
|
XElement *sourcesElement = NULL;
|
|
List<XElement*> selectedElements;
|
|
if(numItems)
|
|
{
|
|
sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
selectedElements << sourcesElement->GetElementByID(selectedIDs[i]);
|
|
}
|
|
|
|
UINT lastItem = (UINT)ListView_GetItemCount(hwndSources)-1;
|
|
UINT lastSelectedID = numSelected-1;
|
|
UINT stateFlags;
|
|
|
|
FREEZE_WND(hwndSources);
|
|
for(int i=(int)lastSelectedID; i>=0; i--)
|
|
{
|
|
if( (i == lastSelectedID && selectedIDs[i] < lastItem) ||
|
|
(i != lastSelectedID && selectedIDs[i+1] != selectedIDs[i]+1) )
|
|
{
|
|
if(App->scene)
|
|
selectedSceneItems[i]->MoveDown();
|
|
else
|
|
selectedElements[i]->MoveDown();
|
|
|
|
String strName = GetLVText(hwndSources, selectedIDs[i]);
|
|
bool checkState = ListView_GetCheckState(hwndSources, selectedIDs[i]) > 0;
|
|
|
|
bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, selectedIDs[i]);
|
|
InsertSourceItem(++selectedIDs[i], (LPWSTR)strName.Array(), checkState);
|
|
|
|
if(focusedItem == selectedIDs[i]-1)
|
|
stateFlags = LVIS_SELECTED | LVIS_FOCUSED;
|
|
else
|
|
stateFlags = LVIS_SELECTED;
|
|
if(selectionMark == selectedIDs[i]-1)
|
|
ListView_SetSelectionMark(hwndSources, selectedIDs[i]);
|
|
|
|
ListView_SetItemState(hwndSources, selectedIDs[i], stateFlags, stateFlags);
|
|
bChangingSources = false;
|
|
}
|
|
}
|
|
THAW_WND(hwndSources);
|
|
|
|
ReportSourceOrderChanged();
|
|
}
|
|
|
|
void OBS::MoveSourcesToTop()
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
UINT focusedItem = -1, selectionMark;
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
List<UINT> selectedIDs;
|
|
|
|
selectionMark = ListView_GetSelectionMark(hwndSources);
|
|
|
|
//get selected items
|
|
int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
while (iPos != -1)
|
|
{
|
|
selectedIDs.Add((UINT) iPos);
|
|
if(ListView_GetItemState(hwndSources, iPos, LVIS_FOCUSED) & LVIS_FOCUSED)
|
|
focusedItem = iPos;
|
|
iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED);
|
|
}
|
|
|
|
if (!selectedSceneItems.Num() && !selectedIDs.Num())
|
|
return;
|
|
|
|
XElement *sourcesElement = NULL;
|
|
List<XElement*> selectedElements;
|
|
if(numItems)
|
|
{
|
|
sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
selectedElements << sourcesElement->GetElementByID(selectedIDs[i]);
|
|
}
|
|
|
|
if(App->scene)
|
|
{
|
|
for(int i=(int)selectedSceneItems.Num()-1; i>=0; i--)
|
|
selectedSceneItems[i]->MoveToTop();
|
|
}
|
|
else
|
|
{
|
|
for(int i=(int)selectedElements.Num()-1; i>=0; i--)
|
|
selectedElements[i]->MoveToTop();
|
|
}
|
|
|
|
UINT stateFlags;
|
|
|
|
FREEZE_WND(hwndSources);
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
{
|
|
if(selectedIDs[i] != i)
|
|
{
|
|
String strName = GetLVText(hwndSources, selectedIDs[i]);
|
|
bool checkState = ListView_GetCheckState(hwndSources, selectedIDs[i]) > 0;
|
|
|
|
bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, selectedIDs[i]);
|
|
InsertSourceItem(i, (LPWSTR)strName.Array(), checkState);
|
|
|
|
if(focusedItem == selectedIDs[i])
|
|
stateFlags = LVIS_SELECTED | LVIS_FOCUSED;
|
|
else
|
|
stateFlags = LVIS_SELECTED;
|
|
if(selectionMark == selectedIDs[i])
|
|
ListView_SetSelectionMark(hwndSources, i);
|
|
|
|
ListView_SetItemState(hwndSources, i, stateFlags, stateFlags);
|
|
bChangingSources = false;
|
|
}
|
|
}
|
|
THAW_WND(hwndSources);
|
|
|
|
ReportSourceOrderChanged();
|
|
}
|
|
|
|
void OBS::MoveSourcesToBottom()
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
UINT focusedItem = -1, selectionMark;
|
|
|
|
List<SceneItem*> selectedSceneItems;
|
|
if(App->scene)
|
|
App->scene->GetSelectedItems(selectedSceneItems);
|
|
|
|
List<UINT> selectedIDs;
|
|
|
|
selectionMark = ListView_GetSelectionMark(hwndSources);
|
|
|
|
//get selected items
|
|
int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED);
|
|
while (iPos != -1)
|
|
{
|
|
selectedIDs.Add((UINT) iPos);
|
|
if(ListView_GetItemState(hwndSources, iPos, LVIS_FOCUSED) & LVIS_FOCUSED)
|
|
focusedItem = iPos;
|
|
iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED);
|
|
}
|
|
|
|
if (!selectedSceneItems.Num() && !selectedIDs.Num())
|
|
return;
|
|
|
|
XElement *sourcesElement = NULL;
|
|
List<XElement*> selectedElements;
|
|
if(numItems)
|
|
{
|
|
sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
for(UINT i=0; i<selectedIDs.Num(); i++)
|
|
selectedElements << sourcesElement->GetElementByID(selectedIDs[i]);
|
|
}
|
|
|
|
if(App->scene)
|
|
{
|
|
for(UINT i=0; i<selectedSceneItems.Num(); i++)
|
|
selectedSceneItems[i]->MoveToBottom();
|
|
}
|
|
else
|
|
{
|
|
for(UINT i=0; i<selectedElements.Num(); i++)
|
|
selectedElements[i]->MoveToBottom();
|
|
}
|
|
|
|
UINT curID = ListView_GetItemCount(hwndSources)-1;
|
|
UINT stateFlags;
|
|
|
|
FREEZE_WND(hwndSources);
|
|
for(int i=int(selectedIDs.Num()-1); i>=0; i--)
|
|
{
|
|
if(selectedIDs[i] != curID)
|
|
{
|
|
String strName = GetLVText(hwndSources, selectedIDs[i]);
|
|
bool checkState = ListView_GetCheckState(hwndSources, selectedIDs[i]) > 0;
|
|
|
|
bChangingSources = true;
|
|
ListView_DeleteItem(hwndSources, selectedIDs[i]);
|
|
InsertSourceItem(curID, (LPWSTR)strName.Array(), checkState);
|
|
|
|
if(focusedItem == selectedIDs[i])
|
|
stateFlags = LVIS_SELECTED | LVIS_FOCUSED;
|
|
else
|
|
stateFlags = LVIS_SELECTED;
|
|
if(selectionMark == selectedIDs[i])
|
|
ListView_SetSelectionMark(hwndSources, curID);
|
|
|
|
ListView_SetItemState(hwndSources, curID, stateFlags, stateFlags);
|
|
bChangingSources = false;
|
|
}
|
|
|
|
curID--;
|
|
}
|
|
THAW_WND(hwndSources);
|
|
|
|
ReportSourceOrderChanged();
|
|
}
|
|
|
|
void OBS::CenterItems(bool horizontal, bool vertical)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
Vect2 baseSize = App->GetBaseSize();
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
|
|
if (horizontal)
|
|
item->pos.x = (baseSize.x*0.5f)-((item->size.x + item->GetCrop().x - item->GetCrop().w)*0.5f);
|
|
if (vertical)
|
|
item->pos.y = (baseSize.y*0.5f)-((item->size.y + item->GetCrop().y - item->GetCrop().z)*0.5f);
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
if (horizontal)
|
|
itemElement->SetInt(TEXT("x"), int(item->pos.x));
|
|
if (vertical)
|
|
itemElement->SetInt(TEXT("y"), int(item->pos.y));
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::MoveItemsToEdge(int horizontal, int vertical)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
Vect2 baseSize = App->GetBaseSize();
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
|
|
if (horizontal == 1)
|
|
item->pos.x = baseSize.x - item->size.x + item->GetCrop().w;
|
|
if (horizontal == -1)
|
|
item->pos.x = -item->GetCrop().x;
|
|
|
|
if (vertical == 1)
|
|
item->pos.y = baseSize.y - item->size.y + item->GetCrop().z;
|
|
if (vertical == -1)
|
|
item->pos.y = -item->GetCrop().y;
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
if (horizontal)
|
|
itemElement->SetInt(TEXT("x"), int(item->pos.x));
|
|
if (vertical)
|
|
itemElement->SetInt(TEXT("y"), int(item->pos.y));
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::MoveItemsByPixels(int dx, int dy)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
Vect2 baseSize = App->GetBaseSize();
|
|
Vect2 renderSize = App->GetRenderFrameSize();
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
item->pos.x += dx;
|
|
item->pos.y += dy;
|
|
if(App->bMouseMoved)
|
|
{
|
|
App->startMousePos.x -= dx * renderSize.x / baseSize.x;
|
|
App->startMousePos.y -= dy * renderSize.y / baseSize.y;
|
|
}
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
itemElement->SetInt(TEXT("x"), int(item->pos.x));
|
|
itemElement->SetInt(TEXT("y"), int(item->pos.y));
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::FitItemsToScreen()
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
Vect2 baseSize = App->GetBaseSize();
|
|
double baseAspect = double(baseSize.x)/double(baseSize.y);
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
item->pos = (baseSize*0.5f)-(item->size*0.5f);
|
|
|
|
if(item->source)
|
|
{
|
|
Vect2 itemSize = item->source->GetSize();
|
|
itemSize.x -= (item->crop.x + item->crop.w);
|
|
itemSize.y -= (item->crop.y + item->crop.z);
|
|
|
|
Vect2 size = baseSize;
|
|
double sourceAspect = double(itemSize.x)/double(itemSize.y);
|
|
if(!CloseDouble(baseAspect, sourceAspect))
|
|
{
|
|
if(baseAspect < sourceAspect)
|
|
size.y = float(double(size.x) / sourceAspect);
|
|
else
|
|
size.x = float(double(size.y) * sourceAspect);
|
|
|
|
size.x = (float)round(size.x);
|
|
size.y = (float)round(size.y);
|
|
}
|
|
|
|
Vect2 scale = itemSize / size;
|
|
size.x += (item->crop.x + item->crop.w) / scale.x;
|
|
size.y += (item->crop.y + item->crop.z) / scale.y;
|
|
item->size = size;
|
|
|
|
Vect2 pos;
|
|
pos.x = (baseSize.x*0.5f)-((item->size.x + item->GetCrop().x - item->GetCrop().w)*0.5f);
|
|
pos.y = (baseSize.y*0.5f)-((item->size.y + item->GetCrop().y - item->GetCrop().z)*0.5f);
|
|
pos.x = (float)round(pos.x);
|
|
pos.y = (float)round(pos.y);
|
|
item->pos = pos;
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
itemElement->SetInt(TEXT("x"), int(pos.x));
|
|
itemElement->SetInt(TEXT("y"), int(pos.y));
|
|
itemElement->SetInt(TEXT("cx"), int(size.x));
|
|
itemElement->SetInt(TEXT("cy"), int(size.y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::ResetItemSizes()
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
if(item->source)
|
|
{
|
|
item->size = item->source->GetSize();
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
itemElement->SetInt(TEXT("cx"), int(item->size.x));
|
|
itemElement->SetInt(TEXT("cy"), int(item->size.y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OBS::ResetItemCrops()
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
List<SceneItem*> selectedItems;
|
|
App->scene->GetSelectedItems(selectedItems);
|
|
|
|
for(UINT i=0; i<selectedItems.Num(); i++)
|
|
{
|
|
SceneItem *item = selectedItems[i];
|
|
if(item->source)
|
|
{
|
|
item->crop = Vect4(0, 0, 0, 0);
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
itemElement->SetFloat(TEXT("crop.left"), item->crop.x);
|
|
itemElement->SetFloat(TEXT("crop.top"), item->crop.y);
|
|
itemElement->SetFloat(TEXT("crop.right"), item->crop.w);
|
|
itemElement->SetFloat(TEXT("crop.bottom"), item->crop.z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
INT_PTR CALLBACK OBS::EnterGlobalSourceNameDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
String &strOut = *(String*)lParam;
|
|
SetWindowText(GetDlgItem(hwnd, IDC_NAME), strOut);
|
|
}
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
String str;
|
|
str.SetLength((UINT)SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXTLENGTH, 0, 0));
|
|
if(!str.Length())
|
|
{
|
|
OBSMessageBox(hwnd, Str("EnterName"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_NAME), WM_GETTEXT, str.Length()+1, (LPARAM)str.Array());
|
|
|
|
String &strOut = *(String*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(globals)
|
|
{
|
|
XElement *foundSource = globals->GetElement(str);
|
|
if(foundSource != NULL && strOut != foundSource->GetName())
|
|
{
|
|
String strExists = Str("NameExists");
|
|
strExists.FindReplace(TEXT("$1"), str);
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
strOut = str;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::GlobalSourcesProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LocalizeWindow(hwnd);
|
|
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(globals)
|
|
{
|
|
UINT numGlobals = globals->NumElements();
|
|
|
|
for(UINT i=0; i<numGlobals; i++)
|
|
{
|
|
XElement *globalSource = globals->GetElementByID(i);
|
|
SendMessage(hwndSources, LB_ADDSTRING, 0, (LPARAM)globalSource->GetName());
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_ADD:
|
|
{
|
|
HMENU hMenu = CreatePopupMenu();
|
|
|
|
for(UINT i=0; i<App->imageSourceClasses.Num(); i++)
|
|
{
|
|
if(App->imageSourceClasses[i].strClass != TEXT("GlobalSource"))
|
|
{
|
|
if (!App->imageSourceClasses[i].bDeprecated)
|
|
{
|
|
String strAdd = Str("Listbox.Add");
|
|
strAdd.FindReplace(TEXT("$1"), App->imageSourceClasses[i].strName);
|
|
AppendMenu(hMenu, MF_STRING, i+1, strAdd.Array());
|
|
}
|
|
}
|
|
}
|
|
|
|
POINT p;
|
|
GetCursorPos(&p);
|
|
|
|
int classID = (int)TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, hwndMain, NULL);
|
|
if(!classID)
|
|
break;
|
|
|
|
String strName;
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwnd, OBS::EnterGlobalSourceNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
ClassInfo &ci = App->imageSourceClasses[classID-1];
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(!globals)
|
|
globals = App->scenesConfig.CreateElement(TEXT("global sources"));
|
|
|
|
XElement *newSourceElement = globals->CreateElement(strName);
|
|
newSourceElement->SetString(TEXT("class"), ci.strClass);
|
|
|
|
if(ci.configProc)
|
|
{
|
|
if(!ci.configProc(newSourceElement, true))
|
|
{
|
|
globals->RemoveElement(newSourceElement);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_SOURCES), LB_ADDSTRING, 0, (LPARAM)strName.Array());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_REMOVE:
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
HWND hwndSceneSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
|
|
UINT id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_SOURCES), LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(!globals)
|
|
break;
|
|
|
|
if(OBSMessageBox(hwnd, Str("GlobalSources.DeleteConfirm"), Str("DeleteConfirm.Title"), MB_YESNO) == IDNO)
|
|
break;
|
|
|
|
App->EnterSceneMutex();
|
|
|
|
XElement *element = globals->GetElementByID(id);
|
|
|
|
if(App->bRunning && App->scene && App->scene->sceneItems.Num())
|
|
{
|
|
for(int i=int(App->scene->sceneItems.Num()-1); i>=0; i--)
|
|
{
|
|
SceneItem *item = App->scene->sceneItems[i];
|
|
if(item->element && scmpi(item->element->GetString(TEXT("class")), TEXT("GlobalSource")) == 0)
|
|
{
|
|
XElement *data = item->element->GetElement(TEXT("data"));
|
|
if(data)
|
|
{
|
|
if(scmpi(data->GetString(TEXT("name")), element->GetName()) == 0)
|
|
{
|
|
LVFINDINFO findInfo;
|
|
findInfo.flags = LVFI_STRING;
|
|
findInfo.psz = (LPCWSTR) item->GetName();
|
|
|
|
int listID = ListView_FindItem(hwndSceneSources, -1, &findInfo);
|
|
if(listID != -1)
|
|
{
|
|
App->bChangingSources = true;
|
|
ListView_DeleteItem(hwndSceneSources, listID);
|
|
App->bChangingSources = false;
|
|
}
|
|
App->scene->RemoveImageSource(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
if(scenes)
|
|
{
|
|
UINT numScenes = scenes->NumElements();
|
|
|
|
for(UINT i=0; i<numScenes; i++)
|
|
{
|
|
XElement *scene = scenes->GetElementByID(i);
|
|
XElement *sources = scene->GetElement(TEXT("sources"));
|
|
if(sources)
|
|
{
|
|
UINT numSources = sources->NumElements();
|
|
|
|
if (numSources)
|
|
{
|
|
for(int j=int(numSources-1); j>=0; j--)
|
|
{
|
|
XElement *source = sources->GetElementByID(j);
|
|
|
|
if(scmpi(source->GetString(TEXT("class")), TEXT("GlobalSource")) == 0)
|
|
{
|
|
XElement *data = source->GetElement(TEXT("data"));
|
|
if(data)
|
|
{
|
|
CTSTR lpName = data->GetString(TEXT("name"));
|
|
if(scmpi(lpName, element->GetName()) == 0)
|
|
{
|
|
LVFINDINFO findInfo;
|
|
findInfo.flags = LVFI_STRING;
|
|
findInfo.psz = (LPCWSTR) source->GetName();
|
|
|
|
int listID = ListView_FindItem(hwndSceneSources, -1, &findInfo);
|
|
if(listID != -1)
|
|
{
|
|
App->bChangingSources = true;
|
|
ListView_DeleteItem(hwndSceneSources, listID);
|
|
App->bChangingSources = false;
|
|
}
|
|
sources->RemoveElement(source);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SendMessage(hwndSources, LB_DELETESTRING, id, 0);
|
|
|
|
if(App->bRunning)
|
|
{
|
|
for(UINT i=0; i<App->globalSources.Num(); i++)
|
|
{
|
|
GlobalSourceInfo &info = App->globalSources[i];
|
|
if(info.strName.CompareI(element->GetName()) && info.source)
|
|
{
|
|
info.FreeData();
|
|
App->globalSources.Remove(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
globals->RemoveElement(element);
|
|
|
|
App->LeaveSceneMutex();
|
|
break;
|
|
}
|
|
|
|
case IDC_RENAME:
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
UINT id = (UINT)SendMessage(hwndSources, LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(!globals)
|
|
break;
|
|
|
|
XElement *element = globals->GetElementByID(id);
|
|
|
|
String strName = element->GetName();
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterGlobalSourceNameDialogProc, (LPARAM)&strName) == IDOK)
|
|
{
|
|
SendMessage(hwndSources, LB_DELETESTRING, id, 0);
|
|
SendMessage(hwndSources, LB_INSERTSTRING, id, (LPARAM)strName.Array());
|
|
SendMessage(hwndSources, LB_SETCURSEL, id, 0);
|
|
|
|
XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes"));
|
|
if(scenes)
|
|
{
|
|
UINT numScenes = scenes->NumElements();
|
|
|
|
for(UINT i=0; i<numScenes; i++)
|
|
{
|
|
XElement *sceneElement = scenes->GetElementByID(i);
|
|
XElement *sources = sceneElement->GetElement(TEXT("sources"));
|
|
if(sources)
|
|
{
|
|
UINT numSources = sources->NumElements();
|
|
|
|
for(UINT j=0; j<numSources; j++)
|
|
{
|
|
XElement *source = sources->GetElementByID(j);
|
|
|
|
if(scmpi(source->GetString(TEXT("class")), TEXT("GlobalSource")) == 0)
|
|
{
|
|
XElement *data = source->GetElement(TEXT("data"));
|
|
if(data)
|
|
{
|
|
CTSTR lpName = data->GetString(TEXT("name"));
|
|
if(scmpi(lpName, element->GetName()) == 0)
|
|
data->SetString(TEXT("name"), strName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
element->SetName(strName);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_IMPORT:
|
|
{
|
|
TCHAR lpFile[MAX_PATH + 1];
|
|
zero(lpFile, sizeof(lpFile));
|
|
|
|
OPENFILENAME ofn;
|
|
zero(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFile = lpFile;
|
|
ofn.hwndOwner = hwndMain;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFilter = TEXT("Scene Files (*.xconfig)\0*.xconfig\0");
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrInitialDir = GlobalConfig->GetString(L"General", L"LastImportExportPath");
|
|
|
|
TCHAR curDirectory[MAX_PATH + 1];
|
|
GetCurrentDirectory(MAX_PATH, curDirectory);
|
|
|
|
BOOL bOpenFile = GetOpenFileName(&ofn);
|
|
SetCurrentDirectory(curDirectory);
|
|
|
|
if(!bOpenFile)
|
|
break;
|
|
|
|
if(GetPathExtension(lpFile).IsEmpty())
|
|
scat(lpFile, L".xconfig");
|
|
|
|
String strSelectedSceneCollectionGlobalSourcesConfig;
|
|
strSelectedSceneCollectionGlobalSourcesConfig = lpFile;
|
|
|
|
if(!App->globalSourcesImportConfig.Open(strSelectedSceneCollectionGlobalSourcesConfig))
|
|
CrashError(TEXT("Could not open '%s"), strSelectedSceneCollectionGlobalSourcesConfig.Array());
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_GLOBAL_SOURCES_IMPORT), hwnd, OBS::GlobalSourcesImportProc) == IDOK)
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
SendMessage(hwndSources, LB_RESETCONTENT, 0, 0);
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(globals)
|
|
{
|
|
UINT numGlobals = globals->NumElements();
|
|
|
|
for(UINT i = 0; i < numGlobals; i++)
|
|
{
|
|
XElement *globalSource = globals->GetElementByID(i);
|
|
SendMessage(hwndSources, LB_ADDSTRING, 0, (LPARAM)globalSource->GetName());
|
|
}
|
|
}
|
|
App->globalSourcesImportConfig.Close();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_CONFIG:
|
|
{
|
|
UINT id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_SOURCES), LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(!globals)
|
|
break;
|
|
|
|
XElement *element = globals->GetElementByID(id);
|
|
CTSTR lpClass = element->GetString(TEXT("class"));
|
|
|
|
ClassInfo *imageSourceClass = App->GetImageSourceClass(lpClass);
|
|
if(imageSourceClass && imageSourceClass->configProc && imageSourceClass->configProc(element, false))
|
|
{
|
|
App->EnterSceneMutex();
|
|
|
|
if(App->bRunning)
|
|
{
|
|
for(UINT i=0; i<App->scene->sceneItems.Num(); i++)
|
|
{
|
|
SceneItem *item = App->scene->sceneItems[i];
|
|
if(item->element && scmpi(item->element->GetString(TEXT("class")), TEXT("GlobalSource")) == 0)
|
|
{
|
|
XElement *data = item->element->GetElement(TEXT("data"));
|
|
if(data)
|
|
{
|
|
if(scmpi(data->GetString(TEXT("name")), element->GetName()) == 0)
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
for(UINT i=0; i<App->globalSources.Num(); i++)
|
|
{
|
|
GlobalSourceInfo &info = App->globalSources[i];
|
|
if(info.strName.CompareI(element->GetName()) && info.source)
|
|
info.source->UpdateSettings();
|
|
}
|
|
}
|
|
|
|
item->Update();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
App->LeaveSceneMutex();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/*case IDC_MOVEUP:
|
|
case IDC_MOVEDOWN:
|
|
case IDC_MOVETOTOP:
|
|
case IDC_MOVETOBOTTOM:
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
|
|
UINT id = (UINT)SendMessage(hwndSources, LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
XElement *globals = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
if(!globals)
|
|
break;
|
|
|
|
XElement *element = globals->GetElementByID(id);
|
|
UINT numElements = globals->NumElements();
|
|
|
|
if(LOWORD(wParam) == IDC_MOVEUP)
|
|
{
|
|
if(id != 0)
|
|
{
|
|
SendMessage(hwndSources, LB_DELETESTRING, id--, 0);
|
|
SendMessage(hwndSources, LB_INSERTSTRING, id, (LPARAM)element->GetName());
|
|
PostMessage(hwndSources, LB_SETCURSEL, id, 0);
|
|
|
|
element->MoveUp();
|
|
}
|
|
}
|
|
else if(LOWORD(wParam) == IDC_MOVEDOWN)
|
|
{
|
|
if(id != (numElements-1))
|
|
{
|
|
SendMessage(hwndSources, LB_DELETESTRING, id++, 0);
|
|
SendMessage(hwndSources, LB_INSERTSTRING, id, (LPARAM)element->GetName());
|
|
PostMessage(hwndSources, LB_SETCURSEL, id, 0);
|
|
|
|
element->MoveDown();
|
|
}
|
|
}
|
|
else if(LOWORD(wParam) == IDC_MOVETOTOP)
|
|
{
|
|
if(id != 0)
|
|
{
|
|
SendMessage(hwndSources, LB_DELETESTRING, id, 0);
|
|
SendMessage(hwndSources, LB_INSERTSTRING, 0, (LPARAM)element->GetName());
|
|
PostMessage(hwndSources, LB_SETCURSEL, 0, 0);
|
|
|
|
element->MoveToTop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(id != (numElements-1))
|
|
{
|
|
SendMessage(hwndSources, LB_DELETESTRING, id, 0);
|
|
SendMessage(hwndSources, LB_INSERTSTRING, (numElements-1), (LPARAM)element->GetName());
|
|
PostMessage(hwndSources, LB_SETCURSEL, (numElements-1), 0);
|
|
|
|
element->MoveToBottom();
|
|
}
|
|
}
|
|
break;
|
|
}*/
|
|
|
|
case IDOK:
|
|
{
|
|
App->scenesConfig.Save();
|
|
EndDialog(hwnd, IDOK);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
{
|
|
App->scenesConfig.Save();
|
|
EndDialog(hwnd, IDOK);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
INT_PTR CALLBACK OBS::GlobalSourcesImportProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LocalizeWindow(hwnd);
|
|
|
|
HWND hwndSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
XElement *globals = App->globalSourcesImportConfig.GetElement(TEXT("global sources"));
|
|
if(globals)
|
|
{
|
|
UINT numGlobals = globals->NumElements();
|
|
|
|
for(UINT i=0; i<numGlobals; i++)
|
|
{
|
|
XElement *globalSource = globals->GetElementByID(i);
|
|
SendMessage(hwndSources, LB_ADDSTRING, 0, (LPARAM)globalSource->GetName());
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
HWND hSources = GetDlgItem(hwnd, IDC_SOURCES);
|
|
int selectedItemsArray[8192];
|
|
|
|
UINT selectedItems = (UINT)SendMessage(hSources, LB_GETSELITEMS, 512, (LPARAM) selectedItemsArray);
|
|
if( selectedItems == LB_ERR)
|
|
break;
|
|
|
|
for(UINT i = 0; i < selectedItems; i++)
|
|
{
|
|
XElement *selectedGlobals = App->globalSourcesImportConfig.GetElement(TEXT("global sources"));
|
|
|
|
XElement *selectedGlobalSources = selectedGlobals->GetElementByID(selectedItemsArray[i]);
|
|
XElement *currentSceneGlobalSources = App->scenesConfig.GetElement(TEXT("global sources"));
|
|
|
|
if(!currentSceneGlobalSources)
|
|
currentSceneGlobalSources = App->scenesConfig.CreateElement(TEXT("global sources"));
|
|
|
|
if(currentSceneGlobalSources)
|
|
{
|
|
XElement *foundGlobalSource = currentSceneGlobalSources->GetElement(selectedGlobalSources->GetName());
|
|
if(foundGlobalSource != NULL && selectedGlobalSources->GetName() != foundGlobalSource->GetName())
|
|
{
|
|
String strExists = Str("ImportGlobalSourceNameExists");
|
|
strExists.FindReplace(TEXT("$1"), selectedGlobalSources->GetName());
|
|
OBSMessageBox(hwnd, strExists, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XElement *newSourceElement = currentSceneGlobalSources->CopyElement(selectedGlobalSources, selectedGlobalSources->GetName());
|
|
newSourceElement->SetString(TEXT("class"), selectedGlobalSources->GetString(TEXT("class")));
|
|
}
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
struct ReconnectInfo
|
|
{
|
|
UINT_PTR timerID;
|
|
UINT secondsLeft;
|
|
};
|
|
|
|
INT_PTR CALLBACK OBS::ReconnectDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LocalizeWindow(hwnd);
|
|
|
|
ReconnectInfo *ri = new ReconnectInfo;
|
|
ri->secondsLeft = App->reconnectTimeout;
|
|
ri->timerID = 1;
|
|
|
|
if(!SetTimer(hwnd, 1, 1000, NULL))
|
|
{
|
|
App->bReconnecting = false;
|
|
EndDialog(hwnd, IDCANCEL);
|
|
delete ri;
|
|
return TRUE;
|
|
}
|
|
|
|
String strText;
|
|
if(App->bReconnecting)
|
|
strText << Str("Reconnecting.Retrying") << UIntString(ri->secondsLeft);
|
|
else
|
|
strText << Str("Reconnecting") << UIntString(ri->secondsLeft);
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_RECONNECTING), strText);
|
|
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)ri);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
ReconnectInfo *ri = (ReconnectInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if(wParam != 1)
|
|
break;
|
|
|
|
if(!ri->secondsLeft)
|
|
{
|
|
if (AppConfig->GetInt(TEXT("Publish"), TEXT("ExperimentalReconnectMode")) == 1 && AppConfig->GetInt(TEXT("Publish"), TEXT("Delay")) == 0)
|
|
App->RestartNetwork();
|
|
else
|
|
SendMessage(hwndMain, OBS_RECONNECT, 0, 0);
|
|
EndDialog(hwnd, IDOK);
|
|
}
|
|
else
|
|
{
|
|
String strText;
|
|
|
|
ri->secondsLeft--;
|
|
|
|
if(App->bReconnecting)
|
|
strText << Str("Reconnecting.Retrying") << UIntString(ri->secondsLeft);
|
|
else
|
|
strText << Str("Reconnecting") << UIntString(ri->secondsLeft);
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_RECONNECTING), strText);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
if(LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
App->bReconnecting = false;
|
|
if (AppConfig->GetInt(TEXT("Publish"), TEXT("ExperimentalReconnectMode")) == 1 && AppConfig->GetInt(TEXT("Publish"), TEXT("Delay")) == 0)
|
|
App->Stop();
|
|
EndDialog(hwnd, IDCANCEL);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
App->bReconnecting = false;
|
|
if (AppConfig->GetInt(TEXT("Publish"), TEXT("ExperimentalReconnectMode")) == 1 && AppConfig->GetInt(TEXT("Publish"), TEXT("Delay")) == 0)
|
|
App->Stop();
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
ReconnectInfo *ri = (ReconnectInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
KillTimer(hwnd, ri->timerID);
|
|
delete ri;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::AddProfilesToMenu(HMENU menu)
|
|
{
|
|
StringList profileList;
|
|
GetProfiles(profileList);
|
|
|
|
for(UINT i=0; i<profileList.Num(); i++)
|
|
{
|
|
String &strProfile = profileList[i];
|
|
|
|
UINT flags = MF_STRING;
|
|
if(strProfile.CompareI(GetCurrentProfile()))
|
|
flags |= MF_CHECKED;
|
|
|
|
AppendMenu(menu, flags, ID_SWITCHPROFILE+i, strProfile.Array());
|
|
}
|
|
}
|
|
|
|
void OBS::AddSceneCollectionToMenu(HMENU menu)
|
|
{
|
|
StringList sceneCollectionList;
|
|
GetSceneCollection(sceneCollectionList);
|
|
|
|
for (UINT i = 0; i < sceneCollectionList.Num(); i++)
|
|
{
|
|
String &strSceneCollection = sceneCollectionList[i];
|
|
|
|
UINT flags = MF_STRING;
|
|
if (strSceneCollection.CompareI(GetCurrentSceneCollection()))
|
|
flags |= MF_CHECKED;
|
|
|
|
AppendMenu(menu, flags, ID_SWITCHSCENECOLLECTION+i, strSceneCollection.Array());
|
|
}
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::AddSceneCollection(SceneCollectionAction action)
|
|
{
|
|
if (App->bRunning)
|
|
return;
|
|
|
|
String strCurSceneCollection = GlobalConfig->GetString(TEXT("General"), TEXT("SceneCollection"));
|
|
|
|
String strSceneCollection;
|
|
if (action == SceneCollectionAction::Rename)
|
|
strSceneCollection = strCurSceneCollection;
|
|
|
|
if (OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterSceneCollectionDialogProc, (LPARAM)&strSceneCollection) != IDOK)
|
|
return;
|
|
|
|
App->scenesConfig.SaveTo(String() << lpAppDataPath << "\\scenes.xconfig");
|
|
App->scenesConfig.Save();
|
|
|
|
String strCurSceneCollectionPath;
|
|
strCurSceneCollectionPath = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, strCurSceneCollection.Array());
|
|
|
|
String strSceneCollectionPath;
|
|
strSceneCollectionPath << lpAppDataPath << TEXT("\\sceneCollection\\") << strSceneCollection << TEXT(".xconfig");
|
|
|
|
if ((action != SceneCollectionAction::Rename || !strSceneCollectionPath.CompareI(strCurSceneCollectionPath)) && OSFileExists(strSceneCollectionPath))
|
|
OBSMessageBox(hwndMain, Str("Settings.General.ScenesExists"), NULL, 0);
|
|
else
|
|
{
|
|
bool success = true;
|
|
App->scenesConfig.Close(true);
|
|
|
|
if (action == SceneCollectionAction::Rename)
|
|
{
|
|
if (!MoveFile(strCurSceneCollectionPath, strSceneCollectionPath))
|
|
success = false;
|
|
}
|
|
else if (action == SceneCollectionAction::Clone)
|
|
{
|
|
if (!CopyFileW(strCurSceneCollectionPath, strSceneCollectionPath, TRUE))
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
if (!App->scenesConfig.Open(strSceneCollectionPath))
|
|
{
|
|
OBSMessageBox(hwndMain, TEXT("Error - unable to create new Scene Collection, could not create file"), NULL, 0);
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
App->scenesConfig.Open(strCurSceneCollectionPath);
|
|
return;
|
|
}
|
|
|
|
GlobalConfig->SetString(TEXT("General"), TEXT("SceneCollection"), strSceneCollection);
|
|
|
|
App->ReloadSceneCollection();
|
|
App->ResetSceneCollectionMenu();
|
|
App->ResetApplicationName();
|
|
App->ReportSceneCollectionsChanged();
|
|
}
|
|
}
|
|
|
|
void OBS::RemoveSceneCollection()
|
|
{
|
|
if (App->bRunning)
|
|
return;
|
|
|
|
String strCurSceneCollection = GlobalConfig->GetString(TEXT("General"), TEXT("SceneCollection"));
|
|
String strCurSceneCollectionFile = strCurSceneCollection + L".xconfig";
|
|
String strCurSceneCollectionDir;
|
|
strCurSceneCollectionDir << lpAppDataPath << TEXT("\\sceneCollection\\");
|
|
|
|
OSFindData ofd;
|
|
HANDLE hFind = OSFindFirstFile(strCurSceneCollectionDir + L"*.xconfig", ofd);
|
|
|
|
if (!hFind)
|
|
{
|
|
Log(L"Find failed for scene collections");
|
|
return;
|
|
}
|
|
|
|
String nextFile;
|
|
|
|
do
|
|
{
|
|
if (scmpi(ofd.fileName, strCurSceneCollectionFile) != 0)
|
|
{
|
|
nextFile = ofd.fileName;
|
|
break;
|
|
}
|
|
} while (OSFindNextFile(hFind, ofd));
|
|
OSFindClose(hFind);
|
|
|
|
if (nextFile.IsEmpty())
|
|
return;
|
|
|
|
String strConfirm = Str("Settings.General.ConfirmDelete");
|
|
strConfirm.FindReplace(TEXT("$1"), strCurSceneCollection);
|
|
if (OBSMessageBox(hwndMain, strConfirm, Str("DeleteConfirm.Title"), MB_YESNO) == IDYES)
|
|
{
|
|
String strCurSceneCollectionPath;
|
|
strCurSceneCollectionPath << strCurSceneCollectionDir << strCurSceneCollection << TEXT(".xconfig");
|
|
OSDeleteFile(strCurSceneCollectionPath);
|
|
App->scenesConfig.Close();
|
|
|
|
GlobalConfig->SetString(L"General", L"SceneCollection", GetPathWithoutExtension(nextFile));
|
|
|
|
App->ReloadSceneCollection();
|
|
App->ResetSceneCollectionMenu();
|
|
App->ReportSceneCollectionsChanged();
|
|
}
|
|
}
|
|
|
|
void OBS::ImportSceneCollection()
|
|
{
|
|
if (OBSMessageBox(hwndMain, Str("ImportCollectionReplaceWarning.Text"), Str("ImportCollectionReplaceWarning.Title"), MB_YESNO) == IDNO)
|
|
return;
|
|
|
|
TCHAR lpFile[MAX_PATH+1];
|
|
zero(lpFile, sizeof(lpFile));
|
|
|
|
OPENFILENAME ofn;
|
|
zero(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFile = lpFile;
|
|
ofn.hwndOwner = hwndMain;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFilter = TEXT("Scene Files (*.xconfig)\0*.xconfig\0");
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrInitialDir = GlobalConfig->GetString(L"General", L"LastImportExportPath");
|
|
|
|
TCHAR curDirectory[MAX_PATH+1];
|
|
GetCurrentDirectory(MAX_PATH, curDirectory);
|
|
|
|
BOOL bOpenFile = GetOpenFileName(&ofn);
|
|
SetCurrentDirectory(curDirectory);
|
|
|
|
if (!bOpenFile)
|
|
return;
|
|
|
|
if (GetPathExtension(lpFile).IsEmpty())
|
|
scat(lpFile, L".xconfig");
|
|
|
|
GlobalConfig->SetString(L"General", L"LastImportExportPath", GetPathDirectory(lpFile));
|
|
|
|
String strCurSceneCollection = GlobalConfig->GetString(TEXT("General"), TEXT("SceneCollection"));
|
|
String strCurSceneCollectionFile;
|
|
strCurSceneCollectionFile << lpAppDataPath << TEXT("\\sceneCollection\\") << strCurSceneCollection << L".xconfig";
|
|
|
|
scenesConfig.Close();
|
|
CopyFile(lpFile, strCurSceneCollectionFile, false);
|
|
App->ReloadSceneCollection();
|
|
App->ReportSceneCollectionsChanged();
|
|
}
|
|
|
|
void OBS::ExportSceneCollection()
|
|
{
|
|
TCHAR lpFile[MAX_PATH+1];
|
|
zero(lpFile, sizeof(lpFile));
|
|
|
|
OPENFILENAME ofn;
|
|
zero(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFile = lpFile;
|
|
ofn.hwndOwner = hwndMain;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFilter = TEXT("Scene Files (*.xconfig)\0*.xconfig\0");
|
|
ofn.nFilterIndex = 1;
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
|
ofn.lpstrInitialDir = GlobalConfig->GetString(L"General", L"LastImportExportPath");
|
|
|
|
TCHAR curDirectory[MAX_PATH+1];
|
|
GetCurrentDirectory(MAX_PATH, curDirectory);
|
|
|
|
BOOL bSaveFile = GetSaveFileName(&ofn);
|
|
SetCurrentDirectory(curDirectory);
|
|
|
|
if (!bSaveFile)
|
|
return;
|
|
|
|
if (GetPathExtension(lpFile).IsEmpty())
|
|
scat(lpFile, L".xconfig");
|
|
|
|
GlobalConfig->SetString(L"General", L"LastImportExportPath", GetPathDirectory(lpFile));
|
|
|
|
scenesConfig.SaveTo(lpFile);
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::AddProfile(ProfileAction action)
|
|
{
|
|
if (App->bRunning)
|
|
return;
|
|
|
|
String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile"));
|
|
|
|
String strProfile;
|
|
if (action == ProfileAction::Rename)
|
|
strProfile = strCurProfile;
|
|
|
|
if (OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_ENTERNAME), hwndMain, OBS::EnterProfileDialogProc, (LPARAM)&strProfile) != IDOK)
|
|
return;
|
|
|
|
String strCurProfilePath;
|
|
strCurProfilePath = FormattedString(L"%s\\profiles\\%s.ini", lpAppDataPath, strCurProfile.Array());
|
|
|
|
String strProfilePath;
|
|
strProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini");
|
|
|
|
if ((action != ProfileAction::Rename || !strProfilePath.CompareI(strCurProfilePath)) && OSFileExists(strProfilePath))
|
|
OBSMessageBox(hwndMain, Str("MainMenu.Profiles.ProfileExists"), NULL, 0);
|
|
else
|
|
{
|
|
bool success = true;
|
|
|
|
if (action == ProfileAction::Rename)
|
|
{
|
|
if (!MoveFile(strCurProfilePath, strProfilePath))
|
|
success = false;
|
|
AppConfig->SetFilePath(strProfilePath);
|
|
}
|
|
else if (action == ProfileAction::Clone)
|
|
{
|
|
if (!CopyFileW(strCurProfilePath, strProfilePath, TRUE))
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
if(!AppConfig->Create(strProfilePath))
|
|
{
|
|
OBSMessageBox(hwndMain, TEXT("Error - unable to create new profile, could not create file"), NULL, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
AppConfig->Open(strCurProfilePath);
|
|
return;
|
|
}
|
|
|
|
GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile);
|
|
|
|
App->ReloadIniSettings();
|
|
App->ResetProfileMenu();
|
|
App->ResetApplicationName();
|
|
}
|
|
}
|
|
|
|
void OBS::RemoveProfile()
|
|
{
|
|
if (App->bRunning)
|
|
return;
|
|
|
|
String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile"));
|
|
|
|
String strCurProfileFile = strCurProfile + L".ini";
|
|
String strCurProfileDir;
|
|
strCurProfileDir << lpAppDataPath << TEXT("\\profiles\\");
|
|
|
|
OSFindData ofd;
|
|
HANDLE hFind = OSFindFirstFile(strCurProfileDir + L"*.ini", ofd);
|
|
|
|
if (!hFind)
|
|
{
|
|
Log(L"Find failed for profile");
|
|
return;
|
|
}
|
|
|
|
String nextFile;
|
|
|
|
do
|
|
{
|
|
if (scmpi(ofd.fileName, strCurProfileFile) != 0)
|
|
{
|
|
nextFile = ofd.fileName;
|
|
break;
|
|
}
|
|
} while (OSFindNextFile(hFind, ofd));
|
|
OSFindClose(hFind);
|
|
|
|
if (nextFile.IsEmpty())
|
|
return;
|
|
|
|
String strConfirm = Str("Settings.General.ConfirmDelete");
|
|
strConfirm.FindReplace(TEXT("$1"), strCurProfile);
|
|
if (OBSMessageBox(hwndMain, strConfirm, Str("DeleteConfirm.Title"), MB_YESNO) == IDYES)
|
|
{
|
|
String strCurProfilePath;
|
|
strCurProfilePath << strCurProfileDir << strCurProfile << TEXT(".ini");
|
|
OSDeleteFile(strCurProfilePath);
|
|
|
|
GlobalConfig->SetString(L"General", L"Profile", GetPathWithoutExtension(nextFile));
|
|
|
|
strCurProfilePath.Clear();
|
|
strCurProfilePath << strCurProfileDir << nextFile;
|
|
if (!AppConfig->Open(strCurProfilePath))
|
|
{
|
|
OBSMessageBox(hwndMain, TEXT("Error - unable to open ini file"), NULL, 0);
|
|
return;
|
|
}
|
|
|
|
App->ReloadIniSettings();
|
|
App->ResetApplicationName();
|
|
App->ResetProfileMenu();
|
|
}
|
|
}
|
|
|
|
void OBS::ImportProfile()
|
|
{
|
|
if (OBSMessageBox(hwndMain, Str("ImportProfileReplaceWarning.Text"), Str("ImportProfileReplaceWarning.Title"), MB_YESNO) == IDNO)
|
|
return;
|
|
|
|
TCHAR lpFile[MAX_PATH+1];
|
|
zero(lpFile, sizeof(lpFile));
|
|
|
|
OPENFILENAME ofn;
|
|
zero(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFile = lpFile;
|
|
ofn.hwndOwner = hwndMain;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFilter = TEXT("Profile Files (*.ini)\0*.ini\0");
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrInitialDir = GlobalConfig->GetString(L"General", L"LastImportExportPath");
|
|
|
|
TCHAR curDirectory[MAX_PATH+1];
|
|
GetCurrentDirectory(MAX_PATH, curDirectory);
|
|
|
|
BOOL bOpenFile = GetOpenFileName(&ofn);
|
|
SetCurrentDirectory(curDirectory);
|
|
|
|
if (!bOpenFile)
|
|
return;
|
|
|
|
if (GetPathExtension(lpFile).IsEmpty())
|
|
scat(lpFile, L".ini");
|
|
|
|
GlobalConfig->SetString(L"General", L"LastImportExportPath", GetPathDirectory(lpFile));
|
|
|
|
String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile"));
|
|
String strCurProfileFile;
|
|
strCurProfileFile << lpAppDataPath << TEXT("\\profiles\\") << strCurProfile << L".ini";
|
|
|
|
CopyFile(lpFile, strCurProfileFile, false);
|
|
|
|
if(!AppConfig->Open(strCurProfileFile))
|
|
{
|
|
OBSMessageBox(hwndMain, TEXT("Error - unable to open ini file"), NULL, 0);
|
|
return;
|
|
}
|
|
|
|
App->ReloadIniSettings();
|
|
}
|
|
|
|
void OBS::ExportProfile()
|
|
{
|
|
TCHAR lpFile[MAX_PATH+1];
|
|
zero(lpFile, sizeof(lpFile));
|
|
|
|
OPENFILENAME ofn;
|
|
zero(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.lpstrFile = lpFile;
|
|
ofn.hwndOwner = hwndMain;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFilter = TEXT("Profile Files (*.ini)\0*.ini\0");
|
|
ofn.nFilterIndex = 1;
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
|
ofn.lpstrInitialDir = GlobalConfig->GetString(L"General", L"LastImportExportPath");
|
|
|
|
TCHAR curDirectory[MAX_PATH+1];
|
|
GetCurrentDirectory(MAX_PATH, curDirectory);
|
|
|
|
BOOL bSaveFile = GetSaveFileName(&ofn);
|
|
SetCurrentDirectory(curDirectory);
|
|
|
|
if (!bSaveFile)
|
|
return;
|
|
|
|
if (GetPathExtension(lpFile).IsEmpty())
|
|
scat(lpFile, L".ini");
|
|
|
|
String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile"));
|
|
String strCurProfileFile;
|
|
strCurProfileFile << lpAppDataPath << TEXT("\\profiles\\") << strCurProfile << L".ini";
|
|
|
|
GlobalConfig->SetString(L"General", L"LastImportExportPath", GetPathDirectory(lpFile));
|
|
|
|
CopyFile(strCurProfileFile, lpFile, false);
|
|
}
|
|
|
|
|
|
//----------------------------
|
|
|
|
void OBS::ResetSceneCollectionMenu()
|
|
{
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
HMENU hmenuSceneCollection = GetSubMenu(hmenuMain, 3);
|
|
while (DeleteMenu(hmenuSceneCollection, 8, MF_BYPOSITION));
|
|
AddSceneCollectionToMenu(hmenuSceneCollection);
|
|
}
|
|
|
|
void OBS::ResetProfileMenu()
|
|
{
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
HMENU hmenuProfiles = GetSubMenu(hmenuMain, 2);
|
|
while (DeleteMenu(hmenuProfiles, 8, MF_BYPOSITION));
|
|
AddProfilesToMenu(hmenuProfiles);
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::DisableMenusWhileStreaming(bool disable)
|
|
{
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
|
|
EnableMenuItem(hmenuMain, 2, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
|
|
EnableMenuItem(hmenuMain, 3, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
|
|
|
|
EnableMenuItem(hmenuMain, ID_HELP_UPLOAD_CURRENT_LOG, (!disable ? MF_ENABLED : MF_DISABLED));
|
|
EnableMenuItem(hmenuMain, ID_HELP_ANALYZE_CURRENT_LOG, (!disable ? MF_ENABLED : MF_DISABLED));
|
|
|
|
DrawMenuBar(hwndMain);
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void LogUploadMonitorCallback()
|
|
{
|
|
PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_REFRESH_LOGS, 0), 0);
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
static HMENU FindParent(HMENU root, UINT id, String *name=nullptr)
|
|
{
|
|
MENUITEMINFO info;
|
|
zero(&info, sizeof(info));
|
|
info.cbSize = sizeof(info);
|
|
info.fMask = MIIM_SUBMENU | (name ? MIIM_STRING : 0);
|
|
|
|
MENUITEMINFO verifier;
|
|
zero(&verifier, sizeof(verifier));
|
|
verifier.cbSize = sizeof(verifier);
|
|
|
|
bool found = false;
|
|
|
|
int count = GetMenuItemCount(root);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
info.cch = 0;
|
|
if (!GetMenuItemInfo(root, i, true, &info))
|
|
continue;
|
|
|
|
if (!info.hSubMenu)
|
|
continue;
|
|
|
|
HMENU submenu = info.hSubMenu;
|
|
if (!GetMenuItemInfo(submenu, id, false, &verifier))
|
|
continue;
|
|
|
|
if (name)
|
|
{
|
|
name->SetLength(info.cch++);
|
|
info.dwTypeData = name->Array();
|
|
GetMenuItemInfo(root, i, true, &info);
|
|
info.dwTypeData = nullptr;
|
|
}
|
|
|
|
found = true;
|
|
|
|
root = submenu;
|
|
i = 0;
|
|
count = GetMenuItemCount(root);
|
|
}
|
|
|
|
return found ? root : nullptr;
|
|
}
|
|
|
|
void OBS::ResetLogUploadMenu()
|
|
{
|
|
String logfilePattern = FormattedString(L"%s/logs/*.log", OBSGetAppDataPath());
|
|
|
|
std::vector<decltype(App->logFiles.cbegin())> validLogs;
|
|
|
|
OSFindData ofd;
|
|
HANDLE finder;
|
|
if (!App->logDirectoryMonitor)
|
|
{
|
|
App->logDirectoryMonitor = OSMonitorDirectoryCallback(String(OBSGetAppDataPath()) << L"/logs/", LogUploadMonitorCallback);
|
|
|
|
if (!(finder = OSFindFirstFile(logfilePattern, ofd)))
|
|
return;
|
|
|
|
char *contents = (char*)Allocate(1024 * 8);
|
|
|
|
do
|
|
{
|
|
if (ofd.bDirectory) continue;
|
|
|
|
String filename = GetPathFileName(ofd.fileName, true);
|
|
auto iter = App->logFiles.emplace(filename.Array(), false).first;
|
|
|
|
XFile f(String(OBSGetAppDataPath()) << L"/logs/" << filename, XFILE_READ | XFILE_SHARED, XFILE_OPENEXISTING);
|
|
if (!f.IsOpen())
|
|
continue;
|
|
|
|
DWORD nRead = f.Read(contents, 1024*8 - 1);
|
|
contents[nRead] = 0;
|
|
|
|
bool validLog = (strstr(contents, "Open Broadcaster Software") != nullptr);
|
|
|
|
if (!validLog)
|
|
continue;
|
|
|
|
iter->second = true;
|
|
validLogs.push_back(iter);
|
|
} while (OSFindNextFile(finder, ofd));
|
|
|
|
Free(contents);
|
|
}
|
|
else
|
|
{
|
|
if (finder = OSFindFirstFile(logfilePattern, ofd))
|
|
{
|
|
auto previous = std::move(App->logFiles);
|
|
|
|
App->logFiles.clear();
|
|
|
|
do
|
|
{
|
|
if (ofd.bDirectory) continue;
|
|
|
|
std::wstring log = GetPathFileName(ofd.fileName, true);
|
|
if (previous.find(log) == previous.end())
|
|
continue;
|
|
|
|
if (!(App->logFiles[log] = previous[log]))
|
|
continue;
|
|
|
|
validLogs.push_back(App->logFiles.find(log));
|
|
} while (OSFindNextFile(finder, ofd));
|
|
}
|
|
else
|
|
App->logFiles.clear();
|
|
}
|
|
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
HMENU hmenuUpload = FindParent(hmenuMain, ID_HELP_UPLOAD_CURRENT_LOG);
|
|
if (!hmenuUpload)
|
|
return;
|
|
|
|
while (DeleteMenu(hmenuUpload, 2, MF_BYPOSITION));
|
|
|
|
if (validLogs.empty())
|
|
return;
|
|
|
|
AppendMenu(hmenuUpload, MF_SEPARATOR, 0, nullptr);
|
|
|
|
AppendMenu(hmenuUpload, MF_STRING, ID_UPLOAD_ANALYZE_LOG, Str("MainMenu.Help.AnalyzeLastLog"));
|
|
AppendMenu(hmenuUpload, MF_STRING, ID_UPLOAD_LOG, Str("MainMenu.Help.UploadLastLog"));
|
|
|
|
AppendMenu(hmenuUpload, MF_SEPARATOR, 0, nullptr);
|
|
|
|
unsigned i = 0;
|
|
for (auto iter = validLogs.rbegin(); iter != validLogs.rend(); i++, iter++)
|
|
{
|
|
HMENU items = CreateMenu();
|
|
AppendMenu(items, MF_STRING, ID_UPLOAD_ANALYZE_LOG + i, Str("LogUpload.Analyze"));
|
|
AppendMenu(items, MF_STRING, ID_UPLOAD_LOG + i, Str("LogUpload.Upload"));
|
|
AppendMenu(items, MF_STRING, ID_VIEW_LOG + i, Str("LogUpload.View"));
|
|
|
|
AppendMenu(hmenuUpload, MF_STRING | MF_POPUP, (UINT_PTR)items, (*iter)->first.c_str());
|
|
}
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
String GetLogUploadMenuItem(UINT item)
|
|
{
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
|
|
String log;
|
|
FindParent(hmenuMain, item, &log);
|
|
|
|
return log;
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
namespace
|
|
{
|
|
struct HLOCALDeleter
|
|
{
|
|
void operator()(void *h) { GlobalFree(h); }
|
|
};
|
|
|
|
struct MemUnlocker
|
|
{
|
|
void operator()(void *m) { GlobalUnlock(m); }
|
|
};
|
|
|
|
struct ClipboardHelper
|
|
{
|
|
ClipboardHelper(HWND owner) { OpenClipboard(owner); }
|
|
~ClipboardHelper() { CloseClipboard(); }
|
|
bool Insert(String &str)
|
|
{
|
|
using namespace std;
|
|
|
|
if (!EmptyClipboard()) return false;
|
|
|
|
unique_ptr<void, HLOCALDeleter> h(GlobalAlloc(LMEM_MOVEABLE, (str.Length() + 1) * sizeof TCHAR));
|
|
if (!h) return false;
|
|
|
|
unique_ptr<void, MemUnlocker> mem(GlobalLock(h.get()));
|
|
if (!mem) return false;
|
|
|
|
tstr_to_wide(str.Array(), (wchar_t*)mem.get(), str.Length() + 1);
|
|
|
|
if (!SetClipboardData(CF_UNICODETEXT, mem.get())) return false;
|
|
|
|
h.release();
|
|
return true;
|
|
}
|
|
|
|
bool Contains(String &str)
|
|
{
|
|
using namespace std;
|
|
|
|
HANDLE h = GetClipboardData(CF_UNICODETEXT);
|
|
if (!h) return false;
|
|
|
|
unique_ptr<void, MemUnlocker> mem(GlobalLock(h));
|
|
if (!mem) return false;
|
|
|
|
return !!str.Compare((CTSTR)mem.get());
|
|
}
|
|
};
|
|
}
|
|
|
|
INT_PTR CALLBACK LogUploadResultProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LocalizeWindow(hwnd);
|
|
|
|
HWND hwndOwner = GetParent(hwnd);
|
|
if (!hwndOwner) hwndOwner = GetDesktopWindow();
|
|
|
|
RECT rc, rcDlg, rcOwner;
|
|
|
|
GetWindowRect(hwndOwner, &rcOwner);
|
|
GetWindowRect(hwnd, &rcDlg);
|
|
CopyRect(&rc, &rcOwner);
|
|
|
|
// Offset the owner and dialog box rectangles so that right and bottom
|
|
// values represent the width and height, and then offset the owner again
|
|
// to discard space taken up by the dialog box.
|
|
|
|
OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
|
|
|
|
// The new position is the sum of half the remaining space and the owner's
|
|
// original position.
|
|
|
|
SetWindowPos(hwnd,
|
|
HWND_TOP,
|
|
rcOwner.left + (rc.right / 2),
|
|
rcOwner.top + (rc.bottom / 2),
|
|
0, 0, // Ignores size arguments.
|
|
SWP_NOSIZE);
|
|
|
|
LogUploadResult &result = *(LogUploadResult*)lParam;
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_URL), result.url.Array());
|
|
|
|
AddClipboardFormatListener(hwnd);
|
|
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
|
|
if (result.openAnalyzerOnSuccess)
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_ANALYZE, 0), 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_CLIPBOARDUPDATE:
|
|
{
|
|
LONG_PTR ptr = GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if (!ptr) break;
|
|
|
|
ClipboardHelper clip(hwnd);
|
|
if (!clip.Contains(((LogUploadResult*)ptr)->url))
|
|
ShowWindow(GetDlgItem(hwnd, IDC_COPIED), SW_HIDE);
|
|
else
|
|
{
|
|
SetWindowText(GetDlgItem(hwnd, IDC_COPIED), Str("LogUpload.SuccessDialog.CopySuccess"));
|
|
ShowWindow(GetDlgItem(hwnd, IDC_COPIED), SW_SHOW);
|
|
}
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_COPY:
|
|
{
|
|
LONG_PTR ptr = GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if (!ptr) break;
|
|
|
|
ClipboardHelper clip(hwnd);
|
|
if (clip.Insert(((LogUploadResult*)ptr)->url))
|
|
SetWindowText(GetDlgItem(hwnd, IDC_COPIED), Str("LogUpload.SuccessDialog.CopySuccess"));
|
|
else
|
|
SetWindowText(GetDlgItem(hwnd, IDC_COPIED), Str("LogUpload.SuccessDialog.CopyFailure"));
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_COPIED), SW_SHOW);
|
|
}
|
|
break;
|
|
case IDOK:
|
|
SendMessage(hwnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
case IDC_ANALYZE:
|
|
{
|
|
LONG_PTR ptr = GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if (!ptr) break;
|
|
|
|
String url = CreateHTTPURL(L"obsproject.com", L"/analyzer", FormattedString(L"?url=%s", ((LogUploadResult*)ptr)->analyzerURL.Array()));
|
|
if (url.IsEmpty())
|
|
break;
|
|
|
|
if (!ShellExecute(nullptr, nullptr, url.Array(), nullptr, nullptr, SW_SHOWDEFAULT))
|
|
OBSMessageBox(hwnd, Str("LogUploader.FailedToAnalyze"), nullptr, MB_ICONERROR);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CTLCOLORSTATIC:
|
|
if (GetDlgCtrlID((HWND)lParam) == IDC_COPIED)
|
|
{
|
|
LONG_PTR ptr = GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if (!ptr) break;
|
|
|
|
ClipboardHelper clip(hwnd);
|
|
if (clip.Contains(((LogUploadResult*)ptr)->url))
|
|
SetTextColor((HDC)wParam, RGB(0, 200, 0));
|
|
else
|
|
SetTextColor((HDC)wParam, RGB(200, 0, 0));
|
|
SetBkColor((HDC)wParam, COLORREF(GetSysColor(COLOR_3DFACE)));
|
|
return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
EndDialog(hwnd, 0);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
RemoveClipboardFormatListener(hwnd);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void ShowLogUploadResult(LogUploadResult &result, bool success)
|
|
{
|
|
if (!success) {
|
|
OBSMessageBox(hwndMain, result.errors.Array(), nullptr, MB_ICONEXCLAMATION);
|
|
return;
|
|
}
|
|
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_LOGUPLOADED), hwndMain, LogUploadResultProc, (LPARAM)&result);
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
static void OBSUpdateLog()
|
|
{
|
|
static unsigned position = 0;
|
|
|
|
String content;
|
|
ReadLogPartial(content, position);
|
|
|
|
if (content.IsEmpty()) return;
|
|
|
|
int start, end;
|
|
SendMessage(hwndLog, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
|
|
|
|
SendMessage(hwndLog, EM_SETSEL, INT_MAX, INT_MAX);
|
|
|
|
SendMessage(hwndLog, EM_REPLACESEL, 0, (LPARAM)content.Array());
|
|
|
|
SendMessage(hwndLog, EM_SETSEL, start, end);
|
|
|
|
App->ReportLogUpdate(content.Array(), content.Length());
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
String OBS::GetApplicationName()
|
|
{
|
|
String name;
|
|
|
|
// we hide the bit version on 32 bit to avoid confusing users who have a 64
|
|
// bit pc unncessarily asking for the 64 bit version under the assumption
|
|
// that the 32 bit version doesn't work or something.
|
|
name << "Profile: " << App->GetCurrentProfile() << " - " << "Scenes: " << App->GetCurrentSceneCollection() << L" - " OBS_VERSION_STRING
|
|
#ifdef _WIN64
|
|
L" - 64bit";
|
|
#else
|
|
L"";
|
|
#endif
|
|
return name;
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
void OBS::ResetApplicationName()
|
|
{
|
|
SetWindowText(hwndMain, GetApplicationName());
|
|
}
|
|
|
|
//----------------------------
|
|
|
|
LRESULT CALLBACK OBS::OBSProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case ID_SETTINGS_SETTINGS:
|
|
case ID_SETTINGS:
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_SETTINGS), hwnd, (DLGPROC)OBS::SettingsDialogProc);
|
|
break;
|
|
|
|
case ID_GLOBALSOURCES:
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_GLOBAL_SOURCES), hwnd, (DLGPROC)OBS::GlobalSourcesProc);
|
|
break;
|
|
|
|
case ID_FILE_SAVE2:
|
|
App->scenesConfig.SaveTo(String() << lpAppDataPath << "\\scenes.xconfig");
|
|
App->scenesConfig.Save();
|
|
break;
|
|
|
|
case ID_TOGGLERECORDING:
|
|
App->RefreshStreamButtons(true);
|
|
App->ToggleRecording();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
|
|
case ID_TOGGLERECORDINGREPLAYBUFFER:
|
|
App->RefreshStreamButtons(true);
|
|
App->ToggleReplayBuffer();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
case ID_EXIT:
|
|
if (App->bRunning)
|
|
{
|
|
if (App->bRecording)
|
|
{
|
|
if (OBSMessageBox(hwnd, Str("CloseWhileActiveWarning.Message"), Str("CloseWhileActiveWarning.Title"), MB_ICONQUESTION | MB_YESNO) == IDYES)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
else if (App->bStreaming)
|
|
{
|
|
if (OBSMessageBox(hwnd, Str("CloseWhileActiveWarning.Message"), Str("CloseWhileActiveWarning.Title"), MB_ICONQUESTION | MB_YESNO) == IDYES)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case ID_RECORDINGSFOLDER:
|
|
case ID_SAVEDREPLAYBUFFERS:
|
|
{
|
|
bool replayBuffer = LOWORD(wParam) == ID_SAVEDREPLAYBUFFERS;
|
|
String path = OSGetDefaultVideoSavePath();
|
|
path = AppConfig->GetString(L"Publish", replayBuffer ? L"ReplayBufferSavePath" : L"SavePath", path.Array());
|
|
path = GetExpandedRecordingDirectoryBase(path);
|
|
|
|
if (!OSFileExists(path))
|
|
{
|
|
String message = replayBuffer ? Str("MainMenu.File.ShowSavedReplayBuffers.DoesNotExist") : Str("MainMenu.File.OpenRecordingsFolder.DoesNotExist");
|
|
message.FindReplace(L"$1", path);
|
|
OBSMessageBox(hwnd, message, Str("MainMenu.File.DirectoryDoesNotExistCaption"), MB_ICONWARNING | MB_OK);
|
|
break;
|
|
}
|
|
|
|
path.FindReplace(L"/", L"\\");
|
|
String lastFile = App->lastOutputFile.FindReplace(L"/", L"\\");
|
|
|
|
LPITEMIDLIST item = nullptr;
|
|
if (lastFile.IsValid() && path == lastFile.Left(path.Length()) && OSFileExists(lastFile) && (item = ILCreateFromPath(lastFile)))
|
|
{
|
|
SHOpenFolderAndSelectItems(item, 0, nullptr, 0);
|
|
ILFree(item);
|
|
}
|
|
else
|
|
ShellExecute(NULL, L"open", path, 0, 0, SW_SHOWNORMAL);
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_ALWAYSONTOP:
|
|
{
|
|
HMENU hmenuBar = GetMenu(hwnd);
|
|
App->bAlwaysOnTop = !App->bAlwaysOnTop;
|
|
CheckMenuItem(hmenuBar, ID_ALWAYSONTOP, (App->bAlwaysOnTop)?MF_CHECKED:MF_UNCHECKED);
|
|
|
|
SetWindowPos(hwndMain, (App->bAlwaysOnTop)?HWND_TOPMOST:HWND_NOTOPMOST, 0, 0, 0, 0,
|
|
SWP_NOSIZE | SWP_NOMOVE);
|
|
}
|
|
break;
|
|
|
|
case ID_FULLSCREENMODE:
|
|
App->SetFullscreenMode(!App->bFullscreenMode);
|
|
break;
|
|
|
|
case ID_SHOWLOG:
|
|
ShowWindow(hwndLogWindow, SW_SHOW);
|
|
SetForegroundWindow(hwndLogWindow);
|
|
break;
|
|
|
|
case ID_HELP_VISITWEBSITE:
|
|
ShellExecute(NULL, TEXT("open"), TEXT("https://obsproject.com"), 0, 0, SW_SHOWNORMAL);
|
|
break;
|
|
|
|
case ID_HELP_OPENHELP:
|
|
ShellExecute(NULL, TEXT("open"), TEXT("http://jp9000.github.io/OBS/"), 0, 0, SW_SHOWNORMAL);
|
|
break;
|
|
|
|
case ID_HELP_CHECK_FOR_UPDATES:
|
|
OSCloseThread(OSCreateThread((XTHREAD)CheckUpdateThread, (LPVOID)1));
|
|
break;
|
|
|
|
case ID_HELP_ANALYZE_CURRENT_LOG:
|
|
case ID_HELP_UPLOAD_CURRENT_LOG:
|
|
if (App->bRunning)
|
|
break;
|
|
|
|
{
|
|
LogUploadResult result;
|
|
result.openAnalyzerOnSuccess = LOWORD(wParam) == ID_HELP_ANALYZE_CURRENT_LOG;
|
|
ShowLogUploadResult(result, UploadCurrentLog(result));
|
|
break;
|
|
}
|
|
|
|
case ID_REFRESH_LOGS:
|
|
App->ResetLogUploadMenu();
|
|
break;
|
|
|
|
/*case ID_DASHBOARD:
|
|
ShellExecute(NULL, TEXT("open"), App->strDashboard, 0, 0, SW_SHOWNORMAL);
|
|
break;*/
|
|
|
|
case ID_SETTINGS_OPENCONFIGFOLDER:
|
|
{
|
|
String strAppPath = API->GetAppDataPath();
|
|
ShellExecute(NULL, TEXT("open"), strAppPath, 0, 0, SW_SHOWNORMAL);
|
|
}
|
|
break;
|
|
|
|
case ID_SETTINGS_OPENLOGFOLDER:
|
|
{
|
|
String strAppPath = API->GetAppDataPath();
|
|
strAppPath << TEXT("\\logs");
|
|
ShellExecute(NULL, TEXT("open"), strAppPath, 0, 0, SW_SHOWNORMAL);
|
|
}
|
|
break;
|
|
|
|
case ID_PLUGINS:
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_PLUGINS), hwnd, (DLGPROC)OBS::PluginsDialogProc);
|
|
break;
|
|
|
|
case ID_MICVOLUME:
|
|
if(HIWORD(wParam) == VOLN_ADJUSTING || HIWORD(wParam) == VOLN_FINALVALUE)
|
|
{
|
|
if(IsWindowEnabled((HWND)lParam))
|
|
{
|
|
App->micVol = GetVolumeControlValue((HWND)lParam);
|
|
|
|
bool finalValue = HIWORD(wParam) == VOLN_FINALVALUE;
|
|
|
|
App->ReportMicVolumeChange(App->micVol, App->micVol < VOLN_MUTELEVEL, finalValue);
|
|
|
|
if(App->micVol < EPSILON)
|
|
App->micVol = 0.0f;
|
|
|
|
if(finalValue)
|
|
{
|
|
AppConfig->SetFloat(TEXT("Audio"), TEXT("MicVolume"), App->micVol);
|
|
if (App->micVol < EPSILON)
|
|
AppConfig->SetFloat(TEXT("Audio"), TEXT("MicMutedVolume"), GetVolumeControlMutedVal((HWND)lParam));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_DESKTOPVOLUME:
|
|
if(HIWORD(wParam) == VOLN_ADJUSTING || HIWORD(wParam) == VOLN_FINALVALUE)
|
|
{
|
|
if(IsWindowEnabled((HWND)lParam))
|
|
{
|
|
App->desktopVol = GetVolumeControlValue((HWND)lParam);
|
|
|
|
bool finalValue = HIWORD(wParam) == VOLN_FINALVALUE;
|
|
|
|
App->ReportDesktopVolumeChange(App->desktopVol, App->desktopVol < VOLN_MUTELEVEL, finalValue);
|
|
|
|
if(App->desktopVol < EPSILON)
|
|
App->desktopVol = 0.0f;
|
|
|
|
if(finalValue)
|
|
{
|
|
AppConfig->SetFloat(TEXT("Audio"), TEXT("DesktopVolume"), App->desktopVol);
|
|
if (App->desktopVol < EPSILON)
|
|
AppConfig->SetFloat(TEXT("Audio"), TEXT("DesktopMutedVolume"), GetVolumeControlMutedVal((HWND)lParam));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ID_DESKTOPVOLUMEMETER:
|
|
case ID_MICVOLUMEMETER:
|
|
if(HIWORD(wParam) == VOLN_METERED)
|
|
{
|
|
App->UpdateAudioMeters();
|
|
}
|
|
break;
|
|
case ID_SCENEEDITOR:
|
|
if(HIWORD(wParam) == BN_CLICKED)
|
|
App->bEditMode = !App->bEditMode;
|
|
break;
|
|
|
|
case ID_SCENES:
|
|
if(HIWORD(wParam) == LBN_SELCHANGE)
|
|
{
|
|
HWND hwndScenes = (HWND)lParam;
|
|
UINT id = (UINT)SendMessage(hwndScenes, LB_GETCURSEL, 0, 0);
|
|
if(id != LB_ERR)
|
|
{
|
|
String strName;
|
|
strName.SetLength((UINT)SendMessage(hwndScenes, LB_GETTEXTLEN, id, 0));
|
|
SendMessage(hwndScenes, LB_GETTEXT, id, (LPARAM)strName.Array());
|
|
App->SetScene(strName);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_SCENECOLLECTION_NEW:
|
|
App->AddSceneCollection(SceneCollectionAction::Add);
|
|
break;
|
|
case ID_SCENECOLLECTION_CLONE:
|
|
App->AddSceneCollection(SceneCollectionAction::Clone);
|
|
break;
|
|
case ID_SCENECOLLECTION_RENAME:
|
|
App->AddSceneCollection(SceneCollectionAction::Rename);
|
|
break;
|
|
case ID_SCENECOLLECTION_REMOVE:
|
|
App->RemoveSceneCollection();
|
|
break;
|
|
case ID_SCENECOLLECTION_IMPORT:
|
|
App->ImportSceneCollection();
|
|
break;
|
|
case ID_SCENECOLLECTION_EXPORT:
|
|
App->ExportSceneCollection();
|
|
break;
|
|
|
|
case ID_PROFILE_NEW:
|
|
App->AddProfile(ProfileAction::Add);
|
|
break;
|
|
case ID_PROFILE_CLONE:
|
|
App->AddProfile(ProfileAction::Clone);
|
|
break;
|
|
case ID_PROFILE_RENAME:
|
|
App->AddProfile(ProfileAction::Rename);
|
|
break;
|
|
case ID_PROFILE_REMOVE:
|
|
App->RemoveProfile();
|
|
break;
|
|
case ID_PROFILE_IMPORT:
|
|
App->ImportProfile();
|
|
break;
|
|
case ID_PROFILE_EXPORT:
|
|
App->ExportProfile();
|
|
break;
|
|
|
|
case ID_TESTSTREAM:
|
|
App->RefreshStreamButtons(true);
|
|
App->bTestStream = true;
|
|
App->ToggleCapturing();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
|
|
case ID_STARTSTOP:
|
|
App->RefreshStreamButtons(true);
|
|
App->ToggleCapturing();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
|
|
case ID_MINIMIZERESTORE:
|
|
{
|
|
bool bMinimizeToNotificationArea = AppConfig->GetInt(TEXT("General"), TEXT("MinimizeToNotificationArea"), 0) != 0;
|
|
if (bMinimizeToNotificationArea)
|
|
{
|
|
if (IsWindowVisible(hwnd))
|
|
{
|
|
ShowWindow(hwnd, SW_MINIMIZE);
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
}
|
|
else
|
|
{
|
|
ShowWindow(hwnd, SW_SHOW);
|
|
ShowWindow(hwnd, SW_RESTORE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsIconic(hwnd))
|
|
ShowWindow(hwnd, SW_RESTORE);
|
|
else
|
|
ShowWindow(hwnd, SW_MINIMIZE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDA_SOURCE_DELETE:
|
|
App->DeleteItems();
|
|
break;
|
|
|
|
case IDA_SOURCE_CENTER:
|
|
App->CenterItems(true, true);
|
|
break;
|
|
|
|
case IDA_SOURCE_CENTER_VER:
|
|
App->CenterItems(false, true);
|
|
break;
|
|
|
|
case IDA_SOURCE_CENTER_HOR:
|
|
App->CenterItems(true, false);
|
|
break;
|
|
|
|
case IDA_SOURCE_LEFT_CANVAS:
|
|
App->MoveItemsToEdge(-1, 0);
|
|
break;
|
|
|
|
case IDA_SOURCE_TOP_CANVAS:
|
|
App->MoveItemsToEdge(0, -1);
|
|
break;
|
|
|
|
case IDA_SOURCE_RIGHT_CANVAS:
|
|
App->MoveItemsToEdge(1, 0);
|
|
break;
|
|
|
|
case IDA_SOURCE_BOTTOM_CANVAS:
|
|
App->MoveItemsToEdge(0, 1);
|
|
break;
|
|
|
|
case IDA_SOURCE_FITTOSCREEN:
|
|
App->FitItemsToScreen();
|
|
break;
|
|
|
|
case IDA_SOURCE_RESETSIZE:
|
|
App->ResetItemSizes();
|
|
break;
|
|
|
|
case IDA_SOURCE_RESETCROP:
|
|
App->ResetItemCrops();
|
|
break;
|
|
|
|
case IDA_SOURCE_MOVEUP:
|
|
App->MoveSourcesUp();
|
|
break;
|
|
|
|
case IDA_SOURCE_MOVEDOWN:
|
|
App->MoveSourcesDown();
|
|
break;
|
|
|
|
case IDA_SOURCE_MOVETOTOP:
|
|
App->MoveSourcesToTop();
|
|
break;
|
|
|
|
case IDA_SOURCE_MOVETOBOTTOM:
|
|
App->MoveSourcesToBottom();
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
// This actually means the user pressed the ESC key
|
|
if(App->bFullscreenMode)
|
|
App->SetFullscreenMode(false);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
UINT id = LOWORD(wParam);
|
|
if (id >= ID_SWITCHPROFILE &&
|
|
id <= ID_SWITCHPROFILE_END)
|
|
{
|
|
if (App->bRunning)
|
|
break;
|
|
|
|
MENUITEMINFO mii;
|
|
zero(&mii, sizeof(mii));
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_STRING;
|
|
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
HMENU hmenuProfiles = GetSubMenu(hmenuMain, 2);
|
|
GetMenuItemInfo(hmenuProfiles, id, FALSE, &mii);
|
|
|
|
String strProfile;
|
|
strProfile.SetLength(mii.cch++);
|
|
mii.dwTypeData = strProfile.Array();
|
|
|
|
GetMenuItemInfo(hmenuProfiles, id, FALSE, &mii);
|
|
|
|
if(!strProfile.CompareI(GetCurrentProfile()))
|
|
{
|
|
String strProfilePath;
|
|
strProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini");
|
|
|
|
if(!AppConfig->Open(strProfilePath))
|
|
{
|
|
OBSMessageBox(hwnd, TEXT("Error - unable to open ini file"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile);
|
|
App->ReloadIniSettings();
|
|
ResetProfileMenu();
|
|
ResetApplicationName();
|
|
}
|
|
}
|
|
else if (id >= ID_SWITCHSCENECOLLECTION &&
|
|
id <= ID_SWITCHSCENECOLLECTION_END)
|
|
{
|
|
if (App->bRunning)
|
|
break;
|
|
|
|
MENUITEMINFO mii;
|
|
zero(&mii, sizeof(mii));
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_STRING;
|
|
|
|
HMENU hmenuMain = GetMenu(hwndMain);
|
|
HMENU hmenuSceneCollection = GetSubMenu(hmenuMain, 3);
|
|
GetMenuItemInfo(hmenuSceneCollection, id, FALSE, &mii);
|
|
|
|
String strSceneCollection;
|
|
strSceneCollection.SetLength(mii.cch++);
|
|
mii.dwTypeData = strSceneCollection.Array();
|
|
|
|
GetMenuItemInfo(hmenuSceneCollection, id, FALSE, &mii);
|
|
|
|
if (!strSceneCollection.CompareI(GetCurrentSceneCollection()))
|
|
{
|
|
if (!App->SetSceneCollection(strSceneCollection))
|
|
{
|
|
OBSMessageBox(hwnd, TEXT("Error - unable to open xconfig file"), NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (id >= ID_UPLOAD_LOG && id <= ID_UPLOAD_LOG_END)
|
|
{
|
|
String log = GetLogUploadMenuItem(id);
|
|
if (log.IsEmpty())
|
|
break;
|
|
|
|
LogUploadResult result;
|
|
ShowLogUploadResult(result, UploadLog(log, result));
|
|
}
|
|
else if (id >= ID_UPLOAD_ANALYZE_LOG && id <= ID_UPLOAD_ANALYZE_LOG_END)
|
|
{
|
|
String log = GetLogUploadMenuItem(id);
|
|
if (log.IsEmpty())
|
|
break;
|
|
|
|
LogUploadResult result;
|
|
result.openAnalyzerOnSuccess = true;
|
|
ShowLogUploadResult(result, UploadLog(log, result));
|
|
}
|
|
else if (id >= ID_VIEW_LOG && id <= ID_VIEW_LOG_END)
|
|
{
|
|
String log = GetLogUploadMenuItem(id);
|
|
if (log.IsEmpty())
|
|
break;
|
|
|
|
String tar = FormattedString(L"%s\\logs\\%s", OBSGetAppDataPath(), log.Array());
|
|
|
|
HINSTANCE result = ShellExecute(nullptr, L"edit", tar.Array(), 0, 0, SW_SHOWNORMAL);
|
|
if (result > (HINSTANCE)32)
|
|
break;
|
|
|
|
result = ShellExecute(nullptr, nullptr, tar.Array(), 0, 0, SW_SHOWNORMAL);
|
|
if (result > (HINSTANCE)32)
|
|
break;
|
|
|
|
if (result == (HINSTANCE)SE_ERR_NOASSOC || result == (HINSTANCE)SE_ERR_ASSOCINCOMPLETE)
|
|
{
|
|
OPENASINFO info;
|
|
info.pcszFile = tar.Array();
|
|
info.pcszClass = nullptr;
|
|
info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
|
|
if (SHOpenWithDialog(nullptr, &info) == S_OK)
|
|
break;
|
|
}
|
|
|
|
String error = Str("Sources.TextSource.FileNotFound");
|
|
OBSMessageBox(hwndMain, error.FindReplace(L"$1", tar.Array()).Array(), nullptr, MB_ICONERROR);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR nmh = *(LPNMHDR)lParam;
|
|
|
|
switch(wParam)
|
|
{
|
|
case ID_SOURCES:
|
|
if(nmh.code == NM_CUSTOMDRAW)
|
|
{
|
|
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
|
|
switch( lplvcd->nmcd.dwDrawStage)
|
|
{
|
|
case CDDS_ITEMPREPAINT:
|
|
|
|
int state, bkMode;
|
|
BOOL checkedState;
|
|
RECT iconRect, textRect, itemRect, gsVIRect;
|
|
COLORREF oldTextColor;
|
|
|
|
String itemText;
|
|
|
|
HDC hdc = lplvcd->nmcd.hdc;
|
|
int itemId = (int)lplvcd->nmcd.dwItemSpec;
|
|
|
|
bool isGlobalSource = false;
|
|
|
|
XElement *sources, *sourcesElement = NULL;
|
|
|
|
sources = App->sceneElement->GetElement(TEXT("sources"));
|
|
|
|
if(sources)
|
|
{
|
|
sourcesElement = sources->GetElementByID(itemId);
|
|
|
|
if(sourcesElement)
|
|
if(scmpi(sourcesElement->GetString(TEXT("class")), TEXT("GlobalSource")) == 0)
|
|
isGlobalSource = true;
|
|
}
|
|
|
|
ListView_GetItemRect(nmh.hwndFrom,itemId, &itemRect, LVIR_BOUNDS);
|
|
ListView_GetItemRect(nmh.hwndFrom,itemId, &textRect, LVIR_LABEL);
|
|
|
|
iconRect.right = textRect.left - 1;
|
|
iconRect.left = iconRect.right - (textRect.bottom - textRect.top);
|
|
iconRect.top = textRect.top + 2;
|
|
iconRect.bottom = textRect.bottom - 2;
|
|
|
|
gsVIRect.left = itemRect.left + 1;
|
|
gsVIRect.right = itemRect.left + 2;
|
|
gsVIRect.top = iconRect.top + 1;
|
|
gsVIRect.bottom = iconRect.bottom - 1;
|
|
|
|
state = ListView_GetItemState(nmh.hwndFrom, itemId, LVIS_SELECTED);
|
|
checkedState = ListView_GetCheckState(nmh.hwndFrom, itemId);
|
|
|
|
oldTextColor = GetTextColor(hdc);
|
|
|
|
if(state&LVIS_SELECTED)
|
|
{
|
|
FillRect(hdc, &itemRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
|
|
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
|
|
}
|
|
else
|
|
{
|
|
FillRect(hdc, &itemRect, (HBRUSH)(COLOR_WINDOW + 1));
|
|
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
|
|
}
|
|
|
|
|
|
HTHEME hTheme = OpenThemeData(hwnd, TEXT("BUTTON"));
|
|
if(hTheme)
|
|
{
|
|
if(checkedState)
|
|
DrawThemeBackground(hTheme, hdc, BP_CHECKBOX, (state&LVIS_SELECTED)?CBS_CHECKEDPRESSED:CBS_CHECKEDNORMAL, &iconRect, NULL);
|
|
else
|
|
DrawThemeBackground(hTheme, hdc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &iconRect, NULL);
|
|
CloseThemeData(hTheme);
|
|
}
|
|
else
|
|
{
|
|
iconRect.right = iconRect.left + iconRect.bottom - iconRect.top;
|
|
if(checkedState)
|
|
DrawFrameControl(hdc,&iconRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_FLAT);
|
|
else
|
|
DrawFrameControl(hdc,&iconRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT);
|
|
}
|
|
|
|
if(isGlobalSource)
|
|
FillRect(hdc, &gsVIRect, (state&LVIS_SELECTED) ? (HBRUSH)(COLOR_HIGHLIGHTTEXT + 1) : (HBRUSH)(COLOR_WINDOWTEXT + 1));
|
|
|
|
textRect.left += 2;
|
|
|
|
if(sourcesElement)
|
|
{
|
|
itemText = sourcesElement->GetName();
|
|
if(itemText.IsValid())
|
|
{
|
|
bkMode = SetBkMode(hdc, TRANSPARENT);
|
|
DrawText(hdc, itemText, slen(itemText), &textRect, DT_LEFT | DT_END_ELLIPSIS | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
|
|
SetBkMode(hdc, bkMode);
|
|
}
|
|
}
|
|
|
|
SetTextColor(hdc, oldTextColor);
|
|
|
|
return CDRF_SKIPDEFAULT;
|
|
}
|
|
|
|
return CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYITEMDRAW;
|
|
}
|
|
|
|
if(nmh.code == LVN_ITEMCHANGED && !App->bChangingSources)
|
|
{
|
|
NMLISTVIEW pnmv = *(LPNMLISTVIEW)lParam;
|
|
if((pnmv.uOldState & LVIS_SELECTED) != (pnmv.uNewState & LVIS_SELECTED))
|
|
{
|
|
/*item selected*/
|
|
App->SelectSources();
|
|
}
|
|
if((pnmv.uOldState & LVIS_STATEIMAGEMASK) != (pnmv.uNewState & LVIS_STATEIMAGEMASK))
|
|
{
|
|
//checks changed
|
|
App->CheckSources();
|
|
}
|
|
}
|
|
else if(nmh.code == NM_DBLCLK)
|
|
{
|
|
NMITEMACTIVATE itemActivate = *(LPNMITEMACTIVATE)lParam;
|
|
UINT selectedID = itemActivate.iItem;
|
|
|
|
/* check to see if the double click is on top of the checkbox
|
|
if so, forget about it */
|
|
LVHITTESTINFO hitInfo;
|
|
hitInfo.pt = itemActivate.ptAction;
|
|
ListView_HitTestEx(nmh.hwndFrom, &hitInfo);
|
|
if(hitInfo.flags & LVHT_ONITEMSTATEICON)
|
|
break;
|
|
|
|
if(!App->sceneElement)
|
|
break;
|
|
|
|
XElement *sourcesElement = App->sceneElement->GetElement(TEXT("sources"));
|
|
if(!sourcesElement)
|
|
break;
|
|
|
|
XElement *selectedElement = sourcesElement->GetElementByID(selectedID);
|
|
if(!selectedElement)
|
|
break;
|
|
|
|
ClassInfo *curClassInfo = App->GetImageSourceClass(selectedElement->GetString(TEXT("class")));
|
|
if(curClassInfo && curClassInfo->configProc)
|
|
{
|
|
Vect2 multiple;
|
|
ImageSource *source;
|
|
|
|
App->EnableSceneSwitching(false);
|
|
|
|
if(App->bRunning && App->scene)
|
|
{
|
|
SceneItem* selectedItem = App->scene->GetSceneItem(selectedID);
|
|
source = selectedItem->GetSource();
|
|
if(source)
|
|
{
|
|
Vect2 curSize = Vect2(selectedElement->GetFloat(TEXT("cx"), 32.0f), selectedElement->GetFloat(TEXT("cy"), 32.0f));
|
|
Vect2 baseSize = source->GetSize();
|
|
|
|
multiple = curSize/baseSize;
|
|
}
|
|
}
|
|
|
|
if(curClassInfo->configProc(selectedElement, false))
|
|
{
|
|
if(App->bRunning)
|
|
{
|
|
App->EnterSceneMutex();
|
|
|
|
if(App->scene)
|
|
{
|
|
Vect2 newSize = Vect2(selectedElement->GetFloat(TEXT("cx"), 32.0f), selectedElement->GetFloat(TEXT("cy"), 32.0f));
|
|
newSize *= multiple;
|
|
|
|
selectedElement->SetFloat(TEXT("cx"), newSize.x);
|
|
selectedElement->SetFloat(TEXT("cy"), newSize.y);
|
|
|
|
SceneItem* selectedItem = App->scene->GetSceneItem(selectedID);
|
|
if(selectedItem->GetSource())
|
|
selectedItem->GetSource()->UpdateSettings();
|
|
selectedItem->Update();
|
|
}
|
|
|
|
App->LeaveSceneMutex();
|
|
}
|
|
}
|
|
|
|
App->EnableSceneSwitching(true);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_TOGGLERECORDING:
|
|
if (nmh.code == BCN_DROPDOWN)
|
|
{
|
|
LPNMBCDROPDOWN drop = (LPNMBCDROPDOWN)lParam;
|
|
|
|
HMENU menu = CreatePopupMenu();
|
|
AppendMenu(menu, MF_STRING, 1, App->bRecordingReplayBuffer ? Str("MainWindow.StopReplayBuffer") : Str("MainWindow.StartReplayBuffer"));
|
|
AppendMenu(menu, MF_STRING | (App->bRecording == !App->bRecordingReplayBuffer ? MF_DISABLED : 0), 2,
|
|
App->bRecording ? Str("MainWindow.StopRecordingAndReplayBuffer") : Str("MainWindow.StartRecordingAndReplayBuffer"));
|
|
|
|
POINT p;
|
|
p.x = drop->rcButton.right;
|
|
p.y = drop->rcButton.bottom;
|
|
ClientToScreen(GetDlgItem(hwndMain, ID_TOGGLERECORDING), &p);
|
|
|
|
switch (TrackPopupMenu(menu, TPM_RETURNCMD | TPM_RIGHTALIGN, p.x, p.y, 0, GetDlgItem(hwndMain, ID_TOGGLERECORDING), nullptr))
|
|
{
|
|
case 1:
|
|
App->RefreshStreamButtons(true);
|
|
App->ToggleReplayBuffer();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
case 2:
|
|
App->RefreshStreamButtons(true);
|
|
App->ToggleRecording();
|
|
App->ToggleReplayBuffer();
|
|
App->RefreshStreamButtons();
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
App->bDragResize = true;
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
if(App->bSizeChanging)
|
|
{
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
App->ResizeWindow(true);
|
|
App->bSizeChanging = false;
|
|
}
|
|
App->bDragResize = false;
|
|
break;
|
|
|
|
case WM_DRAWITEM:
|
|
if(wParam == ID_STATUS)
|
|
{
|
|
DRAWITEMSTRUCT &dis = *(DRAWITEMSTRUCT*)lParam; //don't dis me bro
|
|
App->DrawStatusBar(dis);
|
|
}
|
|
break;
|
|
|
|
case WM_SIZING:
|
|
{
|
|
RECT &screenSize = *(RECT*)lParam;
|
|
|
|
int newWidth = MAX(screenSize.right - screenSize.left, minClientWidth+App->borderXSize);
|
|
int newHeight = MAX(screenSize.bottom - screenSize.top , minClientHeight+App->borderYSize);
|
|
|
|
/*int maxCX = GetSystemMetrics(SM_CXFULLSCREEN);
|
|
int maxCY = GetSystemMetrics(SM_CYFULLSCREEN);
|
|
|
|
if(newWidth > maxCX)
|
|
newWidth = maxCX;
|
|
if(newHeight > maxCY)
|
|
newHeight = maxCY;*/
|
|
|
|
if(wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
|
|
screenSize.left = screenSize.right - newWidth;
|
|
else
|
|
screenSize.right = screenSize.left + newWidth;
|
|
|
|
if(wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT)
|
|
screenSize.bottom = screenSize.top + newHeight;
|
|
else
|
|
screenSize.top = screenSize.bottom - newHeight;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
|
|
if(wParam != SIZE_MINIMIZED && (App->clientWidth != client.right || App->clientHeight != client.bottom))
|
|
{
|
|
App->clientWidth = client.right;
|
|
App->clientHeight = client.bottom;
|
|
App->bSizeChanging = true;
|
|
|
|
if(wParam == SIZE_MAXIMIZED)
|
|
App->ResizeWindow(true);
|
|
else
|
|
App->ResizeWindow(!App->bDragResize);
|
|
|
|
if(!App->bDragResize)
|
|
App->bSizeChanging = false;
|
|
}
|
|
else if (wParam == SIZE_MINIMIZED && AppConfig->GetInt(TEXT("General"), TEXT("MinimizeToNotificationArea"), 0) != 0)
|
|
{
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OBS_REQUESTSTOP:
|
|
if (!App->IsRunning())
|
|
break;
|
|
|
|
App->Stop();
|
|
|
|
if((App->bFirstConnect && App->totalStreamTime < 30000) || !App->bAutoReconnect)
|
|
{
|
|
if(App->streamReport.IsValid())
|
|
{
|
|
OBSMessageBox(hwndMain, App->streamReport.Array(), Str("StreamReport"), MB_ICONEXCLAMATION);
|
|
App->streamReport.Clear();
|
|
}
|
|
else if (wParam != 1)
|
|
OBSMessageBox(hwnd, Str("Connection.Disconnected"), NULL, MB_ICONEXCLAMATION);
|
|
}
|
|
else if(wParam == 0)
|
|
{
|
|
//FIXME: show some kind of non-modal notice to the user explaining why they got disconnected
|
|
//status bar would be nice, but that only renders when we're streaming.
|
|
PlaySound((LPCTSTR)SND_ALIAS_SYSTEMASTERISK, NULL, SND_ALIAS_ID | SND_ASYNC);
|
|
|
|
if (!App->reconnectTimeout)
|
|
{
|
|
//fire immediately
|
|
PostMessage(hwndMain, OBS_RECONNECT, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
App->bReconnecting = false;
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwnd, OBS::ReconnectDialogProc);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case OBS_NETWORK_FAILED:
|
|
if ((App->bFirstConnect && App->totalStreamTime < 30000) || !App->bAutoReconnect)
|
|
{
|
|
//no reconnect, or the connection died very early in the stream
|
|
App->Stop();
|
|
if (App->streamReport.IsValid())
|
|
{
|
|
OBSMessageBox(hwndMain, App->streamReport.Array(), Str("StreamReport"), MB_ICONEXCLAMATION);
|
|
App->streamReport.Clear();
|
|
}
|
|
else
|
|
OBSMessageBox(hwnd, Str("Connection.Disconnected"), NULL, MB_ICONEXCLAMATION);
|
|
}
|
|
else
|
|
{
|
|
PlaySound((LPCTSTR)SND_ALIAS_SYSTEMASTERISK, NULL, SND_ALIAS_ID | SND_ASYNC);
|
|
|
|
if (!App->reconnectTimeout)
|
|
{
|
|
//fire immediately
|
|
App->RestartNetwork();
|
|
}
|
|
else
|
|
{
|
|
OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwnd, OBS::ReconnectDialogProc);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OBS_CALLHOTKEY:
|
|
App->CallHotkey((DWORD)lParam, wParam != 0);
|
|
break;
|
|
|
|
case OBS_RECONNECT:
|
|
App->bReconnecting = true;
|
|
App->Start();
|
|
break;
|
|
|
|
case OBS_SETSCENE:
|
|
{
|
|
TSTR lpScene = (TSTR)lParam;
|
|
App->SetScene(lpScene);
|
|
Free(lpScene);
|
|
break;
|
|
}
|
|
case OBS_SETSOURCEORDER:
|
|
{
|
|
StringList *order = (StringList*)lParam;
|
|
App->SetSourceOrder(*order);
|
|
delete(order);
|
|
break;
|
|
}
|
|
case OBS_SETSOURCERENDER:
|
|
{
|
|
bool render = lParam > 0;
|
|
CTSTR sourceName = (CTSTR) wParam;
|
|
App->SetSourceRender(sourceName, render);
|
|
Free((void *)sourceName);
|
|
break;
|
|
}
|
|
case OBS_UPDATESTATUSBAR:
|
|
App->SetStatusBarData();
|
|
break;
|
|
|
|
case OBS_CONFIGURE_STREAM_BUTTONS:
|
|
App->ConfigureStreamButtons();
|
|
break;
|
|
|
|
case OBS_NOTIFICATIONAREA:
|
|
// the point is to only perform the show/hide (minimize) or the menu creation if no modal dialogs are opened
|
|
// if a modal dialog is topmost, then simply focus it
|
|
switch (lParam)
|
|
{
|
|
case WM_LBUTTONUP:
|
|
if (IsWindowEnabled(hwnd))
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(ID_MINIMIZERESTORE, 0), 0);
|
|
else
|
|
SetForegroundWindow(GetWindow(hwnd, GW_ENABLEDPOPUP));
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
if (IsWindowEnabled(hwnd))
|
|
{
|
|
HMENU hMenu = CreatePopupMenu();
|
|
if (AppConfig->GetInt(TEXT("General"), TEXT("MinimizeToNotificationArea"), 0) != 0)
|
|
{
|
|
AppendMenu(hMenu, MF_STRING, ID_MINIMIZERESTORE, IsWindowVisible(hwnd) ? Str("Settings.General.HideObs") : Str("Settings.General.ShowObs"));
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
}
|
|
AddProfilesToMenu(hMenu);
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
if (App->bRunning)
|
|
{
|
|
if (App->bTestStream)
|
|
AppendMenu(hMenu, MF_STRING, ID_TESTSTREAM, Str("MainWindow.StopTest"));
|
|
else
|
|
AppendMenu(hMenu, MF_STRING, ID_STARTSTOP, Str("MainWindow.StopStream"));
|
|
}
|
|
else
|
|
AppendMenu(hMenu, MF_STRING, ID_STARTSTOP, Str("MainWindow.StartStream"));
|
|
if (!IsIconic(hwnd))
|
|
AppendMenu(hMenu, MF_STRING | (App->bFullscreenMode ? MF_CHECKED : 0), ID_FULLSCREENMODE, Str("MainMenu.Settings.FullscreenMode"));
|
|
AppendMenu(hMenu, MF_STRING | (App->bAlwaysOnTop ? MF_CHECKED : 0), ID_ALWAYSONTOP, Str("MainMenu.Settings.AlwaysOnTop"));
|
|
AppendMenu(hMenu, MF_STRING, ID_FILE_EXIT, Str("MainWindow.Exit"));
|
|
|
|
SetForegroundWindow(hwnd);
|
|
POINT p;
|
|
GetCursorPos(&p);
|
|
TrackPopupMenu(hMenu, TPM_LEFTALIGN, p.x, p.y, 0, hwnd, NULL);
|
|
DestroyMenu(hMenu);
|
|
}
|
|
else
|
|
SetForegroundWindow(GetWindow(hwnd, GW_ENABLEDPOPUP));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
if (App->bRunning)
|
|
{
|
|
if (App->bRecording)
|
|
{
|
|
if (OBSMessageBox(hwnd, Str("CloseWhileActiveWarning.Message"), Str("CloseWhileActiveWarning.Title"), MB_ICONQUESTION | MB_YESNO) == IDYES)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
else if (App->bStreaming)
|
|
{
|
|
if (OBSMessageBox(hwnd, Str("CloseWhileActiveWarning.Message"), Str("CloseWhileActiveWarning.Title"), MB_ICONQUESTION | MB_YESNO) == IDYES)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
if (wParam == TRUE)
|
|
{
|
|
// AppConfig / GlobalConfig should already save most important changes. A few UI
|
|
// things like last window size / position and other UI state gets lost though.
|
|
App->scenesConfig.Save();
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
if (App && message == App->wmExplorerRestarted)
|
|
{
|
|
if(App->bNotificationAreaIcon)
|
|
{
|
|
App->bNotificationAreaIcon = false;
|
|
App->ShowNotificationAreaIcon();
|
|
}
|
|
}
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ItemModifyType GetItemModifyType(const Vect2 &mousePos, const Vect2 &itemPos, const Vect2 &itemSize, const Vect4 &crop, const Vect2 &scaleVal)
|
|
{
|
|
Vect2 lowerRight = itemPos+itemSize;
|
|
float epsilon = 10.0f;
|
|
|
|
Vect2 croppedItemPos = itemPos + Vect2(crop.x / scaleVal.x, crop.y / scaleVal.y);
|
|
Vect2 croppedLowerRight = lowerRight - Vect2(crop.w / scaleVal.x, crop.z / scaleVal.y);
|
|
|
|
if( mousePos.x < croppedItemPos.x ||
|
|
mousePos.y < croppedItemPos.y ||
|
|
mousePos.x > croppedLowerRight.x ||
|
|
mousePos.y > croppedLowerRight.y )
|
|
{
|
|
return ItemModifyType_None;
|
|
}
|
|
|
|
// Corner sizing
|
|
if(mousePos.CloseTo(croppedItemPos, epsilon))
|
|
return ItemModifyType_ScaleTopLeft;
|
|
else if(mousePos.CloseTo(croppedLowerRight, epsilon))
|
|
return ItemModifyType_ScaleBottomRight;
|
|
else if(mousePos.CloseTo(Vect2(croppedLowerRight.x, croppedItemPos.y), epsilon))
|
|
return ItemModifyType_ScaleTopRight;
|
|
else if(mousePos.CloseTo(Vect2(croppedItemPos.x, croppedLowerRight.y), epsilon))
|
|
return ItemModifyType_ScaleBottomLeft;
|
|
|
|
epsilon = 4.0f;
|
|
|
|
// Edge sizing
|
|
if(CloseFloat(mousePos.x, croppedItemPos.x, epsilon))
|
|
return ItemModifyType_ScaleLeft;
|
|
else if(CloseFloat(mousePos.x, croppedLowerRight.x, epsilon))
|
|
return ItemModifyType_ScaleRight;
|
|
else if(CloseFloat(mousePos.y, croppedItemPos.y, epsilon))
|
|
return ItemModifyType_ScaleTop;
|
|
else if(CloseFloat(mousePos.y, croppedLowerRight.y, epsilon))
|
|
return ItemModifyType_ScaleBottom;
|
|
|
|
|
|
return ItemModifyType_Move;
|
|
}
|
|
|
|
/**
|
|
* Maps a point in window coordinates to frame coordinates.
|
|
*/
|
|
Vect2 OBS::MapWindowToFramePos(Vect2 mousePos)
|
|
{
|
|
if(App->renderFrameIn1To1Mode)
|
|
return (mousePos - App->GetRenderFrameOffset()) * (App->GetBaseSize() / App->GetOutputSize());
|
|
return (mousePos - App->GetRenderFrameOffset()) * (App->GetBaseSize() / App->GetRenderFrameSize());
|
|
}
|
|
|
|
/**
|
|
* Maps a point in frame coordinates to window coordinates.
|
|
*/
|
|
Vect2 OBS::MapFrameToWindowPos(Vect2 framePos)
|
|
{
|
|
if(App->renderFrameIn1To1Mode)
|
|
return framePos * (App->GetOutputSize() / App->GetBaseSize()) + App->GetRenderFrameOffset();
|
|
return framePos * (App->GetRenderFrameSize() / App->GetBaseSize()) + App->GetRenderFrameOffset();
|
|
}
|
|
|
|
/**
|
|
* Maps a size in window coordinates to frame coordinates.
|
|
*/
|
|
Vect2 OBS::MapWindowToFrameSize(Vect2 windowSize)
|
|
{
|
|
if(App->renderFrameIn1To1Mode)
|
|
return windowSize * (App->GetBaseSize() / App->GetOutputSize());
|
|
return windowSize * (App->GetBaseSize() / App->GetRenderFrameSize());
|
|
}
|
|
|
|
/**
|
|
* Maps a size in frame coordinates to window coordinates.
|
|
*/
|
|
Vect2 OBS::MapFrameToWindowSize(Vect2 frameSize)
|
|
{
|
|
if(App->renderFrameIn1To1Mode)
|
|
return frameSize * (App->GetOutputSize() / App->GetBaseSize());
|
|
return frameSize * (App->GetRenderFrameSize() / App->GetBaseSize());
|
|
}
|
|
|
|
/**
|
|
* Returns the scale of the window relative to the actual frame size. E.g.
|
|
* if the window is twice the size of the frame this will return "0.5".
|
|
*/
|
|
Vect2 OBS::GetWindowToFrameScale()
|
|
{
|
|
return MapWindowToFrameSize(Vect2(1.0f, 1.0f));
|
|
}
|
|
|
|
/**
|
|
* Returns the scale of the frame relative to the window size. E.g.
|
|
* if the window is twice the size of the frame this will return "2.0".
|
|
*/
|
|
Vect2 OBS::GetFrameToWindowScale()
|
|
{
|
|
return MapFrameToWindowSize(Vect2(1.0f, 1.0f));
|
|
}
|
|
|
|
bool OBS::EnsureCropValid(SceneItem *&scaleItem, Vect2 &minSize, Vect2 &snapSize, bool bSnap, int cropEdges, bool cropSymmetric)
|
|
{
|
|
Vect2 scale = (scaleItem->GetSource() ? scaleItem->GetSource()->GetSize() : scaleItem->GetSize()) / scaleItem->GetSize();
|
|
|
|
// When keep aspect is on, cropping can only be half size - 2 * minsize
|
|
if (cropSymmetric)
|
|
{
|
|
if (cropEdges & (edgeLeft | edgeRight))
|
|
{
|
|
if (scaleItem->GetCrop().x > (scaleItem->size.x / 2 ) - 2 * minSize.x)
|
|
{
|
|
scaleItem->crop.x = ((scaleItem->size.x / 2 ) - 2 * minSize.x) * scale.x;
|
|
scaleItem->crop.w = ((scaleItem->size.x / 2 ) - 2 * minSize.x) * scale.x;
|
|
}
|
|
scaleItem->crop.x = (scaleItem->crop.x < 0.0f) ? 0.0f : scaleItem->crop.x;
|
|
scaleItem->crop.w = (scaleItem->crop.w < 0.0f) ? 0.0f : scaleItem->crop.w;
|
|
}
|
|
if (cropEdges & (edgeTop | edgeBottom))
|
|
{
|
|
if (scaleItem->GetCrop().y > (scaleItem->size.y / 2 ) - 2 * minSize.y)
|
|
{
|
|
scaleItem->crop.y = ((scaleItem->size.y / 2 ) - 2 * minSize.y) * scale.y;
|
|
scaleItem->crop.z = ((scaleItem->size.y / 2 ) - 2 * minSize.y) * scale.y;
|
|
}
|
|
scaleItem->crop.y = (scaleItem->crop.y < 0.0f) ? 0.0f : scaleItem->crop.y;
|
|
scaleItem->crop.z = (scaleItem->crop.z < 0.0f) ? 0.0f : scaleItem->crop.z;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// left
|
|
if (scaleItem->GetCrop().x > (scaleItem->size.x - scaleItem->GetCrop().w - 32) - minSize.x && cropEdges & edgeLeft)
|
|
{
|
|
scaleItem->crop.x = ((scaleItem->size.x - scaleItem->GetCrop().w - 32) - minSize.x) * scale.x;
|
|
}
|
|
scaleItem->crop.x = (scaleItem->crop.x < 0.0f) ? 0.0f : scaleItem->crop.x;
|
|
|
|
// top
|
|
if (scaleItem->GetCrop().y > (scaleItem->size.y - scaleItem->GetCrop().z - 32) - minSize.y && cropEdges & edgeTop)
|
|
{
|
|
scaleItem->crop.y = ((scaleItem->size.y - scaleItem->GetCrop().z - 32) - minSize.y) * scale.y;
|
|
}
|
|
scaleItem->crop.y = (scaleItem->crop.y < 0.0f) ? 0.0f : scaleItem->crop.y;
|
|
|
|
// right
|
|
if (scaleItem->GetCrop().w > (scaleItem->size.x - scaleItem->GetCrop().x - 32) - minSize.x && cropEdges & edgeRight)
|
|
{
|
|
scaleItem->crop.w = ((scaleItem->size.x - scaleItem->GetCrop().x - 32) - minSize.x) * scale.x;
|
|
}
|
|
scaleItem->crop.w = (scaleItem->crop.w < 0.0f) ? 0.0f : scaleItem->crop.w;
|
|
|
|
// bottom
|
|
if (scaleItem->GetCrop().z > (scaleItem->size.y - scaleItem->GetCrop().y - 32) - minSize.y && cropEdges & edgeBottom)
|
|
{
|
|
scaleItem->crop.z = ((scaleItem->size.y - scaleItem->GetCrop().y - 32) - minSize.y) * scale.y;
|
|
}
|
|
scaleItem->crop.z = (scaleItem->crop.z < 0.0f) ? 0.0f : scaleItem->crop.z;
|
|
}
|
|
if (bSnap)
|
|
{
|
|
// left
|
|
if(CloseFloat(scaleItem->GetCrop().x, 0.0f, snapSize.x))
|
|
{
|
|
scaleItem->crop.x = 0.0f;
|
|
}
|
|
// top
|
|
if(CloseFloat(scaleItem->GetCrop().y, 0.0f, snapSize.y))
|
|
{
|
|
scaleItem->crop.y = 0.0f;
|
|
}
|
|
// right
|
|
if(CloseFloat(scaleItem->GetCrop().w, 0.0f, snapSize.x))
|
|
{
|
|
scaleItem->crop.w = 0.0f;
|
|
}
|
|
// bottom
|
|
if(CloseFloat(scaleItem->GetCrop().z, 0.0f, snapSize.y))
|
|
{
|
|
scaleItem->crop.z = 0.0f;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
LRESULT CALLBACK OBS::ProjectorFrameProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message) {
|
|
case WM_KEYDOWN:
|
|
if (wParam == VK_ESCAPE)
|
|
App->bPleaseDisableProjector = true;
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
App->bPleaseDisableProjector = true;
|
|
break;
|
|
|
|
case WM_SETCURSOR:
|
|
if (App->bEnableProjectorCursor)
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
else
|
|
SetCursor(NULL);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CALLBACK OBS::RenderFrameProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
|
|
if(message == WM_ERASEBKGND)
|
|
{
|
|
if(App->bRenderViewEnabled && App->bRunning)
|
|
return 1L;
|
|
else
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
else if(message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN)
|
|
{
|
|
POINTS pos;
|
|
pos.x = (short)LOWORD(lParam);
|
|
pos.y = (short)HIWORD(lParam);
|
|
|
|
if((App->bEditMode || message == WM_RBUTTONDOWN) && App->scene)
|
|
{
|
|
Vect2 mousePos = Vect2(float(pos.x), float(pos.y));
|
|
Vect2 framePos = MapWindowToFramePos(mousePos);
|
|
|
|
bool bControlDown = HIBYTE(GetKeyState(VK_LCONTROL)) != 0 || HIBYTE(GetKeyState(VK_RCONTROL)) != 0;
|
|
|
|
SetFocus(hwnd);
|
|
|
|
List<SceneItem*> items;
|
|
App->scene->GetSelectedItems(items);
|
|
if(!items.Num())
|
|
{
|
|
App->scene->GetItemsOnPoint(framePos, items);
|
|
|
|
if(items.Num())
|
|
{
|
|
SceneItem *topItem = items.Last();
|
|
App->bItemWasSelected = topItem->bSelected;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
/* clears all selections */
|
|
App->bChangingSources = true;
|
|
ListView_SetItemState(hwndSources, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
App->bChangingSources = false;
|
|
|
|
App->scene->DeselectAll();
|
|
}
|
|
|
|
topItem->Select(true);
|
|
App->bChangingSources = true;
|
|
|
|
ListView_SetItemState(hwndSources, topItem->GetID(), LVIS_SELECTED, LVIS_SELECTED);
|
|
App->bChangingSources = false;
|
|
|
|
if (App->bEditMode)
|
|
if(App->modifyType == ItemModifyType_None)
|
|
App->modifyType = ItemModifyType_Move;
|
|
}
|
|
else if(!bControlDown) //clicked on empty space without control
|
|
{
|
|
/* clears all selections */
|
|
App->bChangingSources = true;
|
|
ListView_SetItemState(hwndSources, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
App->bChangingSources = false;
|
|
|
|
App->scene->DeselectAll();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
App->scene->GetItemsOnPoint(framePos, items);
|
|
if(items.Num())
|
|
{
|
|
SceneItem *topItem = items.Last();
|
|
App->bItemWasSelected = topItem->bSelected;
|
|
}
|
|
}
|
|
|
|
if (message == WM_LBUTTONDOWN)
|
|
{
|
|
App->bMouseDown = true;
|
|
App->lastMousePos = App->startMousePos = mousePos;
|
|
|
|
App->scene->GetSelectedItems(items);
|
|
for(UINT i=0; i<items.Num(); i++)
|
|
{
|
|
items[i]->startPos = items[i]->pos;
|
|
items[i]->startSize = items[i]->size;
|
|
}
|
|
}
|
|
else
|
|
App->bRMouseDown = true;
|
|
}
|
|
else
|
|
{
|
|
//SendMessage(hwndMain, WM_NCLBUTTONDOWN, HTCAPTION, (LPARAM)&pos);
|
|
return 0;
|
|
}
|
|
}
|
|
else if(message == WM_MOUSEMOVE)
|
|
{
|
|
if(App->bEditMode && App->scene)
|
|
{
|
|
POINTS pos;
|
|
pos.x = (short)LOWORD(lParam);
|
|
pos.y = (short)HIWORD(lParam);
|
|
Vect2 mousePos = Vect2(float(pos.x), float(pos.y));
|
|
Vect2 scaleVal = GetWindowToFrameScale();
|
|
|
|
SceneItem *&scaleItem = App->scaleItem; //just reduces a bit of typing
|
|
|
|
if(!App->bMouseDown)
|
|
{
|
|
List<SceneItem*> items;
|
|
App->scene->GetSelectedItems(items);
|
|
|
|
App->scaleItem = NULL;
|
|
App->modifyType = ItemModifyType_None;
|
|
|
|
for(int i=int(items.Num()-1); i>=0; i--)
|
|
{
|
|
SceneItem *item = items[i];
|
|
|
|
// Get item in window coordinates
|
|
Vect2 adjPos = MapFrameToWindowPos(item->GetPos());
|
|
Vect2 adjSize = MapFrameToWindowSize(item->GetSize());
|
|
Vect2 adjSizeBase = MapFrameToWindowSize(item->GetSource() ? item->GetSource()->GetSize() : item->GetSize());
|
|
|
|
ItemModifyType curType = GetItemModifyType(mousePos, adjPos, adjSize, item->GetCrop(), scaleVal);
|
|
if(curType > ItemModifyType_Move)
|
|
{
|
|
App->modifyType = curType;
|
|
scaleItem = item;
|
|
break;
|
|
}
|
|
else if(curType == ItemModifyType_Move)
|
|
App->modifyType = ItemModifyType_Move;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vect2 baseRenderSize = App->GetBaseSize();
|
|
Vect2 framePos = MapWindowToFramePos(mousePos);
|
|
|
|
if(!App->bMouseMoved && mousePos.Dist(App->startMousePos) > 2.0f)
|
|
{
|
|
SetCapture(hwnd);
|
|
App->bMouseMoved = true;
|
|
}
|
|
|
|
if(App->bMouseMoved)
|
|
{
|
|
bool bControlDown = HIBYTE(GetKeyState(VK_LCONTROL)) != 0 || HIBYTE(GetKeyState(VK_RCONTROL)) != 0;
|
|
bool bKeepAspect = HIBYTE(GetKeyState(VK_LSHIFT)) == 0 && HIBYTE(GetKeyState(VK_RSHIFT)) == 0;
|
|
bool bCropSymmetric = bKeepAspect;
|
|
bool isCropping = HIBYTE(GetKeyState(VK_MENU)) != 0;
|
|
|
|
if (isCropping) {
|
|
switch(App->modifyType) {
|
|
case ItemModifyType_ScaleTop:
|
|
App->modifyType = ItemModifyType_CropTop;
|
|
break;
|
|
case ItemModifyType_ScaleLeft:
|
|
App->modifyType = ItemModifyType_CropLeft;
|
|
break;
|
|
case ItemModifyType_ScaleRight:
|
|
App->modifyType = ItemModifyType_CropRight;
|
|
break;
|
|
case ItemModifyType_ScaleBottom:
|
|
App->modifyType = ItemModifyType_CropBottom;
|
|
break;
|
|
case ItemModifyType_ScaleTopLeft:
|
|
App->modifyType = ItemModifyType_CropTopLeft;
|
|
break;
|
|
case ItemModifyType_ScaleTopRight:
|
|
App->modifyType = ItemModifyType_CropTopRight;
|
|
break;
|
|
case ItemModifyType_ScaleBottomRight:
|
|
App->modifyType = ItemModifyType_CropBottomRight;
|
|
break;
|
|
case ItemModifyType_ScaleBottomLeft:
|
|
App->modifyType = ItemModifyType_CropBottomLeft;
|
|
break;
|
|
}
|
|
}
|
|
|
|
List<SceneItem*> items;
|
|
App->scene->GetSelectedItems(items);
|
|
|
|
Vect2 totalAdjust = (mousePos-App->startMousePos)*scaleVal;
|
|
Vect2 frameStartMousePos = App->MapWindowToFramePos(App->startMousePos);
|
|
Vect2 minSize = scaleVal*21.0f;
|
|
Vect2 snapSize = scaleVal*10.0f;
|
|
|
|
Vect2 baseScale;
|
|
float baseScaleAspect;
|
|
Vect2 cropFactor;
|
|
|
|
if(scaleItem)
|
|
{
|
|
if(scaleItem->GetSource())
|
|
{
|
|
baseScale = scaleItem->GetSource()->GetSize();
|
|
}
|
|
else
|
|
{
|
|
bKeepAspect = false; //if the source is missing (due to invalid setting or missing plugin), don't allow aspect lock
|
|
baseScale = scaleItem->size;
|
|
}
|
|
baseScaleAspect = baseScale.x/baseScale.y;
|
|
cropFactor = baseScale / scaleItem->GetSize();
|
|
}
|
|
|
|
|
|
int edgeAll = edgeLeft | edgeRight | edgeBottom | edgeTop;
|
|
//more clusterf*** action, straight to your doorstep. does moving, scaling, snapping, as well as keeping aspect ratio with shift down
|
|
switch(App->modifyType)
|
|
{
|
|
case ItemModifyType_Move:
|
|
{
|
|
bool bFoundSnap = false;
|
|
|
|
for(UINT i=0; i<items.Num(); i++)
|
|
{
|
|
SceneItem *item = items[i];
|
|
item->pos = item->startPos+totalAdjust;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
Vect2 pos = item->pos;
|
|
Vect2 bottomRight = pos+item->size;
|
|
pos += item->GetCropTL();
|
|
bottomRight += item->GetCropBR();
|
|
|
|
bool bVerticalSnap = true;
|
|
if(CloseFloat(pos.x, 0.0f, snapSize.x))
|
|
item->pos.x = -item->GetCrop().x;
|
|
else if(CloseFloat(bottomRight.x, baseRenderSize.x, snapSize.x))
|
|
item->pos.x = baseRenderSize.x-item->size.x+item->GetCrop().w;
|
|
else
|
|
bVerticalSnap = false;
|
|
|
|
bool bHorizontalSnap = true;
|
|
if(CloseFloat(pos.y, 0.0f, snapSize.y))
|
|
item->pos.y = -item->GetCrop().y;
|
|
else if(CloseFloat(bottomRight.y, baseRenderSize.y, snapSize.y))
|
|
item->pos.y = baseRenderSize.y-item->size.y+item->GetCrop().z;
|
|
else
|
|
bHorizontalSnap = true;
|
|
|
|
if(bVerticalSnap || bHorizontalSnap)
|
|
{
|
|
bFoundSnap = true;
|
|
totalAdjust = item->pos-item->startPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bFoundSnap)
|
|
{
|
|
for(UINT i=0; i<items.Num(); i++)
|
|
{
|
|
SceneItem *item = items[i];
|
|
item->pos = item->startPos+totalAdjust;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ItemModifyType_CropTop:
|
|
if (!scaleItem)
|
|
break;
|
|
scaleItem->crop.y = ((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y) * cropFactor.y;
|
|
if (!bCropSymmetric)
|
|
scaleItem->crop.z = ((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y) * cropFactor.y;
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeTop | (!bCropSymmetric ? edgeBottom : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropBottom:
|
|
if (!scaleItem)
|
|
break;
|
|
scaleItem->crop.z = ((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y) * cropFactor.y;
|
|
if (!bCropSymmetric)
|
|
scaleItem->crop.y = ((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y) * cropFactor.y;
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeBottom | (!bCropSymmetric ? edgeTop : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropLeft:
|
|
if (!scaleItem)
|
|
break;
|
|
scaleItem->crop.x = ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x) * cropFactor.x;
|
|
if (!bCropSymmetric)
|
|
scaleItem->crop.w = ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x) * cropFactor.x;
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeLeft | (!bCropSymmetric ? edgeRight : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropRight:
|
|
if (!scaleItem)
|
|
break;
|
|
scaleItem->crop.w = ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x) * cropFactor.x;
|
|
if (!bCropSymmetric)
|
|
scaleItem->crop.x = ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x) * cropFactor.x;
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeRight | (!bCropSymmetric ? edgeLeft : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropBottomLeft:
|
|
if (!scaleItem)
|
|
break;
|
|
if (bCropSymmetric)
|
|
{
|
|
scaleItem->crop.z = ((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y) * cropFactor.y;
|
|
scaleItem->crop.x = ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x) * cropFactor.x;
|
|
}
|
|
else
|
|
{
|
|
float amount = MIN(((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y), ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x));
|
|
scaleItem->crop.w = amount * cropFactor.x;
|
|
scaleItem->crop.x = amount * cropFactor.x;
|
|
scaleItem->crop.y = amount * cropFactor.y;
|
|
scaleItem->crop.z = amount * cropFactor.y;
|
|
}
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeLeft | edgeBottom | (!bCropSymmetric ? edgeAll : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropBottomRight:
|
|
if (!scaleItem)
|
|
break;
|
|
if (bCropSymmetric)
|
|
{
|
|
scaleItem->crop.z = ((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y) * cropFactor.y;
|
|
scaleItem->crop.w = ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x) * cropFactor.x;
|
|
}
|
|
else
|
|
{
|
|
float amount = MIN(((scaleItem->pos.y + scaleItem->size.y - frameStartMousePos.y) - totalAdjust.y), ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x));
|
|
scaleItem->crop.w = amount * cropFactor.x;
|
|
scaleItem->crop.x = amount * cropFactor.x;
|
|
scaleItem->crop.y = amount * cropFactor.y;
|
|
scaleItem->crop.z = amount * cropFactor.y;
|
|
}
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeRight | edgeBottom | (!bCropSymmetric ? edgeAll : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropTopLeft:
|
|
if (!scaleItem)
|
|
break;
|
|
if (bCropSymmetric)
|
|
{
|
|
scaleItem->crop.y = ((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y) * cropFactor.y;
|
|
scaleItem->crop.x = ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x) * cropFactor.x;
|
|
}
|
|
else
|
|
{
|
|
float amount = MIN(((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y), ((frameStartMousePos.x - scaleItem->pos.x) + totalAdjust.x));
|
|
scaleItem->crop.w = amount * cropFactor.x;
|
|
scaleItem->crop.x = amount * cropFactor.x;
|
|
scaleItem->crop.y = amount * cropFactor.y;
|
|
scaleItem->crop.z = amount * cropFactor.y;
|
|
}
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeLeft | edgeTop | (!bCropSymmetric ? edgeAll : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_CropTopRight:
|
|
if (!scaleItem)
|
|
break;
|
|
if (bCropSymmetric)
|
|
{
|
|
scaleItem->crop.y = ((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y) * cropFactor.y;
|
|
scaleItem->crop.w = ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x) * cropFactor.x;
|
|
}
|
|
else
|
|
{
|
|
float amount = MIN(((frameStartMousePos.y - scaleItem->pos.y) + totalAdjust.y), ((scaleItem->pos.x + scaleItem->size.x - frameStartMousePos.x) - totalAdjust.x));
|
|
scaleItem->crop.w = amount * cropFactor.x;
|
|
scaleItem->crop.x = amount * cropFactor.x;
|
|
scaleItem->crop.y = amount * cropFactor.y;
|
|
scaleItem->crop.z = amount * cropFactor.y;
|
|
}
|
|
EnsureCropValid(scaleItem, minSize, snapSize, !bControlDown, edgeRight | edgeTop | (!bCropSymmetric ? edgeAll : 0), !bCropSymmetric);
|
|
break;
|
|
|
|
case ItemModifyType_ScaleBottom:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->GetCropTL();
|
|
|
|
scaleItem->size.y = scaleItem->startSize.y+totalAdjust.y;
|
|
if(scaleItem->size.y < minSize.y)
|
|
scaleItem->size.y = minSize.y;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
float bottom = scaleItem->pos.y+scaleItem->size.y-scaleItem->GetCrop().z;
|
|
|
|
if(CloseFloat(bottom, baseRenderSize.y, snapSize.y))
|
|
{
|
|
bottom = baseRenderSize.y;
|
|
scaleItem->size.y = bottom-scaleItem->pos.y+scaleItem->GetCrop().z;
|
|
}
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else
|
|
scaleItem->size.x = scaleItem->startSize.x;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropTL();
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleTop:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->size + scaleItem->GetCropBR();
|
|
|
|
scaleItem->size.y = scaleItem->startSize.y-totalAdjust.y;
|
|
if(scaleItem->size.y < minSize.y)
|
|
scaleItem->size.y = minSize.y;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
float top = scaleItem->startPos.y+(scaleItem->startSize.y-scaleItem->size.y)+scaleItem->GetCrop().x;
|
|
if(CloseFloat(top, 0.0f, snapSize.y))
|
|
scaleItem->size.y = scaleItem->startPos.y+scaleItem->startSize.y+scaleItem->GetCrop().x;
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else
|
|
scaleItem->size.x = scaleItem->startSize.x;
|
|
|
|
totalAdjust.y = scaleItem->startSize.y-scaleItem->size.y;
|
|
scaleItem->pos.y = scaleItem->startPos.y+totalAdjust.y;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropBR() - scaleItem->size;
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleRight:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->GetCropTL();
|
|
|
|
scaleItem->size.x = scaleItem->startSize.x+totalAdjust.x;
|
|
if(scaleItem->size.x < minSize.x)
|
|
scaleItem->size.x = minSize.x;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
float right = scaleItem->pos.x + scaleItem->size.x-scaleItem->GetCrop().y;
|
|
|
|
if(CloseFloat(right, baseRenderSize.x, snapSize.x))
|
|
{
|
|
right = baseRenderSize.x;
|
|
scaleItem->size.x = right-scaleItem->pos.x+scaleItem->GetCrop().y;
|
|
}
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
else
|
|
scaleItem->size.y = scaleItem->startSize.y;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropTL();
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleLeft:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->size + scaleItem->GetCropBR();
|
|
|
|
scaleItem->size.x = scaleItem->startSize.x-totalAdjust.x;
|
|
if(scaleItem->size.x < minSize.x)
|
|
scaleItem->size.x = minSize.x;
|
|
|
|
if(!bControlDown)
|
|
{
|
|
float left = scaleItem->startPos.x+(scaleItem->startSize.x-scaleItem->size.x)+scaleItem->GetCrop().w;
|
|
|
|
if(CloseFloat(left, 0.0f, snapSize.x))
|
|
scaleItem->size.x = scaleItem->startPos.x+scaleItem->startSize.x+scaleItem->GetCrop().w;
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
else
|
|
scaleItem->size.y = scaleItem->startSize.y;
|
|
|
|
totalAdjust.x = scaleItem->startSize.x-scaleItem->size.x;
|
|
scaleItem->pos.x = scaleItem->startPos.x+totalAdjust.x;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropBR() - scaleItem->size;
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleBottomRight:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->GetCropTL();
|
|
|
|
scaleItem->size = scaleItem->startSize+totalAdjust;
|
|
scaleItem->size.ClampMin(minSize);
|
|
|
|
if(!bControlDown)
|
|
{
|
|
Vect2 cropPart = Vect2(scaleItem->GetCrop().y, scaleItem->GetCrop().z);
|
|
Vect2 lowerRight = scaleItem->pos+scaleItem->size-cropPart;
|
|
|
|
|
|
if(CloseFloat(lowerRight.x, baseRenderSize.x, snapSize.x))
|
|
{
|
|
lowerRight.x = baseRenderSize.x;
|
|
scaleItem->size.x = lowerRight.x-scaleItem->pos.x+cropPart.x;
|
|
}
|
|
if(CloseFloat(lowerRight.y, baseRenderSize.y, snapSize.y))
|
|
{
|
|
lowerRight.y = baseRenderSize.y;
|
|
scaleItem->size.y = lowerRight.y-scaleItem->pos.y+cropPart.y;
|
|
}
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
{
|
|
float scaleAspect = scaleItem->size.x/scaleItem->size.y;
|
|
if(scaleAspect < baseScaleAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else if(scaleAspect > baseScaleAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
}
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropTL();
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleTopLeft:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + scaleItem->size + scaleItem->GetCropBR();
|
|
|
|
scaleItem->size = scaleItem->startSize-totalAdjust;
|
|
scaleItem->size.ClampMin(minSize);
|
|
|
|
if(!bControlDown)
|
|
{
|
|
Vect2 cropPart = Vect2(scaleItem->crop.w, scaleItem->crop.x);
|
|
Vect2 topLeft = scaleItem->startPos+(scaleItem->startSize-scaleItem->size)+cropPart;
|
|
|
|
if(CloseFloat(topLeft.x, 0.0f, snapSize.x))
|
|
scaleItem->size.x = scaleItem->startPos.x+scaleItem->startSize.x+cropPart.x;
|
|
if(CloseFloat(topLeft.y, 0.0f, snapSize.y))
|
|
scaleItem->size.y = scaleItem->startPos.y+scaleItem->startSize.y+cropPart.y;
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
{
|
|
float scaleAspect = scaleItem->size.x/scaleItem->size.y;
|
|
if(scaleAspect < baseScaleAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else if(scaleAspect > baseScaleAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
}
|
|
|
|
totalAdjust = scaleItem->startSize-scaleItem->size;
|
|
scaleItem->pos = scaleItem->startPos+totalAdjust;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropBR() - scaleItem->size;
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleBottomLeft:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
Vect2 pos = scaleItem->pos + Vect2(scaleItem->size.x, 0) + scaleItem->GetCropTR();
|
|
|
|
scaleItem->size.x = scaleItem->startSize.x-totalAdjust.x;
|
|
scaleItem->size.y = scaleItem->startSize.y+totalAdjust.y;
|
|
scaleItem->size.ClampMin(minSize);
|
|
|
|
if(!bControlDown)
|
|
{
|
|
Vect2 cropPart = Vect2(scaleItem->GetCrop().w, scaleItem->GetCrop().z);
|
|
float left = scaleItem->startPos.x+(scaleItem->startSize.x-scaleItem->size.x)+cropPart.x;
|
|
float bottom = scaleItem->pos.y+scaleItem->size.y-cropPart.y;
|
|
|
|
if(CloseFloat(left, 0.0f, snapSize.x))
|
|
scaleItem->size.x = scaleItem->startPos.x+scaleItem->startSize.x+cropPart.x;
|
|
|
|
if(CloseFloat(bottom, baseRenderSize.y, snapSize.y))
|
|
{
|
|
bottom = baseRenderSize.y;
|
|
scaleItem->size.y = bottom-scaleItem->pos.y+cropPart.y;
|
|
}
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
{
|
|
float scaleAspect = scaleItem->size.x/scaleItem->size.y;
|
|
if(scaleAspect < baseScaleAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else if(scaleAspect > baseScaleAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
}
|
|
|
|
totalAdjust.x = scaleItem->startSize.x-scaleItem->size.x;
|
|
scaleItem->pos.x = scaleItem->startPos.x+totalAdjust.x;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropTR() - Vect2(scaleItem->size.x, 0);
|
|
}
|
|
break;
|
|
|
|
case ItemModifyType_ScaleTopRight:
|
|
{
|
|
if (!scaleItem)
|
|
break;
|
|
|
|
Vect2 pos = scaleItem->pos + Vect2(0, scaleItem->size.y) + scaleItem->GetCropBL();
|
|
|
|
scaleItem->size.x = scaleItem->startSize.x+totalAdjust.x;
|
|
scaleItem->size.y = scaleItem->startSize.y-totalAdjust.y;
|
|
scaleItem->size.ClampMin(minSize);
|
|
|
|
if(!bControlDown)
|
|
{
|
|
Vect2 cropPart = Vect2(scaleItem->GetCrop().y, scaleItem->GetCrop().x);
|
|
float right = scaleItem->pos.x+scaleItem->size.x-cropPart.x;
|
|
float top = scaleItem->startPos.y+(scaleItem->startSize.y-scaleItem->size.y)+cropPart.y;
|
|
|
|
if(CloseFloat(right, baseRenderSize.x, snapSize.x))
|
|
{
|
|
right = baseRenderSize.x;
|
|
scaleItem->size.x = right-scaleItem->pos.x+cropPart.x;
|
|
}
|
|
|
|
if(CloseFloat(top, 0.0f, snapSize.y))
|
|
scaleItem->size.y = scaleItem->startPos.y+scaleItem->startSize.y+cropPart.y;
|
|
}
|
|
|
|
if(bKeepAspect)
|
|
{
|
|
float scaleAspect = scaleItem->size.x/scaleItem->size.y;
|
|
if(scaleAspect < baseScaleAspect)
|
|
scaleItem->size.x = scaleItem->size.y*baseScaleAspect;
|
|
else if(scaleAspect > baseScaleAspect)
|
|
scaleItem->size.y = scaleItem->size.x/baseScaleAspect;
|
|
}
|
|
|
|
totalAdjust.y = scaleItem->startSize.y-scaleItem->size.y;
|
|
scaleItem->pos.y = scaleItem->startPos.y+totalAdjust.y;
|
|
|
|
EnsureCropValid(scaleItem, minSize, snapSize, false, edgeAll, false);
|
|
|
|
scaleItem->pos = pos - scaleItem->GetCropBL() - Vect2(0, scaleItem->size.y);
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
App->lastMousePos = mousePos;
|
|
}
|
|
}
|
|
|
|
switch(App->modifyType)
|
|
{
|
|
case ItemModifyType_ScaleBottomLeft:
|
|
case ItemModifyType_ScaleTopRight:
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENESW));
|
|
return 0;
|
|
|
|
case ItemModifyType_ScaleBottomRight:
|
|
case ItemModifyType_ScaleTopLeft:
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENWSE));
|
|
return 0;
|
|
|
|
case ItemModifyType_CropLeft:
|
|
case ItemModifyType_CropRight:
|
|
case ItemModifyType_ScaleLeft:
|
|
case ItemModifyType_ScaleRight:
|
|
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
|
return 0;
|
|
|
|
case ItemModifyType_CropTop:
|
|
case ItemModifyType_CropBottom:
|
|
case ItemModifyType_ScaleTop:
|
|
case ItemModifyType_ScaleBottom:
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENS));
|
|
return 0;
|
|
|
|
default:
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if(message == WM_LBUTTONUP || message == WM_RBUTTONUP)
|
|
{
|
|
if((App->bEditMode || message == WM_RBUTTONUP) && App->scene)
|
|
{
|
|
POINTS pos;
|
|
pos.x = (short)LOWORD(lParam);
|
|
pos.y = (short)HIWORD(lParam);
|
|
|
|
if(App->bMouseDown || App->bRMouseDown)
|
|
{
|
|
Vect2 mousePos = Vect2(float(pos.x), float(pos.y));
|
|
|
|
bool bControlDown = HIBYTE(GetKeyState(VK_CONTROL)) != 0;
|
|
|
|
List<SceneItem*> items;
|
|
|
|
if(!App->bMouseMoved)
|
|
{
|
|
Vect2 framePos = MapWindowToFramePos(mousePos);
|
|
App->scene->GetItemsOnPoint(framePos, items);
|
|
|
|
if(bControlDown && App->bItemWasSelected && items.Num())
|
|
{
|
|
SceneItem *lastItem = items.Last();
|
|
lastItem->Select(false);
|
|
ListView_SetItemState(hwndSources, lastItem->GetID(), 0, LVIS_SELECTED);
|
|
}
|
|
else
|
|
{
|
|
if(items.Num())
|
|
{
|
|
SceneItem *topItem = items.Last();
|
|
|
|
if(!bControlDown)
|
|
{
|
|
App->bChangingSources = true;
|
|
ListView_SetItemState(hwndSources, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
App->bChangingSources = false;
|
|
|
|
App->scene->DeselectAll();
|
|
}
|
|
|
|
topItem->Select(true);
|
|
|
|
App->bChangingSources = true;
|
|
SetFocus(hwnd);
|
|
ListView_SetItemState(hwndSources, topItem->GetID(), LVIS_SELECTED, LVIS_SELECTED);
|
|
App->bChangingSources = false;
|
|
|
|
if(App->modifyType == ItemModifyType_None)
|
|
App->modifyType = ItemModifyType_Move;
|
|
}
|
|
else if(!bControlDown) //clicked on empty space without control
|
|
{
|
|
App->bChangingSources = true;
|
|
ListView_SetItemState(hwndSources, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
|
App->bChangingSources = false;
|
|
App->scene->DeselectAll();
|
|
}
|
|
}
|
|
}
|
|
else if(message == WM_LBUTTONUP)
|
|
{
|
|
App->scene->GetSelectedItems(items);
|
|
|
|
ReleaseCapture();
|
|
App->bMouseMoved = false;
|
|
|
|
for(UINT i=0; i<items.Num(); i++)
|
|
{
|
|
SceneItem *item = items[i];
|
|
(item->pos += 0.5f).Floor();
|
|
(item->size += 0.5f).Floor();
|
|
|
|
XElement *itemElement = item->GetElement();
|
|
itemElement->SetInt(TEXT("x"), int(item->pos.x));
|
|
itemElement->SetInt(TEXT("y"), int(item->pos.y));
|
|
itemElement->SetInt(TEXT("cx"), int(item->size.x));
|
|
itemElement->SetInt(TEXT("cy"), int(item->size.y));
|
|
itemElement->SetFloat(TEXT("crop.left"), item->crop.x);
|
|
itemElement->SetFloat(TEXT("crop.top"), item->crop.y);
|
|
itemElement->SetFloat(TEXT("crop.right"), item->crop.w);
|
|
itemElement->SetFloat(TEXT("crop.bottom"), item->crop.z);
|
|
}
|
|
|
|
App->modifyType = ItemModifyType_None;
|
|
}
|
|
|
|
App->bMouseDown = false;
|
|
App->bRMouseDown = false;
|
|
}
|
|
}
|
|
if(message == WM_RBUTTONUP)
|
|
{
|
|
HMENU hPopup = CreatePopupMenu();
|
|
|
|
//---------------------------------------------------
|
|
|
|
if (App->bRunning) {
|
|
HMENU hProjector = CreatePopupMenu();
|
|
AppendMenu(hProjector, MF_STRING | (App->bProjector ? 0 : MF_CHECKED),
|
|
ID_PROJECTOR, Str("Disable"));
|
|
AppendMenu(hProjector, MF_SEPARATOR, 0, 0);
|
|
for (UINT i = 0; i < App->NumMonitors(); i++) {
|
|
String strMonitor = Str("MonitorNum");
|
|
strMonitor.FindReplace(L"$1", UIntString(i+1));
|
|
|
|
const MonitorInfo &mi = App->GetMonitor(i);
|
|
|
|
bool grayed = mi.hMonitor == MonitorFromWindow(hwndMain, MONITOR_DEFAULTTONULL);
|
|
|
|
bool enabled = App->bProjector && App->projectorMonitorID == i;
|
|
|
|
AppendMenu(hProjector, MF_STRING | (enabled ? MF_CHECKED : 0) | (grayed ? MF_GRAYED : 0),
|
|
ID_PROJECTOR+i+1, strMonitor);
|
|
}
|
|
|
|
AppendMenu(hPopup, MF_STRING|MF_POPUP, (UINT_PTR)hProjector, Str("MainMenu.Settings.Projector"));
|
|
AppendMenu(hPopup, MF_SEPARATOR, 0, 0);
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
|
|
AppendMenu(hPopup, MF_STRING | (App->bFullscreenMode ? MF_CHECKED : 0), ID_TOGGLEFULLSCREEN, Str("MainMenu.Settings.FullscreenMode"));
|
|
|
|
HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES);
|
|
int numItems = ListView_GetItemCount(hwndSources);
|
|
bool bSelected = ListView_GetSelectedCount(hwndSources) != 0;
|
|
HMENU hMenuPreview;
|
|
|
|
if (App->IsRunning() && bSelected)
|
|
hMenuPreview = CreatePopupMenu();
|
|
else
|
|
{
|
|
hMenuPreview = hPopup;
|
|
AppendMenu(hMenuPreview, MF_SEPARATOR, 0, 0);
|
|
}
|
|
|
|
AppendMenu(hMenuPreview, MF_STRING | (!App->renderFrameIn1To1Mode ? MF_CHECKED : 0), ID_PREVIEWSCALETOFITMODE, Str("RenderView.ViewModeScaleToFit"));
|
|
AppendMenu(hMenuPreview, MF_STRING | (App->renderFrameIn1To1Mode ? MF_CHECKED : 0), ID_PREVIEW1TO1MODE, Str("RenderView.ViewMode1To1"));
|
|
AppendMenu(hMenuPreview, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenuPreview, MF_STRING | (App->bRenderViewEnabled ? MF_CHECKED : 0), ID_TOGGLERENDERVIEW, Str("RenderView.EnableView"));
|
|
AppendMenu(hMenuPreview, MF_STRING | (App->bPanelVisible ? MF_CHECKED : 0), ID_TOGGLEPANEL, Str("RenderView.DisplayPanel"));
|
|
|
|
if (App->IsRunning())
|
|
{
|
|
if (bSelected)
|
|
AppendMenu(hPopup, MF_STRING|MF_POPUP, (UINT_PTR)hMenuPreview, Str("Preview"));
|
|
App->AppendModifyListbox(hwndSources, hPopup, ID_SOURCES, numItems, bSelected);
|
|
}
|
|
|
|
POINT p;
|
|
GetCursorPos(&p);
|
|
|
|
int ret = (int)TrackPopupMenuEx(hPopup, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, hwndMain, NULL);
|
|
App->TrackModifyListbox(hwnd, ret);
|
|
|
|
DestroyMenu(hPopup);
|
|
}
|
|
}
|
|
else if(message == WM_GETDLGCODE && App->bEditMode)
|
|
{
|
|
return DLGC_WANTARROWS;
|
|
}
|
|
else if(message == WM_KEYDOWN && App->bRunning && App->bEditMode)
|
|
{
|
|
int dx, dy ;
|
|
dx = dy = 0;
|
|
switch(wParam)
|
|
{
|
|
case VK_UP:
|
|
dy = -1;
|
|
break;
|
|
case VK_DOWN:
|
|
dy = 1;
|
|
break;
|
|
case VK_RIGHT:
|
|
dx = 1;
|
|
break;
|
|
case VK_LEFT:
|
|
dx = -1;
|
|
break;
|
|
default:
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
App->MoveItemsByPixels(dx, dy);
|
|
return 0;
|
|
}
|
|
else if(message == WM_KEYUP && App->bEditMode)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CALLBACK OBS::LogWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_SIZE:
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
|
|
MoveWindow(hwndLog, client.left, client.top, client.right, client.bottom, true);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case ID_LOG_WINDOW:
|
|
OBSUpdateLog();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
typedef CTSTR (*GETPLUGINNAMEPROC)();
|
|
typedef CTSTR (*GETPLUGINDESCRIPTIONPROC)();
|
|
typedef void (*CONFIGUREPLUGINPROC)(HWND);
|
|
|
|
|
|
|
|
INT_PTR CALLBACK OBS::PluginsDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LocalizeWindow(hwnd);
|
|
|
|
HWND hwndPlugins = GetDlgItem(hwnd, IDC_PLUGINS);
|
|
|
|
for(UINT i=0; i<App->plugins.Num(); i++)
|
|
{
|
|
PluginInfo &pluginInfo = App->plugins[i];
|
|
GETPLUGINNAMEPROC getName = (GETPLUGINNAMEPROC)GetProcAddress(pluginInfo.hModule, "GetPluginName");
|
|
|
|
CTSTR lpName;
|
|
if(getName)
|
|
lpName = getName();
|
|
else
|
|
lpName = pluginInfo.strFile;
|
|
|
|
UINT id = (UINT)SendMessage(hwndPlugins, LB_ADDSTRING, 0, (LPARAM)lpName);
|
|
SendMessage(hwndPlugins, LB_SETITEMDATA, id, (LPARAM)i);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_PLUGINS:
|
|
if(HIWORD(wParam) == LBN_SELCHANGE)
|
|
{
|
|
UINT id = (UINT)SendMessage((HWND)lParam, LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
UINT pluginID = (UINT)SendMessage((HWND)lParam, LB_GETITEMDATA, id, 0);
|
|
PluginInfo &pluginInfo = App->plugins[pluginID];
|
|
|
|
//-------------------------------------
|
|
|
|
GETPLUGINDESCRIPTIONPROC getDescription = (GETPLUGINDESCRIPTIONPROC)GetProcAddress(pluginInfo.hModule, "GetPluginDescription");
|
|
|
|
CTSTR lpDescription = NULL;
|
|
if(getDescription)
|
|
lpDescription = getDescription();
|
|
|
|
String strText;
|
|
strText << Str("Plugins.Filename") << TEXT(" ") << pluginInfo.strFile;
|
|
|
|
if(lpDescription)
|
|
strText << TEXT("\r\n\r\n") << Str("Plugins.Description") << TEXT("\r\n") << lpDescription;
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_DESCRIPTION), strText.Array());
|
|
|
|
//-------------------------------------
|
|
|
|
CONFIGUREPLUGINPROC configPlugin = (CONFIGUREPLUGINPROC)GetProcAddress(pluginInfo.hModule, "ConfigPlugin");
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIG), configPlugin != NULL ? TRUE : FALSE);
|
|
}
|
|
break;
|
|
|
|
case IDC_CONFIG:
|
|
if(HIWORD(wParam) == BN_CLICKED)
|
|
{
|
|
HWND hwndPlugins = GetDlgItem(hwnd, IDC_PLUGINS);
|
|
|
|
UINT id = (UINT)SendMessage(hwndPlugins, LB_GETCURSEL, 0, 0);
|
|
if(id == LB_ERR)
|
|
break;
|
|
|
|
UINT pluginID = (UINT)SendMessage(hwndPlugins, LB_GETITEMDATA, id, 0);
|
|
PluginInfo &pluginInfo = App->plugins[pluginID];
|
|
|
|
//-------------------------------------
|
|
|
|
CONFIGUREPLUGINPROC configPlugin = (CONFIGUREPLUGINPROC)GetProcAddress(pluginInfo.hModule, "ConfigPlugin");
|
|
configPlugin(hwnd);
|
|
}
|
|
break;
|
|
|
|
case IDOK:
|
|
EndDialog(hwnd, IDOK);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
EndDialog(hwnd, IDOK);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int SetSliderText(HWND hwndParent, int controlSlider, int controlText)
|
|
{
|
|
HWND hwndSlider = GetDlgItem(hwndParent, controlSlider);
|
|
HWND hwndText = GetDlgItem(hwndParent, controlText);
|
|
|
|
int sliderVal = (int)SendMessage(hwndSlider, TBM_GETPOS, 0, 0);
|
|
float floatVal = float(sliderVal)*0.01f;
|
|
|
|
SetWindowText(hwndText, FormattedString(TEXT("%.02f"), floatVal));
|
|
|
|
return sliderVal;
|
|
}
|
|
|