/*
   rdesktop: A Remote Desktop Protocol client.
   Connection settings dialog
   Copyright (C) Ged Murphy 2007

   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.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "precomp.h"

#include <winreg.h>
#include <commdlg.h>

#define MAX_KEY_NAME 255

HINSTANCE hInst;

static VOID ReLoadGeneralPage(PINFO pInfo);
static VOID ReLoadDisplayPage(PINFO pInfo);

static VOID
DoOpenFile(PINFO pInfo)
{
    OPENFILENAMEW ofn;
    WCHAR szFileName[MAX_PATH] = L"Default.rdp";
    static WCHAR szFilter[] = L"Remote Desktop Files (*.RDP)\0*.rdp\0";

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize   = sizeof(OPENFILENAMEW);
    ofn.hwndOwner     = pInfo->hGeneralPage;
    ofn.nMaxFile      = MAX_PATH;
    ofn.nMaxFileTitle = MAX_PATH;
    ofn.lpstrDefExt   = L"RDP";
    ofn.lpstrFilter   = szFilter;
    ofn.lpstrFile     = szFileName;
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST;

    if (GetOpenFileNameW(&ofn))
    {
        LoadRdpSettingsFromFile(pInfo->pRdpSettings, szFileName);
        ReLoadGeneralPage(pInfo);
        ReLoadDisplayPage(pInfo);
    }
}


static VOID
DoSaveAs(PINFO pInfo)
{
    OPENFILENAMEW ofn;
    WCHAR szFileName[MAX_PATH] = L"Default.rdp";
    static WCHAR szFilter[] = L"Remote Desktop Files (*.RDP)\0*.rdp\0";

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize   = sizeof(OPENFILENAMEW);
    ofn.hwndOwner     = pInfo->hGeneralPage;
    ofn.nMaxFile      = MAX_PATH;
    ofn.nMaxFileTitle = MAX_PATH;
    ofn.lpstrDefExt   = L"RDP";
    ofn.lpstrFilter   = szFilter;
    ofn.lpstrFile     = szFileName;
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;

    if (GetSaveFileNameW(&ofn))
    {
        SaveAllSettings(pInfo);
        SaveRdpSettingsToFile(szFileName, pInfo->pRdpSettings);
    }
}


static VOID
OnTabWndSelChange(PINFO pInfo)
{
    switch (TabCtrl_GetCurSel(pInfo->hTab))
    {
        case 0: //General
            ShowWindow(pInfo->hGeneralPage, SW_SHOW);
            ShowWindow(pInfo->hDisplayPage, SW_HIDE);
            BringWindowToTop(pInfo->hGeneralPage);
            break;
        case 1: //Display
            ShowWindow(pInfo->hGeneralPage, SW_HIDE);
            ShowWindow(pInfo->hDisplayPage, SW_SHOW);
            BringWindowToTop(pInfo->hDisplayPage);
            break;
    }
}


static VOID
LoadUsernameHint(HWND hDlg, INT iCur)
{
    WCHAR szValue[MAXVALUE+1000];
    WCHAR szName[MAX_KEY_NAME];
    WCHAR szKeyName[] = L"Software\\Microsoft\\Terminal Server Client\\Servers";
    PWCHAR lpAddress;
    HKEY hKey;
    HKEY hSubKey;
    LONG lRet = ERROR_SUCCESS;
    INT iIndex = 0;
    DWORD dwSize = MAX_KEY_NAME;

    SendDlgItemMessageW(hDlg, IDC_SERVERCOMBO, CB_GETLBTEXT, (WPARAM)iCur, (LPARAM)szValue);

    /* remove possible port number */
    lpAddress = wcstok(szValue, L":");

    if (lpAddress == NULL)
        return;

    if (RegOpenKeyExW(HKEY_CURRENT_USER,
                      szKeyName,
                      0,
                      KEY_READ,
                      &hKey) == ERROR_SUCCESS)
    {
        while (lRet == ERROR_SUCCESS)
        {
            dwSize = MAX_KEY_NAME;

            lRet = RegEnumKeyExW(hKey, iIndex, szName, &dwSize, NULL, NULL, NULL, NULL);

            if(lRet == ERROR_SUCCESS && wcscmp(szName, lpAddress) == 0)
            {
                if(RegOpenKeyExW(hKey, szName, 0, KEY_READ, &hSubKey) != ERROR_SUCCESS)
                    break;

                dwSize = MAXVALUE * sizeof(WCHAR);

                if(RegQueryValueExW(hKey, L"UsernameHint", 0,  NULL, (LPBYTE)szValue, &dwSize) == ERROR_SUCCESS)
                {
                    SetDlgItemTextW(hDlg, IDC_NAMEEDIT, szValue);
                }

                RegCloseKey(hSubKey);
                break;
            }
            iIndex++;
        }
        RegCloseKey(hKey);
    }
}


static VOID
FillServerAddressCombo(PINFO pInfo)
{
    HKEY hKey;
    WCHAR KeyName[] = L"Software\\Microsoft\\Terminal Server Client\\Default";
    WCHAR Name[MAX_KEY_NAME];
    LONG ret = ERROR_SUCCESS;
    DWORD size;
    INT i = 0;
    BOOL found = FALSE;

    if (RegOpenKeyExW(HKEY_CURRENT_USER,
                      KeyName,
                      0,
                      KEY_READ,
                      &hKey) == ERROR_SUCCESS)
    {
        while (ret == ERROR_SUCCESS)
        {
            size = MAX_KEY_NAME;
            ret = RegEnumValueW(hKey,
                                i,
                                Name,
                                &size,
                                NULL,
                                NULL,
                                NULL,
                                NULL);
            if (ret == ERROR_SUCCESS)
            {
                size = sizeof(Name);
                if (RegQueryValueExW(hKey,
                                     Name,
                                     0,
                                     NULL,
                                     NULL,
                                     &size) == ERROR_SUCCESS)
                {
                    LPWSTR lpAddress = HeapAlloc(GetProcessHeap(),
                                                 0,
                                                 size);
                    if (lpAddress)
                    {
                        if (RegQueryValueExW(hKey,
                                             Name,
                                             0,
                                             NULL,
                                             (LPBYTE)lpAddress,
                                             &size) == ERROR_SUCCESS)
                        {
                            SendDlgItemMessageW(pInfo->hGeneralPage,
                                                IDC_SERVERCOMBO,
                                                CB_ADDSTRING,
                                                0,
                                                (LPARAM)lpAddress);
                            found = TRUE;
                        }

                        HeapFree(GetProcessHeap(),
                                 0,
                                 lpAddress);
                    }
                }
            }

            i++;
        }
        RegCloseKey(hKey);
    }

    if (LoadStringW(hInst,
                    IDS_BROWSESERVER,
                    Name,
                    sizeof(Name) / sizeof(WCHAR)))
    {
        SendDlgItemMessageW(pInfo->hGeneralPage,
                            IDC_SERVERCOMBO,
                            CB_ADDSTRING,
                            0,
                            (LPARAM)Name);
    }

    if(found)
    {
        SendDlgItemMessageW(pInfo->hGeneralPage,
                            IDC_SERVERCOMBO,
                            CB_SETCURSEL,
                            0,
                            0);
        LoadUsernameHint(pInfo->hGeneralPage, 0);
    }

}


static VOID
ReLoadGeneralPage(PINFO pInfo)
{
    LPWSTR lpText;

    /* add file address */
    lpText = GetStringFromSettings(pInfo->pRdpSettings,
                                   L"full address");
    if (lpText)
    {
        SetDlgItemTextW(pInfo->hGeneralPage,
                        IDC_SERVERCOMBO,
                        lpText);
    }

    /* set user name */
    lpText = GetStringFromSettings(pInfo->pRdpSettings,
                                   L"username");
    if (lpText)
    {
        SetDlgItemTextW(pInfo->hGeneralPage,
                        IDC_NAMEEDIT,
                        lpText);
    }
}


static VOID
GeneralOnInit(HWND hwnd,
              PINFO pInfo)
{
    SetWindowLongPtrW(hwnd,
                      GWLP_USERDATA,
                      (LONG_PTR)pInfo);

    pInfo->hGeneralPage = hwnd;

    SetWindowPos(pInfo->hGeneralPage,
                 NULL,
                 2,
                 22,
                 0,
                 0,
                 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);

    pInfo->hLogon = LoadImageW(hInst,
                               MAKEINTRESOURCEW(IDI_LOGON),
                               IMAGE_ICON,
                               32,
                               32,
                               LR_DEFAULTCOLOR);
    if (pInfo->hLogon)
    {
        SendDlgItemMessageW(pInfo->hGeneralPage,
                            IDC_LOGONICON,
                            STM_SETICON,
                            (WPARAM)pInfo->hLogon,
                            0);
    }

    pInfo->hConn = LoadImageW(hInst,
                              MAKEINTRESOURCEW(IDI_CONN),
                              IMAGE_ICON,
                              32,
                              32,
                              LR_DEFAULTCOLOR);
    if (pInfo->hConn)
    {
        SendDlgItemMessageW(pInfo->hGeneralPage,
                            IDC_CONNICON,
                            STM_SETICON,
                            (WPARAM)pInfo->hConn,
                            0);
    }

    FillServerAddressCombo(pInfo);
    ReLoadGeneralPage(pInfo);
}


INT_PTR CALLBACK
GeneralDlgProc(HWND hDlg,
               UINT message,
               WPARAM wParam,
               LPARAM lParam)
{
    PINFO pInfo = (PINFO)GetWindowLongPtrW(hDlg,
                                           GWLP_USERDATA);

    switch (message)
    {
        case WM_INITDIALOG:
            GeneralOnInit(hDlg, (PINFO)lParam);
            return TRUE;

        case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
                case IDC_SERVERCOMBO:
                    if (HIWORD(wParam) == CBN_SELCHANGE)
                    {
                        INT last, cur;

                        cur = SendDlgItemMessageW(hDlg,
                                                  IDC_SERVERCOMBO,
                                                  CB_GETCURSEL,
                                                  0,
                                                  0);

                        last = SendDlgItemMessageW(hDlg,
                                                   IDC_SERVERCOMBO,
                                                   CB_GETCOUNT,
                                                   0,
                                                   0);
                        if ((cur + 1) == last)
                            MessageBoxW(hDlg, L"SMB is not yet supported", L"RDP error", MB_ICONERROR);
                        else
                        {
                            LoadUsernameHint(hDlg, cur);
                        }
                    }
                    break;

                case IDC_SAVE:
                    SaveAllSettings(pInfo);
                    SaveRdpSettingsToFile(NULL, pInfo->pRdpSettings);
                break;

                case IDC_SAVEAS:
                    DoSaveAs(pInfo);
                break;

                case IDC_OPEN:
                    DoOpenFile(pInfo);
                break;
            }

            break;
        }

        case WM_CLOSE:
        {
            if (pInfo->hLogon)
                DestroyIcon(pInfo->hLogon);

            if (pInfo->hConn)
                DestroyIcon(pInfo->hConn);

            break;
        }
    }

    return 0;
}


static PSETTINGS_ENTRY
GetPossibleSettings(IN LPCWSTR lpDeviceName,
                    OUT DWORD* pSettingsCount,
                    OUT PSETTINGS_ENTRY* CurrentSettings)
{
    DEVMODEW devmode;
    DWORD NbSettings = 0;
    DWORD iMode = 0;
    DWORD dwFlags = 0;
    PSETTINGS_ENTRY Settings = NULL;
    HDC hDC;
    PSETTINGS_ENTRY Current;
    DWORD bpp, xres, yres, checkbpp;

    /* Get current settings */
    *CurrentSettings = NULL;
    hDC = CreateICW(NULL, lpDeviceName, NULL, NULL);
    bpp = GetDeviceCaps(hDC, PLANES);
    bpp *= GetDeviceCaps(hDC, BITSPIXEL);
    xres = GetDeviceCaps(hDC, HORZRES);
    yres = GetDeviceCaps(hDC, VERTRES);
    DeleteDC(hDC);

    /* List all settings */
    devmode.dmSize = (WORD)sizeof(DEVMODE);
    devmode.dmDriverExtra = 0;

    if (!EnumDisplaySettingsExW(lpDeviceName, ENUM_CURRENT_SETTINGS, &devmode, dwFlags))
        return NULL;

    while (EnumDisplaySettingsExW(lpDeviceName, iMode, &devmode, dwFlags))
    {
        if (devmode.dmBitsPerPel==8 ||
            devmode.dmBitsPerPel==16 ||
            devmode.dmBitsPerPel==24 ||
            devmode.dmBitsPerPel==32)
        {
            checkbpp=1;
        }
        else
            checkbpp=0;

        if (devmode.dmPelsWidth < 640 ||
            devmode.dmPelsHeight < 480 || checkbpp == 0)
        {
            iMode++;
            continue;
        }

        Current = HeapAlloc(GetProcessHeap(), 0, sizeof(SETTINGS_ENTRY));
        if (Current != NULL)
        {
            /* Sort resolutions by increasing height, and BPP */
            PSETTINGS_ENTRY Previous = NULL;
            PSETTINGS_ENTRY Next = Settings;
            Current->dmPelsWidth = devmode.dmPelsWidth;
            Current->dmPelsHeight = devmode.dmPelsHeight;
            Current->dmBitsPerPel = devmode.dmBitsPerPel;
            while (Next != NULL &&
                   (Next->dmPelsWidth < Current->dmPelsWidth ||
                    (Next->dmPelsWidth == Current->dmPelsWidth && Next->dmPelsHeight < Current->dmPelsHeight) ||
                    (Next->dmPelsHeight == Current->dmPelsHeight &&
                     Next->dmPelsWidth == Current->dmPelsWidth &&
                     Next->dmBitsPerPel < Current->dmBitsPerPel )))
            {
                Previous = Next;
                Next = Next->Flink;
            }
            Current->Blink = Previous;
            Current->Flink = Next;
            if (Previous == NULL)
                Settings = Current;
            else
                Previous->Flink = Current;
            if (Next != NULL)
                Next->Blink = Current;
            if (devmode.dmPelsWidth == xres && devmode.dmPelsHeight == yres && devmode.dmBitsPerPel == bpp)
            {
                *CurrentSettings = Current;
            }
            NbSettings++;
        }
        iMode++;
    }

    *pSettingsCount = NbSettings;
    return Settings;
}


static BOOL
AddDisplayDevice(PINFO pInfo, PDISPLAY_DEVICEW DisplayDevice)
{
    PDISPLAY_DEVICE_ENTRY newEntry = NULL;
    LPWSTR description = NULL;
    LPWSTR name = NULL;
    LPWSTR key = NULL;
    LPWSTR devid = NULL;
    SIZE_T descriptionSize, nameSize, keySize, devidSize;
    PSETTINGS_ENTRY Current;
    DWORD ResolutionsCount = 1;
    DWORD i;

    newEntry = HeapAlloc(GetProcessHeap(),
                         0,
                         sizeof(DISPLAY_DEVICE_ENTRY));
    if (!newEntry) goto ByeBye;
    ZeroMemory(newEntry, sizeof(DISPLAY_DEVICE_ENTRY));

    newEntry->Settings = GetPossibleSettings(DisplayDevice->DeviceName,
                                             &newEntry->SettingsCount,
                                             &newEntry->CurrentSettings);
    if (!newEntry->Settings) goto ByeBye;

    newEntry->InitialSettings.dmPelsWidth = newEntry->CurrentSettings->dmPelsWidth;
    newEntry->InitialSettings.dmPelsHeight = newEntry->CurrentSettings->dmPelsHeight;
    newEntry->InitialSettings.dmBitsPerPel = newEntry->CurrentSettings->dmBitsPerPel;

    /* Count different resolutions */
    for (Current = newEntry->Settings; Current != NULL; Current = Current->Flink)
    {
        if (Current->Flink != NULL &&
            ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) &&
            (Current->dmPelsHeight != Current->Flink->dmPelsHeight)))
        {
            ResolutionsCount++;
        }
    }

    newEntry->Resolutions = HeapAlloc(GetProcessHeap(),
                                      0,
                                      ResolutionsCount * (sizeof(RESOLUTION_INFO) + 1));
    if (!newEntry->Resolutions) goto ByeBye;

    newEntry->ResolutionsCount = ResolutionsCount;

    /* Fill resolutions infos */
    for (Current = newEntry->Settings, i = 0; Current != NULL; Current = Current->Flink)
    {
        if (Current->Flink == NULL ||
            (Current->Flink != NULL &&
            ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) &&
            (Current->dmPelsHeight != Current->Flink->dmPelsHeight))))
        {
            newEntry->Resolutions[i].dmPelsWidth = Current->dmPelsWidth;
            newEntry->Resolutions[i].dmPelsHeight = Current->dmPelsHeight;
            i++;
        }
    }

    /* fullscreen */
    newEntry->Resolutions[i].dmPelsWidth = GetSystemMetrics(SM_CXSCREEN);
    newEntry->Resolutions[i].dmPelsHeight = GetSystemMetrics(SM_CYSCREEN);

    descriptionSize = (wcslen(DisplayDevice->DeviceString) + 1) * sizeof(WCHAR);
    description = HeapAlloc(GetProcessHeap(), 0, descriptionSize);
    if (!description) goto ByeBye;

    nameSize = (wcslen(DisplayDevice->DeviceName) + 1) * sizeof(WCHAR);
    name = HeapAlloc(GetProcessHeap(), 0, nameSize);
    if (!name) goto ByeBye;

    keySize = (wcslen(DisplayDevice->DeviceKey) + 1) * sizeof(WCHAR);
    key = HeapAlloc(GetProcessHeap(), 0, keySize);
    if (!key) goto ByeBye;

    devidSize = (wcslen(DisplayDevice->DeviceID) + 1) * sizeof(WCHAR);
    devid = HeapAlloc(GetProcessHeap(), 0, devidSize);
    if (!devid) goto ByeBye;

    memcpy(description, DisplayDevice->DeviceString, descriptionSize);
    memcpy(name, DisplayDevice->DeviceName, nameSize);
    memcpy(key, DisplayDevice->DeviceKey, keySize);
    memcpy(devid, DisplayDevice->DeviceID, devidSize);
    newEntry->DeviceDescription = description;
    newEntry->DeviceName = name;
    newEntry->DeviceKey = key;
    newEntry->DeviceID = devid;
    newEntry->DeviceStateFlags = DisplayDevice->StateFlags;
    newEntry->Flink = pInfo->DisplayDeviceList;
    pInfo->DisplayDeviceList = newEntry;
    return TRUE;

ByeBye:
    if (newEntry != NULL)
    {
        if (newEntry->Settings != NULL)
        {
            Current = newEntry->Settings;
            while (Current != NULL)
            {
                PSETTINGS_ENTRY Next = Current->Flink;
                HeapFree(GetProcessHeap(), 0, Current);
                Current = Next;
            }
        }
        if (newEntry->Resolutions != NULL)
            HeapFree(GetProcessHeap(), 0, newEntry->Resolutions);
        HeapFree(GetProcessHeap(), 0, newEntry);
    }
    if (description != NULL)
        HeapFree(GetProcessHeap(), 0, description);
    if (name != NULL)
        HeapFree(GetProcessHeap(), 0, name);
    if (key != NULL)
        HeapFree(GetProcessHeap(), 0, key);
    if (devid != NULL)
        HeapFree(GetProcessHeap(), 0, devid);
    return FALSE;
}


static VOID
OnResolutionChanged(PINFO pInfo, INT position)
{
    WCHAR Buffer[64];
    INT MaxSlider;

    MaxSlider = SendDlgItemMessageW(pInfo->hDisplayPage,
                                    IDC_GEOSLIDER,
                                    TBM_GETRANGEMAX,
                                    0,
                                    0);

    if (position == MaxSlider)
    {
        LoadStringW(hInst,
                    IDS_FULLSCREEN,
                    Buffer,
                    sizeof(Buffer) / sizeof(WCHAR));
    }
    else
    {
        WCHAR Pixel[64];

        if (LoadStringW(hInst,
                        IDS_PIXEL,
                        Pixel,
                        sizeof(Pixel) / sizeof(WCHAR)))
        {
            swprintf(Buffer,
                     Pixel,
                     pInfo->DisplayDeviceList->Resolutions[position].dmPelsWidth,
                     pInfo->DisplayDeviceList->Resolutions[position].dmPelsHeight,
                     Pixel);
        }
    }

    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_SETTINGS_RESOLUTION_TEXT,
                        WM_SETTEXT,
                        0,
                        (LPARAM)Buffer);
}


static VOID
FillResolutionsAndColors(PINFO pInfo)
{
    PSETTINGS_ENTRY Current;
    DWORD index, i, num;
    DWORD MaxBpp = 0;
    UINT types[5];

    pInfo->CurrentDisplayDevice = pInfo->DisplayDeviceList; /* Update global variable */

    /* find max bpp */
    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_BPPCOMBO,
                        CB_RESETCONTENT,
                        0,
                        0);
    for (Current = pInfo->DisplayDeviceList->Settings; Current != NULL; Current = Current->Flink)
    {
        if (Current->dmBitsPerPel > MaxBpp)
            MaxBpp = Current->dmBitsPerPel;
    }
    switch (MaxBpp)
    {
        case 32: num = 4; break;
        case 24: num = 3; break;
        case 16: num = 2; break;
        case 15: num = 1; break;
        case 8:  num = 0; break;
        default: num = 0; break;
    }

    types[0] = IDS_256COLORS;
    types[1] = IDS_HIGHCOLOR15;
    types[2] = IDS_HIGHCOLOR16;
    types[3] = IDS_HIGHCOLOR24;
    types[4] = IDS_HIGHCOLOR32;

    /* Fill color depths combo box */
    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_BPPCOMBO,
                        CB_RESETCONTENT,
                        0,
                        0);

    for (i = 0, Current = pInfo->DisplayDeviceList->Settings;
         i <= num && Current != NULL;
         i++, Current = Current->Flink)
    {
        WCHAR Buffer[64];
        if (LoadStringW(hInst,
                        types[i],
                        Buffer,
                        sizeof(Buffer) / sizeof(WCHAR)))
        {
            index = (DWORD)SendDlgItemMessageW(pInfo->hDisplayPage,
                                               IDC_BPPCOMBO,
                                               CB_FINDSTRINGEXACT,
                                               (WPARAM)-1,
                                               (LPARAM)Buffer);
            if (index == (DWORD)CB_ERR)
            {
                index = (DWORD)SendDlgItemMessageW(pInfo->hDisplayPage,
                                                   IDC_BPPCOMBO,
                                                   CB_ADDSTRING,
                                                   0,
                                                   (LPARAM)Buffer);
                SendDlgItemMessageW(pInfo->hDisplayPage,
                                    IDC_BPPCOMBO,
                                    CB_SETITEMDATA,
                                    index,
                                    types[i]);
            }
        }
    }

    /* Fill resolutions slider */
    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_GEOSLIDER,
                        TBM_CLEARTICS,
                        TRUE,
                        0);
    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_GEOSLIDER,
                        TBM_SETRANGE,
                        TRUE,
                        MAKELONG(0, pInfo->DisplayDeviceList->ResolutionsCount)); //extra 1 for full screen


}


static VOID
ReLoadDisplayPage(PINFO pInfo)
{
    DWORD index;
    INT width, height, pos = 0;
    INT bpp, num, i, screenmode;
    BOOL bSet = FALSE;

    /* get fullscreen info */
    screenmode = GetIntegerFromSettings(pInfo->pRdpSettings, L"screen mode id");

    /* set trackbar position */
    width = GetIntegerFromSettings(pInfo->pRdpSettings, L"desktopwidth");
    height = GetIntegerFromSettings(pInfo->pRdpSettings, L"desktopheight");

    if (width != -1 && height != -1)
    {
        if(screenmode == 2)
        {
            pos = SendDlgItemMessageW(pInfo->hDisplayPage,
                                      IDC_GEOSLIDER,
                                      TBM_GETRANGEMAX,
                                      0,
                                      0);
        }
        else
        {
            for (index = 0; index < pInfo->CurrentDisplayDevice->ResolutionsCount; index++)
            {
                if (pInfo->CurrentDisplayDevice->Resolutions[index].dmPelsWidth == width &&
                    pInfo->CurrentDisplayDevice->Resolutions[index].dmPelsHeight == height)
                {
                    pos = index;
                    break;
                }
            }
        }
    }

    /* set slider position */
    SendDlgItemMessageW(pInfo->hDisplayPage,
                        IDC_GEOSLIDER,
                        TBM_SETPOS,
                        TRUE,
                        pos);

    OnResolutionChanged(pInfo, pos);


     /* set color combo */
    bpp = GetIntegerFromSettings(pInfo->pRdpSettings, L"session bpp");

    num = SendDlgItemMessageW(pInfo->hDisplayPage,
                              IDC_BPPCOMBO,
                              CB_GETCOUNT,
                              0,
                              0);
    for (i = 0; i < num; i++)
    {
        INT data = SendDlgItemMessageW(pInfo->hDisplayPage,
                                       IDC_BPPCOMBO,
                                       CB_GETITEMDATA,
                                       i,
                                       0);
        if (data == bpp)
        {
            SendDlgItemMessageW(pInfo->hDisplayPage,
                                IDC_BPPCOMBO,
                                CB_SETCURSEL,
                                i,
                                0);
            bSet = TRUE;
            break;
        }
    }

    if (!bSet)
    {
        SendDlgItemMessageW(pInfo->hDisplayPage,
                            IDC_BPPCOMBO,
                            CB_SETCURSEL,
                            num - 1,
                            0);
    }
}


static VOID
DisplayOnInit(HWND hwnd,
              PINFO pInfo)
{
    DISPLAY_DEVICEW displayDevice;
    DWORD iDevNum = 0;
    BOOL GotDev = FALSE;

    SetWindowLongPtrW(hwnd,
                      GWLP_USERDATA,
                      (LONG_PTR)pInfo);

    pInfo->hDisplayPage = hwnd;

    SetWindowPos(pInfo->hDisplayPage,
                 NULL,
                 2,
                 22,
                 0,
                 0,
                 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);

        pInfo->hRemote = LoadImageW(hInst,
                                    MAKEINTRESOURCEW(IDI_REMOTE),
                                    IMAGE_ICON,
                                    32,
                                    32,
                                    LR_DEFAULTCOLOR);
        if (pInfo->hRemote)
        {
            SendDlgItemMessageW(pInfo->hDisplayPage,
                                IDC_REMICON,
                                STM_SETICON,
                                (WPARAM)pInfo->hRemote,
                                0);
        }

        pInfo->hColor = LoadImageW(hInst,
                                   MAKEINTRESOURCEW(IDI_COLORS),
                                   IMAGE_ICON,
                                   32,
                                   32,
                                  LR_DEFAULTCOLOR);
        if (pInfo->hColor)
        {
            SendDlgItemMessageW(pInfo->hDisplayPage,
                                IDC_COLORSICON,
                                STM_SETICON,
                                (WPARAM)pInfo->hColor,
                                0);
        }

        pInfo->hSpectrum = LoadImageW(hInst,
                                      MAKEINTRESOURCEW(IDB_SPECT),
                                      IMAGE_BITMAP,
                                      0,
                                      0,
                                      LR_DEFAULTCOLOR);
        if (pInfo->hSpectrum)
        {
            GetObjectW(pInfo->hSpectrum,
                       sizeof(BITMAP),
                       &pInfo->bitmap);
        }

        /* Get video cards list */
        displayDevice.cb = (DWORD)sizeof(DISPLAY_DEVICE);
        while (EnumDisplayDevicesW(NULL, iDevNum, &displayDevice, 0x1))
        {
            if ((displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0)
            {
                if (AddDisplayDevice(pInfo, &displayDevice))
                    GotDev = TRUE;
            }
            iDevNum++;
        }

        if (GotDev)
        {
            FillResolutionsAndColors(pInfo);
            ReLoadDisplayPage(pInfo);
        }
}


INT_PTR CALLBACK
DisplayDlgProc(HWND hDlg,
               UINT message,
               WPARAM wParam,
               LPARAM lParam)
{
    PINFO pInfo = (PINFO)GetWindowLongPtrW(hDlg,
                                           GWLP_USERDATA);

    switch (message)
    {
        case WM_INITDIALOG:
            DisplayOnInit(hDlg, (PINFO)lParam);
            return TRUE;

        case WM_DRAWITEM:
        {
            LPDRAWITEMSTRUCT lpDrawItem;
            lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
            if(lpDrawItem->CtlID == IDC_COLORIMAGE)
            {
                HDC hdcMem;
                HBITMAP hSpecOld;
                hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
                if (hdcMem != NULL)
                {
                    hSpecOld = SelectObject(hdcMem, pInfo->hSpectrum);
                    StretchBlt(lpDrawItem->hDC,
                               lpDrawItem->rcItem.left,
                               lpDrawItem->rcItem.top,
                               lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
                               lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
                               hdcMem,
                               0,
                               0,
                               pInfo->bitmap.bmWidth,
                               pInfo->bitmap.bmHeight,
                               SRCCOPY);
                    SelectObject(hdcMem, hSpecOld);
                    DeleteDC(hdcMem);
                }
            }
            break;
        }

        case WM_HSCROLL:
        {
            switch (LOWORD(wParam))
            {
                case TB_LINEUP:
                case TB_LINEDOWN:
                case TB_PAGEUP:
                case TB_PAGEDOWN:
                case TB_TOP:
                case TB_BOTTOM:
                case TB_ENDTRACK:
                {
                    INT newPosition = (DWORD)SendDlgItemMessageW(hDlg, IDC_GEOSLIDER, TBM_GETPOS, 0, 0);
                    OnResolutionChanged(pInfo, newPosition);
                    break;
                }

                case TB_THUMBTRACK:
                    OnResolutionChanged(pInfo, HIWORD(wParam));
                    break;
            }
            break;
        }

        case WM_CLOSE:
        {
            if (pInfo->hRemote)
                DestroyIcon(pInfo->hRemote);

            if (pInfo->hColor)
                DestroyIcon(pInfo->hColor);

            if (pInfo->hSpectrum)
                DeleteObject(pInfo->hSpectrum);

            break;
        }

        break;
    }
    return 0;
}


static BOOL
OnMainCreate(HWND hwnd,
             PRDPSETTINGS pRdpSettings)
{
    PINFO pInfo;
    TCITEMW item;
    BOOL bRet = FALSE;

    pInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
                      sizeof(INFO));
    if (pInfo)
    {
        SetWindowLongPtrW(hwnd,
                          GWLP_USERDATA,
                          (LONG_PTR)pInfo);

        pInfo->hSelf = hwnd;

        /* add main settings pointer */
        pInfo->pRdpSettings = pRdpSettings;

        /* set the dialog icons */
        pInfo->hMstscSm = LoadImageW(hInst,
                                   MAKEINTRESOURCEW(IDI_MSTSC),
                                   IMAGE_ICON,
                                   16,
                                   16,
                                   LR_DEFAULTCOLOR);
        if (pInfo->hMstscSm)
        {
            SendMessageW(hwnd,
                         WM_SETICON,
                         ICON_SMALL,
                        (WPARAM)pInfo->hMstscSm);
        }
        pInfo->hMstscLg = LoadImageW(hInst,
                                   MAKEINTRESOURCEW(IDI_MSTSC),
                                   IMAGE_ICON,
                                   32,
                                   32,
                                   LR_DEFAULTCOLOR);
        if (pInfo->hMstscLg)
        {
            SendMessageW(hwnd,
                         WM_SETICON,
                         ICON_BIG,
                        (WPARAM)pInfo->hMstscLg);
        }

        pInfo->hHeader = (HBITMAP)LoadImageW(hInst,
                                             MAKEINTRESOURCEW(IDB_HEADER),
                                             IMAGE_BITMAP,
                                             0,
                                             0,
                                             LR_DEFAULTCOLOR);
        if (pInfo->hHeader)
        {
            GetObjectW(pInfo->hHeader,
                       sizeof(BITMAP),
                       &pInfo->headerbitmap);
        }

        /* setup the tabs */
        pInfo->hTab = GetDlgItem(hwnd, IDC_TAB);
        if (pInfo->hTab)
        {
            if (CreateDialogParamW(hInst,
                                   MAKEINTRESOURCEW(IDD_GENERAL),
                                   pInfo->hTab,
                                   GeneralDlgProc,
                                   (LPARAM)pInfo))
            {
                WCHAR str[256];
                ZeroMemory(&item, sizeof(TCITEM));
                item.mask = TCIF_TEXT;
                if (LoadStringW(hInst, IDS_TAB_GENERAL, str, 256))
                    item.pszText = str;
                item.cchTextMax = 256;
                (void)TabCtrl_InsertItem(pInfo->hTab, 0, &item);
            }

            if (CreateDialogParamW(hInst,
                                   MAKEINTRESOURCEW(IDD_DISPLAY),
                                   pInfo->hTab,
                                   DisplayDlgProc,
                                   (LPARAM)pInfo))
            {
                WCHAR str[256];
                ZeroMemory(&item, sizeof(TCITEM));
                item.mask = TCIF_TEXT;
                if (LoadStringW(hInst, IDS_TAB_DISPLAY, str, 256))
                    item.pszText = str;
                item.cchTextMax = 256;
                (void)TabCtrl_InsertItem(pInfo->hTab, 1, &item);
            }

            OnTabWndSelChange(pInfo);
        }
    }

    return bRet;
}

static void Cleanup(PINFO pInfo)
{
    if (pInfo)
    {
        if (pInfo->hMstscSm)
            DestroyIcon(pInfo->hMstscSm);
        if (pInfo->hMstscLg)
            DestroyIcon(pInfo->hMstscLg);
        if (pInfo->hHeader)
            DeleteObject(pInfo->hHeader);
        if (pInfo->hSpectrum)
            DeleteObject(pInfo->hSpectrum);
        if (pInfo->hRemote)
            DestroyIcon(pInfo->hRemote);
        if (pInfo->hLogon)
            DestroyIcon(pInfo->hLogon);
        if (pInfo->hConn)
            DestroyIcon(pInfo->hConn);
        if (pInfo->hColor)
            DestroyIcon(pInfo->hColor);
        HeapFree(GetProcessHeap(),
                 0,
                 pInfo);
    }
}

static INT_PTR CALLBACK
DlgProc(HWND hDlg,
        UINT Message,
        WPARAM wParam,
        LPARAM lParam)
{
    PINFO pInfo;

    /* Get the window context */
    pInfo = (PINFO)GetWindowLongPtrW(hDlg,
                                     GWLP_USERDATA);
    if (pInfo == NULL && Message != WM_INITDIALOG)
    {
        goto HandleDefaultMessage;
    }

    switch(Message)
    {
        case WM_INITDIALOG:
            OnMainCreate(hDlg, (PRDPSETTINGS)lParam);
        break;

        case WM_COMMAND:
        {
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
            {
                if (LOWORD(wParam) == IDOK )
                {
                    SaveAllSettings(pInfo);
                    SaveRdpSettingsToFile(NULL, pInfo->pRdpSettings);
                }
                Cleanup(pInfo);
                EndDialog(hDlg, LOWORD(wParam));
            }

            break;
        }

        case WM_NOTIFY:
        {
            //INT idctrl;
            LPNMHDR pnmh;
            //idctrl = (int)wParam;
            pnmh = (LPNMHDR)lParam;
            if (//(pnmh->hwndFrom == pInfo->hSelf) &&
                (pnmh->idFrom == IDC_TAB) &&
                (pnmh->code == TCN_SELCHANGE))
            {
                OnTabWndSelChange(pInfo);
            }

            break;
        }

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc;

            hdc = BeginPaint(hDlg, &ps);
            if (hdc != NULL)
            {
                HDC hdcMem = CreateCompatibleDC(hdc);
                if (hdcMem)
                {
                    WCHAR szBuffer[32];
                    RECT bmpRc, txtRc;
                    LOGFONTW lf;
                    HFONT hFont, hFontOld;
                    HBITMAP hBmpOld;

                    GetClientRect(pInfo->hSelf, &bmpRc);

                    hBmpOld = SelectObject(hdcMem, pInfo->hHeader);
                    StretchBlt(hdc,
                               0,
                               0,
                               bmpRc.right,
                               pInfo->headerbitmap.bmHeight,
                               hdcMem,
                               0,
                               0,
                               pInfo->headerbitmap.bmWidth,
                               pInfo->headerbitmap.bmHeight,
                               SRCCOPY);

                    SelectObject(hdcMem, hBmpOld);
                    txtRc.left = bmpRc.right / 4;
                    txtRc.top = 10;
                    txtRc.right = bmpRc.right * 3 / 4;
                    txtRc.bottom = pInfo->headerbitmap.bmHeight / 2;

                    ZeroMemory(&lf, sizeof(LOGFONTW));

                    if (LoadStringW(hInst,
                                    IDS_HEADERTEXT1,
                                    szBuffer,
                                    sizeof(szBuffer) / sizeof(WCHAR)))
                    {
                        lf.lfHeight = 20;
                        lf.lfCharSet = OEM_CHARSET;
                        lf.lfQuality = DEFAULT_QUALITY;
                        lf.lfWeight = FW_MEDIUM;
                        wcscpy(lf.lfFaceName, L"Tahoma");

                        hFont = CreateFontIndirectW(&lf);
                        if (hFont)
                        {
                            hFontOld = SelectObject(hdc, hFont);

                            DPtoLP(hdc, (PPOINT)&txtRc, 2);
                            SetTextColor(hdc, RGB(255,255,255));
                            SetBkMode(hdc, TRANSPARENT);
                            DrawTextW(hdc,
                                      szBuffer,
                                      -1,
                                      &txtRc,
                                      DT_BOTTOM | DT_SINGLELINE | DT_NOCLIP | DT_CENTER); //DT_CENTER makes the text visible in RTL layouts...
                            SelectObject(hdc, hFontOld);
                            DeleteObject(hFont);
                        }
                    }

                    txtRc.left = bmpRc.right / 4;
                    txtRc.top = txtRc.bottom - 5;
#ifdef __REACTOS__
                    txtRc.right = bmpRc.right * 4 / 5;
#else
                    txtRc.right = bmpRc.right * 3 / 4;
#endif
                    txtRc.bottom = pInfo->headerbitmap.bmHeight * 9 / 10;

                    if (LoadStringW(hInst,
                                    IDS_HEADERTEXT2,
                                    szBuffer,
                                    sizeof(szBuffer) / sizeof(WCHAR)))
                    {
                        lf.lfHeight = 24;
                        lf.lfCharSet = OEM_CHARSET;
                        lf.lfQuality = DEFAULT_QUALITY;
                        lf.lfWeight = FW_EXTRABOLD;
                        wcscpy(lf.lfFaceName, L"Tahoma");

                        hFont = CreateFontIndirectW(&lf);
                        if (hFont)
                        {
                            hFontOld = SelectObject(hdc, hFont);

                            DPtoLP(hdc, (PPOINT)&txtRc, 2);
                            SetTextColor(hdc, RGB(255,255,255));
                            SetBkMode(hdc, TRANSPARENT);
                            DrawTextW(hdc,
                                      szBuffer,
                                      -1,
                                      &txtRc,
                                      DT_TOP | DT_SINGLELINE);
                            SelectObject(hdc, hFontOld);
                            DeleteObject(hFont);
                        }
                    }

                    DeleteDC(hdcMem);
                }

                EndPaint(hDlg, &ps);
            }

            break;
        }

        case WM_CLOSE:
        {
            Cleanup(pInfo);
            EndDialog(hDlg, 0);
        }
        break;

HandleDefaultMessage:
        default:
            return FALSE;
    }

    return FALSE;
}


BOOL
OpenRDPConnectDialog(HINSTANCE hInstance,
                     PRDPSETTINGS pRdpSettings)
{
    INITCOMMONCONTROLSEX iccx;

    hInst = hInstance;

    iccx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccx.dwICC = ICC_TAB_CLASSES;
    InitCommonControlsEx(&iccx);

    return (DialogBoxParamW(hInst,
                            MAKEINTRESOURCEW(IDD_CONNECTDIALOG),
                            NULL,
                            DlgProc,
                            (LPARAM)pRdpSettings) == IDOK);
}