// FM.cpp

#include "StdAfx.h"

#include "../../../Common/MyWindows.h"

#if defined(__MINGW32__) || defined(__MINGW64__)
#include <shlwapi.h>
#else
#include <Shlwapi.h>
#endif

#include "../../../../C/Compiler.h"
#include "../../../../C/Alloc.h"
#ifdef _WIN32
#include "../../../../C/DllSecur.h"
#endif

#include "../../../Common/StringConvert.h"
#include "../../../Common/StringToInt.h"

#include "../../../Windows/ErrorMsg.h"
#include "../../../Windows/MemoryLock.h"
#include "../../../Windows/NtCheck.h"
#include "../../../Windows/System.h"

#ifndef UNDER_CE
#include "../../../Windows/SecurityUtils.h"
#endif

#include "../GUI/ExtractRes.h"

#include "resource.h"

#include "App.h"
#include "FormatUtils.h"
#include "LangUtils.h"
#include "MyLoadMenu.h"
#include "Panel.h"
#include "RegistryUtils.h"
#include "StringUtils.h"
#include "ViewSettings.h"

using namespace NWindows;
using namespace NFile;
using namespace NFind;

// #define MAX_LOADSTRING 100

extern
bool g_DisableUserQuestions;
bool g_DisableUserQuestions;

extern
bool g_RAM_Size_Defined;
bool g_RAM_Size_Defined;

extern
bool g_LargePagesMode;
bool g_LargePagesMode = false;
// static bool g_OpenArchive = false;

static bool g_Maximized = false;

extern
size_t g_RAM_Size;
size_t g_RAM_Size;

#ifdef _WIN32
extern
HINSTANCE g_hInstance;
HINSTANCE g_hInstance;
#endif

HWND g_HWND;

static UString g_MainPath;
static UString g_ArcFormat;

// HRESULT LoadGlobalCodecs();
void FreeGlobalCodecs();

#ifndef UNDER_CE

#ifdef Z7_USE_DYN_ComCtl32Version
Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION

DWORD g_ComCtl32Version;

static DWORD GetDllVersion(LPCTSTR dllName)
{
  DWORD dwVersion = 0;
  const HMODULE hmodule = LoadLibrary(dllName);
  if (hmodule)
  {
    const
     DLLGETVERSIONPROC f_DllGetVersion = Z7_GET_PROC_ADDRESS(
     DLLGETVERSIONPROC, hmodule,
    "DllGetVersion");
    if (f_DllGetVersion)
    {
      DLLVERSIONINFO dvi;
      ZeroMemory(&dvi, sizeof(dvi));
      dvi.cbSize = sizeof(dvi);
      const HRESULT hr = f_DllGetVersion(&dvi);
      if (SUCCEEDED(hr))
        dwVersion = (DWORD)MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion);
    }
    FreeLibrary(hmodule);
  }
  return dwVersion;
}

#endif
#endif

bool g_IsSmallScreen = false;

extern
bool g_LVN_ITEMACTIVATE_Support;
bool g_LVN_ITEMACTIVATE_Support = true;
// LVN_ITEMACTIVATE replaces both NM_DBLCLK & NM_RETURN
// Windows 2000
// NT/98 + IE 3 (g_ComCtl32Version >= 4.70)


static const int kNumDefaultPanels = 1;
static const int kSplitterWidth = 4;
static const int kSplitterRateMax = 1 << 16;
static const int kPanelSizeMin = 120;


class CSplitterPos
{
  int _ratio; // 10000 is max
  int _pos;
  int _fullWidth;
  void SetRatioFromPos(HWND hWnd)
    { _ratio = (_pos + kSplitterWidth / 2) * kSplitterRateMax /
        MyMax(GetWidth(hWnd), 1); }
public:
  int GetPos() const
    { return _pos; }
  int GetWidth(HWND hWnd) const
  {
    RECT rect;
    ::GetClientRect(hWnd, &rect);
    return rect.right;
  }
  void SetRatio(HWND hWnd, int aRatio)
  {
    _ratio = aRatio;
    SetPosFromRatio(hWnd);
  }
  void SetPosPure(HWND hWnd, int pos)
  {
    int posMax = GetWidth(hWnd) - kSplitterWidth;
    if (posMax < kPanelSizeMin * 2)
      pos = posMax / 2;
    else
    {
      if (pos > posMax - kPanelSizeMin)
        pos = posMax - kPanelSizeMin;
      else if (pos < kPanelSizeMin)
        pos = kPanelSizeMin;
    }
    _pos = pos;
  }
  void SetPos(HWND hWnd, int pos)
  {
    _fullWidth = GetWidth(hWnd);
    SetPosPure(hWnd, pos);
    SetRatioFromPos(hWnd);
  }
  void SetPosFromRatio(HWND hWnd)
  {
    int fullWidth = GetWidth(hWnd);
    if (_fullWidth != fullWidth && fullWidth != 0)
    {
      _fullWidth = fullWidth;
      SetPosPure(hWnd, GetWidth(hWnd) * _ratio / kSplitterRateMax - kSplitterWidth / 2);
    }
  }
};

static bool g_CanChangeSplitter = false;
static UInt32 g_SplitterPos = 0;
static CSplitterPos g_Splitter;
static bool g_PanelsInfoDefined = false;
static bool g_WindowWasCreated = false;

static int g_StartCaptureMousePos;
static int g_StartCaptureSplitterPos;

CApp g_App;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

static const wchar_t * const kWindowClass = L"7-Zip::FM";

#ifdef UNDER_CE
#define WS_OVERLAPPEDWINDOW ( \
  WS_OVERLAPPED   | \
  WS_CAPTION      | \
  WS_SYSMENU      | \
  WS_THICKFRAME   | \
  WS_MINIMIZEBOX  | \
  WS_MAXIMIZEBOX)
#endif


/*
typedef HRESULT (WINAPI *Func_SetWindowTheme)(
  HWND    hwnd,
  LPCWSTR pszSubAppName,
  LPCWSTR pszSubIdList
);

typedef BOOL (WINAPI *Func_AllowDarkModeForWindow)(
  HWND a_HWND, BOOL a_Allow);

enum PreferredAppMode
{
  Default,
  AllowDark,
  ForceDark,
  ForceLight,
  Max
};
// ordinal 135, in 1903
typedef BOOL (WINAPI *Func_SetPreferredAppMode)(PreferredAppMode appMode);

typedef HRESULT (WINAPI *Func_DwmSetWindowAttribute)(
       HWND    hwnd,
       DWORD   dwAttribute,
       LPCVOID pvAttribute,
       DWORD   cbAttribute
);
*/

static BOOL InitInstance(int nCmdShow)
{
  CWindow wnd;

  // LoadString(hInstance, IDS_CLASS, windowClass, MAX_LOADSTRING);

  UString title ("7-Zip"); // LangString(IDS_APP_TITLE, 0x03000000);

  /*
  //If it is already running, then focus on the window
  hWnd = FindWindow(windowClass, title);
  if (hWnd)
  {
    SetForegroundWindow ((HWND) (((DWORD)hWnd) | 0x01));
    return 0;
  }
  */

  WNDCLASSW wc;

  // wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = g_hInstance;
  wc.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICON));

  // wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  wc.hCursor = ::LoadCursor(NULL, IDC_SIZEWE);
  // wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);

  wc.lpszMenuName =
    #ifdef UNDER_CE
    0
    #else
    MAKEINTRESOURCEW(IDM_MENU)
    #endif
    ;

  wc.lpszClassName = kWindowClass;

  if (MyRegisterClass(&wc) == 0)
    return FALSE;

  // RECT rect;
  // GetClientRect(hWnd, &rect);

  DWORD style = WS_OVERLAPPEDWINDOW;
  // DWORD style = 0;
  
  CWindowInfo info;
  info.maximized = false;
  int x, y, xSize, ySize;
  x = y = xSize = ySize = CW_USEDEFAULT;
  bool windowPosIsRead;
  info.Read(windowPosIsRead, g_PanelsInfoDefined);

  if (windowPosIsRead)
  {
    x = info.rect.left;
    y = info.rect.top;
    
    xSize = RECT_SIZE_X(info.rect);
    ySize = RECT_SIZE_Y(info.rect);
  }


  if (g_PanelsInfoDefined)
  {
    g_SplitterPos = info.splitterPos;
    if (info.numPanels < 1 || info.numPanels > 2)
      info.numPanels = kNumDefaultPanels;
    if (info.currentPanel >= 2)
      info.currentPanel = 0;
  }
  else
  {
    info.numPanels = kNumDefaultPanels;
    info.currentPanel = 0;
  }

  g_App.NumPanels = info.numPanels;
  g_App.LastFocusedPanel = info.currentPanel;

  if (!wnd.Create(kWindowClass, title, style,
    x, y, xSize, ySize, NULL, NULL, g_hInstance, NULL))
    return FALSE;

  /*
  // doesn't work
  {
    const HMODULE hmodule = LoadLibrary("UxTheme.dll");
    if (hmodule)
    {
      {
        const
          Func_AllowDarkModeForWindow f = Z7_GET_PROC_ADDRESS(
          Func_AllowDarkModeForWindow, hmodule,
          MAKEINTRESOURCEA(133));
        if (f)
        {
          BOOL res = f((HWND)wnd, TRUE);
          res = res;
        }
      }
      {
        const
          Func_SetPreferredAppMode f = Z7_GET_PROC_ADDRESS(
          Func_SetPreferredAppMode, hmodule,
          MAKEINTRESOURCEA(135));
        if (f)
        {
          f(ForceDark);
        }
      }
      {
        const
          Func_SetWindowTheme f = Z7_GET_PROC_ADDRESS(
          Func_SetWindowTheme, hmodule,
          "SetWindowTheme");
        if (f)
        {
          // HRESULT hres = f((HWND)wnd, L"DarkMode_Explorer", NULL);
          HRESULT hres = f((HWND)wnd, L"Explorer", NULL);
          hres = hres;
        }
      }
      FreeLibrary(hmodule);
    }
  }
  {
    const HMODULE hmodule = LoadLibrary("Dwmapi.dll");
    if (hmodule)
    {
      const
        Func_DwmSetWindowAttribute f = Z7_GET_PROC_ADDRESS(
        Func_DwmSetWindowAttribute, hmodule,
        "DwmSetWindowAttribute");
      if (f)
      {
        #ifndef Z7_WIN_DWMWA_USE_IMMERSIVE_DARK_MODE
        #define Z7_WIN_DWMWA_USE_IMMERSIVE_DARK_MODE 20
        #endif
        BOOL value = TRUE;
        f((HWND)wnd, Z7_WIN_DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
      }
      FreeLibrary(hmodule);
    }
  }
  */

  if (nCmdShow == SW_SHOWNORMAL ||
      nCmdShow == SW_SHOW
      #ifndef UNDER_CE
      || nCmdShow == SW_SHOWDEFAULT
      #endif
      )
  {
    if (info.maximized)
      nCmdShow = SW_SHOWMAXIMIZED;
    else
      nCmdShow = SW_SHOWNORMAL;
  }

  if (nCmdShow == SW_SHOWMAXIMIZED)
    g_Maximized = true;

  #ifndef UNDER_CE
  WINDOWPLACEMENT placement;
  placement.length = sizeof(placement);
  if (wnd.GetPlacement(&placement))
  {
    if (windowPosIsRead)
      placement.rcNormalPosition = info.rect;
    placement.showCmd = (UINT)nCmdShow;
    wnd.SetPlacement(&placement);
  }
  else
  #endif
    wnd.Show(nCmdShow);

  return TRUE;
}

/*
static void GetCommands(const UString &aCommandLine, UString &aCommands)
{
  UString aProgramName;
  aCommands.Empty();
  bool aQuoteMode = false;
  for (int i = 0; i < aCommandLine.Length(); i++)
  {
    wchar_t aChar = aCommandLine[i];
    if (aChar == L'\"')
      aQuoteMode = !aQuoteMode;
    else if (aChar == L' ' && !aQuoteMode)
    {
      if (!aQuoteMode)
      {
        i++;
        break;
      }
    }
    else
      aProgramName += aChar;
  }
  aCommands = aCommandLine.Ptr(i);
}
*/

#if defined(_WIN32) && !defined(_WIN64) && !defined(UNDER_CE)

extern
bool g_Is_Wow64;
bool g_Is_Wow64;

typedef BOOL (WINAPI *Func_IsWow64Process)(HANDLE, PBOOL);

static void Set_Wow64()
{
  g_Is_Wow64 = false;
  const
  Func_IsWow64Process fn = Z7_GET_PROC_ADDRESS(
  Func_IsWow64Process, GetModuleHandleA("kernel32.dll"),
      "IsWow64Process");
  if (fn)
  {
    BOOL isWow;
    if (fn(GetCurrentProcess(), &isWow))
      g_Is_Wow64 = (isWow != FALSE);
  }
}

#endif

#if _MSC_VER > 1400 /* && _MSC_VER <= 1900 */
  // GetVersion was declared deprecated
  #pragma warning(disable : 4996)
#endif
#ifdef __clang__
  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

bool IsLargePageSupported();
bool IsLargePageSupported()
{
  #ifdef _WIN64
  return true;
  #else

  DWORD v = GetVersion();
  // low byte is major version:
  // next byte is minor version:
  v = ((v & 0xff) << 8) | ((v >> 8) & 0xFF);
  return (v > 0x501);
  // if ((Byte)v < 5) return false;
  // if ((Byte)v > 5) return true;
  // return ((Byte)(v >> 8) > 1);
  /* large pages work in 5.1 (XP-32bit) if it's (g_Is_Wow64) mode;
     but here we don't enable them in (XP-32bit). */
  #endif
}

#ifndef UNDER_CE

static void SetMemoryLock()
{
  if (!IsLargePageSupported())
    return;
  // if (ReadLockMemoryAdd())
  NSecurity::AddLockMemoryPrivilege();

  if (ReadLockMemoryEnable())
  if (NSecurity::Get_LargePages_RiskLevel() == 0)
  {
    // note: child processes can inherit that Privilege
    g_LargePagesMode = NSecurity::EnablePrivilege_LockMemory();
  }
}

extern
bool g_SymLink_Supported;
bool g_SymLink_Supported = false;

static void Set_SymLink_Supported()
{
  // g_SymLink_Supported = false;
  const DWORD v = GetVersion();
  // low byte is major version:
  if ((Byte)v < 6)
    return;
  g_SymLink_Supported = true;
  // if (g_SymLink_Supported)
  {
    NSecurity::EnablePrivilege_SymLink();
  }
}

#endif

/*
static const int kNumSwitches = 1;

namespace NKey {
enum Enum
{
  kOpenArachive = 0
};

}

static const CSwitchForm kSwitchForms[kNumSwitches] =
  {
    { L"SOA", NSwitchType::kSimple, false },
  };
*/

// int APIENTRY WinMain2(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int /* nCmdShow */);

static void ErrorMessage(const wchar_t *s)
{
  MessageBoxW(NULL, s, L"7-Zip", MB_ICONERROR);
}

static void ErrorMessage(const char *s)
{
  ErrorMessage(GetUnicodeString(s));
}


#if defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
#define NT_CHECK_FAIL_ACTION ErrorMessage("Unsupported Windows version"); return 1;
#endif

static int WINAPI WinMain2(int nCmdShow)
{
  g_RAM_Size_Defined = NSystem::GetRamSize(g_RAM_Size);

  #ifdef _WIN32

  /*
  #ifndef _WIN64
  #ifndef UNDER_CE
  {
    HMODULE hMod = GetModuleHandle("Kernel32.dll");
    if (hMod)
    {
      typedef BOOL (WINAPI *PSETDEP)(DWORD);
      #define MY_PROCESS_DEP_ENABLE 1
      PSETDEP procSet = (PSETDEP)GetProcAddress(hMod,"SetProcessDEPPolicy");
      if (procSet)
        procSet(MY_PROCESS_DEP_ENABLE);

      typedef BOOL (WINAPI *HSI)(HANDLE, HEAP_INFORMATION_CLASS ,PVOID, SIZE_T);
      HSI hsi = (HSI)GetProcAddress(hMod, "HeapSetInformation");
      #define MY_HeapEnableTerminationOnCorruption ((HEAP_INFORMATION_CLASS)1)
      if (hsi)
        hsi(NULL, MY_HeapEnableTerminationOnCorruption, NULL, 0);
    }
  }
  #endif
  #endif
  */

  NT_CHECK
  #ifdef Z7_LARGE_PAGES
  z7_LargePage_Set(0, 0, 0);
  #endif

  #endif

  #ifdef Z7_LANG
  LoadLangOneTime();
  #endif

  InitCommonControls();

#ifdef Z7_USE_DYN_ComCtl32Version
  g_ComCtl32Version = ::GetDllVersion(TEXT("comctl32.dll"));
  g_LVN_ITEMACTIVATE_Support = (g_ComCtl32Version >= MAKELONG(71, 4));
#endif

  #if defined(_WIN32) && !defined(_WIN64) && !defined(UNDER_CE)
  Set_Wow64();
  #endif


  g_IsSmallScreen = !NWindows::NControl::IsDialogSizeOK(200, 200);

  // OleInitialize is required for drag and drop.
  #ifndef UNDER_CE
  OleInitialize(NULL);
  #endif
  // Maybe needs CoInitializeEx also ?
  // NCOM::CComInitializer comInitializer;

  UString commandsString;
  // MessageBoxW(NULL, GetCommandLineW(), L"", 0);

  #ifdef UNDER_CE
  commandsString = GetCommandLineW();
  #else
  UString programString;
  SplitStringToTwoStrings(GetCommandLineW(), programString, commandsString);
  #endif

  commandsString.Trim();
  UString paramString, tailString;
  SplitStringToTwoStrings(commandsString, paramString, tailString);
  paramString.Trim();
  tailString.Trim();
  if (tailString.IsPrefixedBy("-t"))
    g_ArcFormat = tailString.Ptr(2);

  /*
  UStringVector switches;
  for (;;)
  {
    if (tailString.IsEmpty())
      break;
    UString s1, s2;
    SplitStringToTwoStrings(tailString, s1, s2);
    if (s2.IsEmpty())
    {
      tailString.Trim();
      switches.Add(tailString);
      break;
    }
    s1.Trim();
    switches.Add(s1);
    tailString = s2;
  }

  FOR_VECTOR(i, switches)
  {
    const UString &sw = switches[i];
    if (sw.IsPrefixedBy(L"-t"))
      g_ArcFormat = sw.Ptr(2);
    //
    else if (sw.IsPrefixedBy(L"-stp"))
    {
      const wchar_t *end;
      UInt32 val = ConvertStringToUInt32(sw.Ptr(4), &end);
      if (*end != 0)
        throw 111;
      g_TypeParseLevel = val;
    }
    else
    //
      throw 112;
  }
  */

  if (!paramString.IsEmpty())
  {
    g_MainPath = paramString;
    // return WinMain2(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

    // MessageBoxW(NULL, paramString, L"", 0);
  }
  /*
  UStringVector commandStrings;
  NCommandLineParser::SplitCommandLine(GetCommandLineW(), commandStrings);
  NCommandLineParser::CParser parser(kNumSwitches);
  try
  {
    parser.ParseStrings(kSwitchForms, commandStrings);
    const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
    if (nonSwitchStrings.Size() > 1)
    {
      g_MainPath = nonSwitchStrings[1];
      // g_OpenArchive = parser[NKey::kOpenArachive].ThereIs;
      CFileInfoW fileInfo;
      if (FindFile(g_MainPath, fileInfo))
      {
        if (!fileInfo.IsDir())
          g_OpenArchive = true;
      }
    }
  }
  catch(...) { }
  */


  #if defined(_WIN32) && !defined(UNDER_CE)
  SetMemoryLock();
  Set_SymLink_Supported();
  #endif

  g_App.ReloadLangItems();

  MSG msg;
  if (!InitInstance (nCmdShow))
    return FALSE;

  // we will load Global_Codecs at first use instead.
  /*
  OutputDebugStringW(L"Before LoadGlobalCodecs");
  LoadGlobalCodecs();
  OutputDebugStringW(L"After LoadGlobalCodecs");
  */

  #ifndef _UNICODE
  if (g_IsNT)
  {
    HACCEL hAccels = LoadAcceleratorsW(g_hInstance, MAKEINTRESOURCEW(IDR_ACCELERATOR1));
    while (GetMessageW(&msg, NULL, 0, 0))
    {
      if (TranslateAcceleratorW(g_HWND, hAccels, &msg) == 0)
      {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
      }
    }
  }
  else
  #endif
  {
    HACCEL hAccels = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
    while (GetMessage(&msg, NULL, 0, 0))
    {
      if (TranslateAccelerator(g_HWND, hAccels, &msg) == 0)
      {
        // if (g_Hwnd != NULL || !IsDialogMessage(g_Hwnd, &msg))
        // if (!IsDialogMessage(g_Hwnd, &msg))
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
  }

  // Destructor of g_CodecsReleaser can release DLLs.
  // But we suppose that it's better to release DLLs here (before destructor).
  FreeGlobalCodecs();

  g_HWND = NULL;
  #ifndef UNDER_CE
  OleUninitialize();
  #endif
  return (int)msg.wParam;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
    #ifdef UNDER_CE
    LPWSTR
    #else
    LPSTR
    #endif
    /* lpCmdLine */, int nCmdShow)
{
  g_hInstance = hInstance;

  try
  {
    try
    {
      #ifdef _WIN32
      My_SetDefaultDllDirectories();
      #endif
      return WinMain2(nCmdShow);
    }
    catch (...)
    {
      g_ExitEventLauncher.Exit(true);
      throw;
    }
  }
  catch(const CNewException &)
  {
    ErrorMessage(LangString(IDS_MEM_ERROR));
    return 1;
  }
  catch(const UString &s)
  {
    ErrorMessage(s);
    return 1;
  }
  catch(const AString &s)
  {
    ErrorMessage(s.Ptr());
    return 1;
  }
  catch(const wchar_t *s)
  {
    ErrorMessage(s);
    return 1;
  }
  catch(const char *s)
  {
    ErrorMessage(s);
    return 1;
  }
  catch(int v)
  {
    AString e ("Error: ");
    e.Add_UInt32((unsigned)v);
    ErrorMessage(e);
    return 1;
  }
  catch(...)
  {
    ErrorMessage("Unknown error");
    return 1;
  }
}

static void SaveWindowInfo(HWND aWnd)
{
  CWindowInfo info;

  #ifdef UNDER_CE
  
  if (!::GetWindowRect(aWnd, &info.rect))
    return;
  info.maximized = g_Maximized;
  
  #else
  
  WINDOWPLACEMENT placement;
  placement.length = sizeof(placement);
  if (!::GetWindowPlacement(aWnd, &placement))
    return;
  info.rect = placement.rcNormalPosition;
  info.maximized = BOOLToBool(::IsZoomed(aWnd));
  
  #endif
  
  info.numPanels = g_App.NumPanels;
  info.currentPanel = g_App.LastFocusedPanel;
  info.splitterPos = (unsigned)g_Splitter.GetPos();

  info.Save();
}

static void ExecuteCommand(UINT commandID)
{
  CPanel::CDisableTimerProcessing disableTimerProcessing1(g_App.Panels[0]);
  CPanel::CDisableTimerProcessing disableTimerProcessing2(g_App.Panels[1]);

  switch (commandID)
  {
    case kMenuCmdID_Toolbar_Add: g_App.AddToArchive(); break;
    case kMenuCmdID_Toolbar_Extract: g_App.ExtractArchives(); break;
    case kMenuCmdID_Toolbar_Test: g_App.TestArchives(); break;
  }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_COMMAND:
    {
      unsigned wmId    = LOWORD(wParam);
      unsigned wmEvent = HIWORD(wParam);
      if ((HWND) lParam != NULL && wmEvent != 0)
        break;
      if (wmId >= kMenuCmdID_Toolbar_Start && wmId < kMenuCmdID_Toolbar_End)
      {
        ExecuteCommand(wmId);
        return 0;
      }
      if (OnMenuCommand(hWnd, wmId))
        return 0;
      break;
    }
    case WM_INITMENUPOPUP:
      OnMenuActivating(hWnd, HMENU(wParam), LOWORD(lParam));
      break;

    /*
    It doesn't help
    case WM_EXITMENULOOP:
      {
        OnMenuUnActivating(hWnd);
        break;
      }
    case WM_UNINITMENUPOPUP:
      OnMenuUnActivating(hWnd, HMENU(wParam), lParam);
      break;
    */

    case WM_CREATE:
    {
      g_HWND = hWnd;
      /*
      INITCOMMONCONTROLSEX icex;
      icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
      icex.dwICC  = ICC_BAR_CLASSES;
      InitCommonControlsEx(&icex);
      
      // Toolbar buttons used to create the first 4 buttons.
      TBBUTTON tbb [ ] =
      {
        // {0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0},
        // {VIEW_PARENTFOLDER, kParentFolderID, TBSTATE_ENABLED, BTNS_BUTTON, 0L, 0},
          // {0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0},
        {VIEW_NEWFOLDER, ID_FILE_CREATEFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, 0L, 0},
      };
      
      int baseID = 100;
      NWindows::NControl::CToolBar aToolBar;
      aToolBar.Attach(::CreateToolbarEx (hWnd,
        WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS, //  | TBSTYLE_FLAT
        baseID + 2, 11,
        (HINSTANCE)HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR,
        (LPCTBBUTTON)&tbb, Z7_ARRAY_SIZE(tbb),
        0, 0, 100, 30, sizeof (TBBUTTON)));
      */
      // HCURSOR cursor = ::LoadCursor(0, IDC_SIZEWE);
      // ::SetCursor(cursor);

      if (g_PanelsInfoDefined)
        g_Splitter.SetPos(hWnd, (int)g_SplitterPos);
      else
      {
        g_Splitter.SetRatio(hWnd, kSplitterRateMax / 2);
        g_SplitterPos = (unsigned)g_Splitter.GetPos();
      }

      RECT rect;
      ::GetClientRect(hWnd, &rect);
      const int xSize = rect.right;
      int xSizes[2];
      xSizes[0] = g_Splitter.GetPos();
      xSizes[1] = xSize - kSplitterWidth - xSizes[0];
      if (xSizes[1] < 0)
        xSizes[1] = 0;

      g_App.CreateDragTarget();
      
      COpenResult openRes;
      bool needOpenArc = false;

      UString fullPath = g_MainPath;
      if (!fullPath.IsEmpty() /* && g_OpenArchive */)
      {
        if (!NFile::NName::IsAbsolutePath(fullPath))
        {
          FString fullPathF;
          if (NFile::NName::GetFullPath(us2fs(fullPath), fullPathF))
            fullPath = fs2us(fullPathF);
        }
        if (NFile::NFind::DoesFileExist_FollowLink(us2fs(fullPath)))
          needOpenArc = true;
      }
      
      HRESULT res = g_App.Create(hWnd, fullPath, g_ArcFormat, xSizes,
          needOpenArc,
          openRes);

      if (res == E_ABORT)
        return -1;
      
      if ((needOpenArc && !openRes.ArchiveIsOpened) || res != S_OK)
      {
        UString m ("Error");
        if (res == S_FALSE || res == S_OK)
        {
          m = MyFormatNew(openRes.Encrypted ?
                IDS_CANT_OPEN_ENCRYPTED_ARCHIVE :
                IDS_CANT_OPEN_ARCHIVE,
              fullPath);
        }
        else if (res != S_OK)
          m = HResultToMessage(res);
        if (!openRes.ErrorMessage.IsEmpty())
        {
          m.Add_LF();
          m += openRes.ErrorMessage;
        }
        ErrorMessage(m);
        return -1;
      }

      g_WindowWasCreated = true;
      
      // g_SplitterPos = 0;

      // ::DragAcceptFiles(hWnd, TRUE);
      RegisterDragDrop(hWnd, g_App._dropTarget);

      break;
    }

    case WM_CLOSE:
    {
      // why do we use WA_INACTIVE here ?
      SendMessage(hWnd, WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), (LPARAM)hWnd);
      g_ExitEventLauncher.Exit(false);
      // ::DragAcceptFiles(hWnd, FALSE);
      RevokeDragDrop(hWnd);
      g_App._dropTarget.Release();

      if (g_WindowWasCreated)
        g_App.Save();
    
      g_App.ReleaseApp();
      
      if (g_WindowWasCreated)
        SaveWindowInfo(hWnd);

      g_ExitEventLauncher.Exit(true);
      // default DefWindowProc will call DestroyWindow / WM_DESTROY
      break;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      break;
    }
    
    // case WM_MOVE: break;
    
    case WM_LBUTTONDOWN:
      g_StartCaptureMousePos = LOWORD(lParam);
      g_StartCaptureSplitterPos = g_Splitter.GetPos();
      ::SetCapture(hWnd);
      break;
    
    case WM_LBUTTONUP:
    {
      ::ReleaseCapture();
      break;
    }
    
    case WM_MOUSEMOVE:
    {
      if ((wParam & MK_LBUTTON) != 0 && ::GetCapture() == hWnd)
      {
        g_Splitter.SetPos(hWnd, g_StartCaptureSplitterPos +
            (short)LOWORD(lParam) - g_StartCaptureMousePos);
        g_App.MoveSubWindows();
      }
      break;
    }

    case WM_SIZE:
    {
      if (g_CanChangeSplitter)
        g_Splitter.SetPosFromRatio(hWnd);
      else
      {
        g_Splitter.SetPos(hWnd, (int)g_SplitterPos );
        g_CanChangeSplitter = true;
      }
      
      g_Maximized = (wParam == SIZE_MAXIMIZED) || (wParam == SIZE_MAXSHOW);

      g_App.MoveSubWindows();
      /*
      int xSize = LOWORD(lParam);
      int ySize = HIWORD(lParam);
      // int xSplitter = 2;
      int xWidth = g_SplitPos;
      // int xSplitPos = xWidth;
      g_Panel[0]._listView.MoveWindow(0, 0, xWidth, ySize);
      g_Panel[1]._listView.MoveWindow(xSize - xWidth, 0, xWidth, ySize);
      */
      return 0;
      // break;
    }
    
    case WM_SETFOCUS:
      // g_App.SetFocus(g_App.LastFocusedPanel);
      g_App.SetFocusToLastItem();
      break;
    
    /*
    case WM_ACTIVATE:
    {
      int fActive = LOWORD(wParam);
      switch (fActive)
      {
        case WA_INACTIVE:
        {
          // g_FocusIndex = g_App.LastFocusedPanel;
          // g_App.LastFocusedPanel = g_App.GetFocusedPanelIndex();
          // return 0;
        }
      }
      break;
    }
    */
    
    /*
    case kLangWasChangedMessage:
      MyLoadMenu();
      return 0;
    */
      
    /*
    case WM_SETTINGCHANGE:
      break;
    */
    
    case WM_NOTIFY:
    {
      g_App.OnNotify((int)wParam, (LPNMHDR)lParam);
      break;
    }
    
    /*
    case WM_DROPFILES:
    {
      g_App.GetFocusedPanel().CompressDropFiles((HDROP)wParam);
      return 0 ;
    }
    */
  }
  #ifndef _UNICODE
  if (g_IsNT)
    return DefWindowProcW(hWnd, message, wParam, lParam);
  else
  #endif
    return DefWindowProc(hWnd, message, wParam, lParam);

}

static int Window_GetRealHeight(NWindows::CWindow &w)
{
  RECT rect;
  w.GetWindowRect(&rect);
  int res = RECT_SIZE_Y(rect);
  #ifndef UNDER_CE
  WINDOWPLACEMENT placement;
  if (w.GetPlacement(&placement))
    res += placement.rcNormalPosition.top;
  #endif
  return res;
}

void CApp::MoveSubWindows()
{
  HWND hWnd = _window;
  RECT rect;
  if (!hWnd)
    return;
  ::GetClientRect(hWnd, &rect);
  int xSize = rect.right;
  if (xSize == 0)
    return;
  int headerSize = 0;

  #ifdef UNDER_CE
  _commandBar.AutoSize();
  {
    _commandBar.Show(true); // maybe we need it for
    headerSize += _commandBar.Height();
  }
  #endif

  if (_toolBar)
  {
    _toolBar.AutoSize();
    #ifdef UNDER_CE
    int h2 = Window_GetRealHeight(_toolBar);
    _toolBar.Move(0, headerSize, xSize, h2);
    #endif
    headerSize += Window_GetRealHeight(_toolBar);
  }
  
  int ySize = MyMax((int)(rect.bottom - headerSize), 0);
  
  if (NumPanels > 1)
  {
    Panels[0].Move(0, headerSize, g_Splitter.GetPos(), ySize);
    int xWidth1 = g_Splitter.GetPos() + kSplitterWidth;
    Panels[1].Move(xWidth1, headerSize, xSize - xWidth1, ySize);
  }
  else
  {
    /*
    int otherPanel = 1 - LastFocusedPanel;
    if (PanelsCreated[otherPanel])
      Panels[otherPanel].Move(0, headerSize, 0, ySize);
    */
    Panels[LastFocusedPanel].Move(0, headerSize, xSize, ySize);
  }
}
