// ==================================================================
// MDI-приложение с окнами Toolbar и Statusbar
// ==================================================================

#define STRICT
#include <windows.h>
#include <mem.h>
#include "mditb.hpp"

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export FrameWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK _export ChildWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK _export TbWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK _export SbWndProc(HWND, UINT, WPARAM, LPARAM);

// Имена классов окна
char const szFrameClassName[] = "MDITBAppClass";
char const szChildClassName[] = "MDITBChildAppClass";
char const szTbClassName[] =    "MDITBCtrlAppClass";
char const szSbClassName[] =    "MDISBCtrlAppClass";

// Заголовок окна
char const szWindowTitle[] = "MDI Application";

HINSTANCE hInst;

HWND hwndFrame;  // окно Frame Window
HWND hwndClient; // окно Client Window
HWND hwndChild;  // окно Child Window
HWND hwndTb;     // окно Toolbar
HWND hwndSb;     // окно Statusbar

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpszCmdLine, int nCmdShow)
{
  MSG  msg;          // структура для работы с сообщениями

  hInst = hInstance; // сохраняем идентификатор приложения

  if(hPrevInstance)  // может быть запущена 
    return FALSE;    // только одна копия приложения

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  // Создаем главное окно приложения - Frame Window
  hwndFrame = CreateWindow(
    szFrameClassName,    // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT, 0,    // задаем размеры и расположение
    CW_USEDEFAULT, 0,    // окна, принятые по умолчанию
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные параметры

  // Если создать окно не удалось, завершаем приложение
  if(!hwndFrame)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwndFrame, nCmdShow);
  UpdateWindow(hwndFrame);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, NULL, 0, 0))
  {
    // Трансляция для MDI-приложения
    if(!TranslateMDISysAccel(hwndClient, &msg))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  // Регистрируем класс для главного окна приложения
  // (т. е. для окна Frame Window)
  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = "APP_MENU";
  wc.style         = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc   = (WNDPROC)FrameWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon(hInstance, "APP_ICON");
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  wc.lpszClassName = (LPSTR)szFrameClassName;
  aWndClass = RegisterClass(&wc);

  if(!aWndClass)
    return FALSE;

  // Регистрируем класс окна для дочернего окна Document Window
  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = 0;
  wc.style         = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc   = (WNDPROC)ChildWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon(hInstance, "APPCLIENT_ICON");
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szChildClassName;
  aWndClass = RegisterClass(&wc);

  if(!aWndClass)
    return FALSE;

  // Регистрируем класс для окна Toolbar
  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = 0;
  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC)TbWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = NULL;
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szTbClassName;
  aWndClass = RegisterClass(&wc);

  if(!aWndClass)
    return FALSE;

  // Регистрируем класс для окна Statusbar
  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = 0;
  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC)SbWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = NULL;
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szSbClassName;
  aWndClass = RegisterClass(&wc);

  if(!aWndClass)
    return FALSE;

  return TRUE;
}

// =====================================
// Функция FrameWndProc
// =====================================

LRESULT CALLBACK _export
FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Структура для создания окна Client Window
  CLIENTCREATESTRUCT clcs;

  // Структура для создания дочернего окна Document Window
  MDICREATESTRUCT    mdics;

  switch (msg)
  {
    // Устанавливаем размеры окон Toolbar, Statusbar.
    // Уменьшаем размер окна Client Window
    case WM_SIZE:
    {
      // Располагаем окно Toolbar в верхней части
      // окна Frame Window
      MoveWindow(hwndTb,
        0,                      // x-координата
        0,                      // y-координата
        LOWORD(lParam),         // ширина
        TBAR_SIZE,              // высота
        TRUE);                  // требуется перерисовка окна

      // Располагаем окно Statusbar в нижней части
      // окна Frame Window
      MoveWindow(hwndSb,
        0,                      // x-координата
        HIWORD(lParam) - SBAR_SIZE,  // y-координата
        LOWORD(lParam),         // ширина
        SBAR_SIZE,              // высота
        TRUE);                  // требуется перерисовка окна

      // Если окно не свернуто в пиктограмму и его
      // идентификатор отличен от нуля, вызываем
      // функцию MoveWindow
      if(wParam != SIZEICONIC && hwndClient)
      {
        MoveWindow(hwndClient,
          0,                      // x-координата
          TBAR_SIZE,              // y-координата
          LOWORD(lParam),         // ширина
          HIWORD(lParam) - (TBAR_SIZE + SBAR_SIZE), // высота
          TRUE);                  // требуется перерисовка окна

        // После уменьшения размеров окна нельзя
        // отдавать сообщение WM_SIZE функции DefFrameProc,
        // так как иначе размеры будут восстановлены
        return 0;
      }
      break;
    }


    // При создании окна Frame Window создаем
    // окно Client Window, внутри которого будут создаваться
    // дочерние окна Document Window
    case WM_CREATE:
    {
      // Получаем и сохраняем в структуре clcs идентификатор
      // временного меню "Window". Так как это меню второе слева,
      // его позиция равна 1 (меню "File" имеет позицию 0) 
      clcs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 1);

      // Идентификатор первого дочернего окна Document Window
      clcs.idFirstChild = 500;
      
      // Создаем окно Client Window 
      hwndClient = CreateWindow(
        "MDICLIENT",    // имя класса окна
        NULL,           // заголовок окна
        WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | // стиль окна
          WS_HSCROLL | WS_VSCROLL,  
        0, 0, 0, 0,
        hwnd,           // идентификатор родительского окна
        (HMENU)1,       // идентификатор меню
        hInst,          // идентификатор приложения
        (LPSTR)&clcs);  // указатель на дополнительные параметры

      // Создаем окно Toolbar
      hwndTb = CreateWindow(
        szTbClassName,  // имя класса окна
        NULL,           // заголовок окна
        WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, // стиль окна
        0, 0, 0, 0,
        hwnd,           // идентификатор родительского окна
        (HMENU)2,       // идентификатор меню
        hInst,          // идентификатор приложения
        NULL);  // указатель на дополнительные параметры

      // Создаем окно Statusbar
      hwndSb = CreateWindow(
        szSbClassName,  // имя класса окна
        NULL,           // заголовок окна
        WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, // стиль окна
        0, 0, 0, 0,
        hwnd,           // идентификатор родительского окна
        (HMENU)3,       // идентификатор меню
        hInst,          // идентификатор приложения
        NULL);  // указатель на дополнительные параметры

      break;
    }

    // Обработка сообщений от главного меню приложения
    case WM_COMMAND:
    {
      switch (wParam)
      {
        // Создание нового окна Document Window
        case CM_FILENEW:
        {
          // Заполняем структуру MDICREATESTRUCT
          mdics.szClass = szChildClassName;   // класс окна
          mdics.szTitle = "MDI Child Window"; // заголовок окна
          mdics.hOwner  = hInst;              // идентификатор приложения 
          mdics.x       = CW_USEDEFAULT;      // размеры окна Document Window
          mdics.y       = CW_USEDEFAULT;
          mdics.cx      = CW_USEDEFAULT;
          mdics.cy      = CW_USEDEFAULT;
          mdics.style   = 0;                  // дополнительные стили
          mdics.lParam  = NULL;               // 32-битное значение

          // Посылаем сообщение WM_MDICREATE окну Client Window.
          // В результате будет создано новое окно Document Window
          hwndChild = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM)&mdics);

          break;
        }

        // Размещение окон Document Window рядом друг с другом
        case CM_WINDOWTILE:
        {
          SendMessage(hwndClient, WM_MDITILE, 0, NULL);
          break;
        }

        // Размещение окон Document Window с перекрытием
        case CM_WINDOWCASCADE:
        {
          SendMessage(hwndClient, WM_MDICASCADE, 0, NULL);
          break;
        }

        // Размещение пиктограм минимизированых окон Document Window
        // в нижней части окна Client Window
        case CM_WINDOWICONS:
        {
          SendMessage(hwndClient, WM_MDIICONARRANGE, 0, NULL);
          break;
        }

        // Уничтожение всех окон Document Window
        case CM_WINDOWCLOSEALL:
        {
          HWND hwndTemp;

          // Скрываем окно Client Window для того чтобы избежать
          // многократной перерисовки окон Document Window во время их уничтожения
          ShowWindow(hwndClient, SW_HIDE);

          for(;;)
          {
            // Получаем идентификатор дочернего окна для окна Client Window
            hwndTemp = GetWindow(hwndClient, GW_CHILD);
            // Если дочерних окон больше нет, выходим из цикла
            if(!hwndTemp)
              break;

            // Пропускаем окна-заголовки
            while(hwndTemp && GetWindow(hwndTemp, GW_OWNER))
              hwndTemp = GetWindow(hwndTemp, GW_HWNDNEXT);

            // Удаляем дочернее окно Document Window
            if(hwndTemp)
              SendMessage(hwndClient, WM_MDIDESTROY, (WPARAM)hwndTemp, NULL);
            else
              break;
          }

          // Отображаем окно Client Window
          ShowWindow(hwndClient, SW_SHOW);
          break;
        }

        case CM_HELPABOUT:
        {
	       MessageBox(hwnd,
            "Приложение MDIAPP\n(C) Фролов А.В., 1995",
	         "Simple MDI Application", MB_OK | MB_ICONINFORMATION);
          break;
        }

        // Завершаем работу приложения
        case CM_FILEEXIT:
        {
          DestroyWindow(hwnd);
          break;
        }

        default:
          break;
      }

      // Определяем идентификатор активного окна Document Window
      HWND hwndChild =
        (HWND)LOWORD(SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0l));

      // Если это окно, посылаем ему сообщение WM_COMMAND
      if(IsWindow(hwndChild))
        SendMessage(hwndChild, WM_COMMAND, wParam, lParam);

      return DefFrameProc(hwnd, hwndClient, msg, wParam, lParam);
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      break;
    }

    default:
      break;
  }
  return DefFrameProc(hwnd, hwndClient, msg, wParam, lParam);
}

// =====================================
// Функция ChildWndProc
// =====================================

LRESULT CALLBACK _export
ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;

  switch (msg)
  {
    case WM_PAINT:
    {
      hdc = BeginPaint(hwnd, &ps);
      GetClientRect(hwnd, &rc);

      DrawText(hdc, "Child Window", -1, &rc,
        DT_SINGLELINE | DT_CENTER | DT_VCENTER);

      EndPaint(hwnd, &ps);
    }

    default:
      break;
  }
  return DefMDIChildProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция TbWndProc
// =====================================

LRESULT CALLBACK _export
TbWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;

  switch (msg)
  {
    case WM_PAINT:
    {
      hdc = BeginPaint(hwnd, &ps);
      GetClientRect(hwnd, &rc);

      DrawText(hdc, "Toolbar Window", -1, &rc,
        DT_SINGLELINE | DT_CENTER | DT_VCENTER);

      EndPaint(hwnd, &ps);
    }

    default:
      break;
  }
  return DefMDIChildProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция SbWndProc
// =====================================

LRESULT CALLBACK _export
SbWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;

  switch (msg)
  {
    case WM_PAINT:
    {
      hdc = BeginPaint(hwnd, &ps);
      GetClientRect(hwnd, &rc);

      DrawText(hdc, "Statusbar Window", -1, &rc,
        DT_SINGLELINE | DT_CENTER | DT_VCENTER);

      EndPaint(hwnd, &ps);
    }

    default:
      break;
  }
  return DefMDIChildProc(hwnd, msg, wParam, lParam);
}
