obs/Source/WindowStuff.cpp
jp9000 70cb064d32 Implement scene collections in menu only
This hopefully resolves some conflicting opinions about how scene
collections should be handled, and removes it from the general settings
panel, where it felt a bit cluttered.

Actually, I would kind of like it if profiles were handled this way too.
This feels a lot cleaner than how profiles are handled in the general
settings.  The profiles thing in the general settings definitely feels
clunky.

In the main menu, the scene collections menu now has the following menu
items:

- New
- Duplicate
- Rename
- Remove
- Import
- Export

Duplicate was something I personally added - I felt like it might be a
nice thing to have for the sake of convenience.  Import/Export was
requested by dodgepong, and is also definitely nice to have.  Due to how
easy new scene collections are to set up it didn't take very long to put
in.
2014-07-30 19:28:27 -07:00

5141 lines
199 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,
};
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::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)
{
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);
}
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 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)
{
AppendMenu(hMenu, MF_STRING, ID_LISTBOX_COPY, strCopy);
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));
}
}
}
extern "C" double round(double val);
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)
{
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) element->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();
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_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:
EndDialog(hwnd, IDOK);
}
break;
case WM_CLOSE:
EndDialog(hwnd, IDOK);
}
return FALSE;
}
//----------------------------
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.Save();
String strCurSceneCollectionPath;
strCurSceneCollectionPath = FormattedString(L"%s/%s.xconfig", lpAppDataPath, strCurSceneCollection.Array());
if (!OSFileExists(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();
}
}
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();
}
}
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();
}
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));
String strCurSceneCollection = GlobalConfig->GetString(TEXT("General"), TEXT("SceneCollection"));
String strCurSceneCollectionFile;
strCurSceneCollectionFile << lpAppDataPath << TEXT("\\sceneCollection\\") << strCurSceneCollection << L".xconfig";
scenesConfig.Save();
CopyFile(strCurSceneCollectionFile, 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, 0, MF_BYPOSITION));
AddProfilesToMenu(hmenuProfiles);
}
//----------------------------
void OBS::DisableMenusWhileStreaming(bool disable)
{
HMENU hmenuMain = GetMenu(hwndMain);
HMENU hmenuHelp = GetSubMenu(hmenuMain, 3);
EnableMenuItem(hmenuMain, 2, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
EnableMenuItem(hmenuMain, 3, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
EnableMenuItem(GetSubMenu(hmenuHelp, 3), 0, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
EnableMenuItem(GetSubMenu(hmenuHelp, 3), 1, (!disable ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION);
DrawMenuBar(hwndMain);
}
//----------------------------
void LogUploadMonitorCallback()
{
PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_REFRESH_LOGS, 0), 0);
}
//----------------------------
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 hmenuHelp = GetSubMenu(hmenuMain, 3);
HMENU hmenuUpload = GetSubMenu(hmenuHelp, 3);
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);
HMENU hmenuHelp = GetSubMenu(hmenuMain, 3);
HMENU hmenuLog = GetSubMenu(hmenuHelp, 3);
MENUITEMINFO mii;
zero(&mii, sizeof mii);
mii.cbSize = sizeof mii;
mii.fMask = MIIM_STRING;
GetMenuItemInfo(hmenuLog, 6 + item, true, &mii);
String log;
log.SetLength(mii.cch++);
mii.dwTypeData = log.Array();
GetMenuItemInfo(hmenuLog, 6 + item, true, &mii);
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.Save();
break;
case ID_TOGGLERECORDING:
App->RefreshStreamButtons(true);
App->ToggleRecording();
App->RefreshStreamButtons();
break;
case ID_FILE_EXIT:
case ID_EXIT:
PostQuitMessage(0);
break;
case ID_RECORDINGSFOLDER:
{
String path = OSGetDefaultVideoSavePath();
path = GetPathDirectory(GetPathWithoutExtension(AppConfig->GetString(TEXT("Publish"), TEXT("SavePath"), path.IsValid() ? path.Array() : nullptr))).FindReplace(L"/", L"\\");
String lastFile = App->lastOutputFile.FindReplace(L"/", L"\\");
LPITEMIDLIST item = nullptr;
if (lastFile.IsValid() && path == lastFile.Left(path.Length()) && (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("http://www.obsproject.com"), 0, 0, SW_SHOWNORMAL);
break;
case ID_HELP_CONTENTS:
{
String strHelpPath;
UINT dirSize = GetCurrentDirectory(0, 0);
strHelpPath.SetLength(dirSize);
GetCurrentDirectory(dirSize, strHelpPath.Array());
strHelpPath << TEXT("\\OBSHelp.chm");
ShellExecute(NULL, TEXT("open"), strHelpPath, 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_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()))
{
App->scenesConfig.Save();
CTSTR collection = GetCurrentSceneCollection();
String strSceneCollectionPath;
strSceneCollectionPath = FormattedString(L"%s/%s.xconfig", lpAppDataPath, collection);
if (!OSFileExists(strSceneCollectionPath))
strSceneCollectionPath = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, collection);
if (!App->scenesConfig.Open(strSceneCollectionPath))
{
OBSMessageBox(hwnd, TEXT("Error - unable to open xconfig file"), NULL, 0);
break;
}
GlobalConfig->SetString(TEXT("General"), TEXT("SceneCollection"), strSceneCollection);
App->scenesConfig.Close();
App->ReloadSceneCollection();
ResetSceneCollectionMenu();
ResetApplicationName();
App->UpdateNotificationAreaIcon();
}
}
else if (id >= ID_UPLOAD_LOG && id <= ID_UPLOAD_LOG_END)
{
String log = GetLogUploadMenuItem(id - ID_UPLOAD_LOG);
LogUploadResult result;
ShowLogUploadResult(result, UploadLog(log, result));
}
else if (id >= ID_UPLOAD_ANALYZE_LOG && id <= ID_UPLOAD_ANALYZE_LOG_END)
{
String log = GetLogUploadMenuItem(id - ID_UPLOAD_ANALYZE_LOG);
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 - ID_VIEW_LOG);
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 = 1;
gsVIRect.right = 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;
}
}
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
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_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:
PostQuitMessage(0);
break;
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 bControlDown, 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 (!bControlDown)
{
// 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, bControlDown, 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, bControlDown, 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, bControlDown, 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, bControlDown, 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, bControlDown, 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, bControlDown, 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, bControlDown, 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, bControlDown, 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)
{
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;
}