// <copyright file="Layouts.cs" company="Western Electric Coordinating Council">

// Copyright (c) 2010 All Right Reserved

// </copyright>

// <author>Jeff Martin</author>

// <email>jmartin@bridgeenergygroup.com</email>

// <date>31-Jul-2010</date>

// <summary>Anonymous, singleton class containing functions for saving,

// parsing and loading procbook layout configuration files.</summary>

 

namespace AddInPB_LayoutMgr

{

    using System;

    using System.IO;

    using System.Text;

    using System.Collections;

    using System.Collections.Generic;

    using System.Linq;

    using PBObjLib;

    using PBSymLib;

 

    using System.ComponentModel;

    using System.Windows.Forms;

    using System.Runtime.InteropServices;

 

    class Layouts

    {

        // processbook application

        private PBObjLib.Application app = null;

 

        // store keys that refer to .pdi displays

        string sDisplay = string.Empty;

 

        // store the Procbook main window keys

        string sProcbook = string.Empty;

 

        // string for storing exception messages

        public string sException = string.Empty;

 

        // generic bool

        private static bool bBool;

 

        #if DEBUG

          // generic DialogResult

          private static DialogResult Result;

        #endif

 

        // Dictionary object for storing KEY=VALUE; pairs

        private static Dictionary<string, string> dictLayout = new Dictionary<string, string>();

 

        // list of all child windows titles

        private static List<string> ChildTitles = new List<string>();

 

        // list of all child windows handles

        private static List<System.IntPtr> ChildHandles = new List<System.IntPtr>();

 

        // struct for GetWindowRect() / GetClientRect()

        [StructLayout(LayoutKind.Sequential)]

        public struct RECT

        {

            public Int32 Left;        // x position of upper-left corner

            public Int32 Top;         // y position of upper-left corner

            public Int32 Right;       // x position of lower-right corner

            public Int32 Bottom;      // y position of lower-right corner

 

            public Int32 Width

            {

                get { return Right - Left; }

            }

            public Int32 Height

            {

                get { return Bottom - Top; }

            }

        }

 

        // get the total height of all toolbars

        public static Int32 ToolBarHeight = 0;

 

        // map child screen coordinates to procbook client area coordinates.

        Int32 ChildTop = 0;

        Int32 ChildLeft = 0;

 

        /////////////////////////////////////////////////////////////////////////////////////////

        /////////////////////////// Win API prototypes and structs //////////////////////////////

        // Win API functions for Window Handles

        public delegate bool EnumChildProc(System.IntPtr hwnd, int lParam);

 

        // Win API for looping through child windows

        [DllImport("user32.dll", SetLastError = true)]

        internal static extern int EnumChildWindows(System.IntPtr hWndParent, EnumChildProc callback,

            int lParam);

 

        // Win API function for getting the window label

        [DllImport("user32.dll", SetLastError = true)]

        internal static extern int GetWindowText(System.IntPtr hWnd, StringBuilder title, int count);

 

        // Win API function for sending system messages to a window

        [DllImport("user32.dll", SetLastError = true)]

        internal static extern int SendMessage(System.IntPtr hWnd, int Msg, int wParam, StringBuilder sb);

 

        // Win API constant for getting a windows text

        private static int WM_GETTEXT = 0x000D;

 

        // Win API constant for getting a windows text

        private static uint SWP_SHOWWINDOW = 0x0040;

 

        // Win API constands for sending a Restore command to a window

        //private static int WM_SYSCOMMAND = 0x0112;

        //private static int SC_RESTORE = 0xF120;

 

        // Win API function for setting window position

        [DllImport("user32.dll", SetLastError = true)]

        internal static extern bool SetWindowPos(System.IntPtr hWnd, System.IntPtr hWndInsertAfter,

            int x, int y, int cx, int cy, uint uFlags);

 

        [DllImport("user32.dll", CharSet = CharSet.Auto)]

        internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

 

        // Win API function for getting a windows position

        [DllImport("user32.dll")]

        [return: MarshalAs(UnmanagedType.Bool)]

        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

 

        // Win API function for getting the size of a windows client area

        [DllImport("user32.dll")]

        [return: MarshalAs(UnmanagedType.Bool)]

        static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

 

 

        /////////////////////////////////////////////////////////////////////////////////////////

        // create a new Layouts object and store procbook app reference

        public Layouts(PBObjLib.Application app)

        {

            this.app = app;

 

            // find the start of the client area

            //commandBar = this.app.CommandBars.Item("Layout Manager");

            //ToolBarHeight = commandBar.Top + commandBar.Height;

 

            // find the start of the client area

            // last commandbar top + height

            Int32 iHeight = 0;

            foreach (PBObjLib.PBCommandBar cmdbar in this.app.CommandBars)

            {

                if (cmdbar.Visible == true)

                {

                    if (cmdbar.Top >= ToolBarHeight)

                    {

                        ToolBarHeight = cmdbar.Top;

                        iHeight = cmdbar.Height;

                    }

 

                }

            }

            ToolBarHeight = ToolBarHeight + iHeight;

 

        }

 

        ~Layouts()

        {

        }

 

        ////////////////////////////////////////////////////////////////////

        // load layouts from file

        // J. Martin, jmartin@bridgeenerygroup.com

        ///////////////////////////////////////////////////////////////////

        public Boolean LoadFromFile(string sFile)

        {

 

            // Main Window Handle from the thread

            System.IntPtr hMainWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;

 

            // define a return object for Displays.Open function

            PBObjLib.Display pbDisplay;

 

            // define a return object for ProcBooks.Open function

            PBObjLib.ProcBook pbBook = (PBObjLib.ProcBook)null;

 

            // parse the layout file

            try

            {

                // Create an instance of StreamReader to read from a file.

                // The using statement also closes the StreamReader.

                using (StreamReader sr = new StreamReader(sFile))

                {

                    string line;

                    string sParsed = string.Empty;

                    string sValue = string.Empty;

                    string sKey = string.Empty;

 

                    // Read lines from the the until EOF

                    while ((line = sr.ReadLine()) != null)

                    {

                        // ignore comment lines

                        if (line[0] != '#')

                        {

                            // store key=value; pairs in dictLayout

                            for (int x = 0; x < line.Length; x++)

                            {

                                switch (line[x])

                                {

                                    case '=':

                                        sKey = sParsed.Trim();

                                        sParsed = string.Empty;

                                        break;

                                    case ';':

                                        sValue = sParsed.Trim();

                                        sParsed = string.Empty;

 

                                        // store the key=value; pair

                                        dictLayout.Add(sKey, sValue);

                                        //MessageBox.Show(sKey + "\n" + sValue);

                                        break;

                                    default:

                                        sParsed = sParsed + line[x];

                                        break;

                                }

                            } // for (int x = 0; x < line.Length; x++)

 

 

                            ////////// PROCBOOK ///////////////////////////////////////

                            sProcbook = string.Empty;

                            if (dictLayout.TryGetValue("PARENT", out sProcbook))

                            {

                                // reposition the processbook application window

                            #if DEBUG

                                Result = MessageBox.Show("procbook: \n" +

                                             "\nLeft: " + Convert.ToInt32(dictLayout["LEFT"])  +

                                             "\nTop: " + Convert.ToInt32(dictLayout["TOP"])   +

                                             "\nWidth: " + Convert.ToInt32(dictLayout["WIDTH"]) +

                                             "\nHeight: " + Convert.ToInt32(dictLayout["HEIGHT"]),

                                             "Positioning Procbook.." );

                            #endif

                                SetWindowPos(hMainWnd, System.IntPtr.Zero,

                                             Convert.ToInt32(dictLayout["LEFT"]),

                                             Convert.ToInt32(dictLayout["TOP"]),

                                             Convert.ToInt32(dictLayout["WIDTH"]),

                                             Convert.ToInt32(dictLayout["HEIGHT"]),

                                             SWP_SHOWWINDOW);

                            }

 

                            ///////// .pdi DISPLAYS /////////////////////////////////////

                            sDisplay = string.Empty;

                            if (dictLayout.TryGetValue("DISPLAY", out sDisplay))

                            {

                            #if DEBUG

                                Result = MessageBox.Show("DISPLAY: \n" + dictLayout["DISPLAY"] +

                                             "\nLeft: " + Convert.ToInt32(dictLayout["LEFT"])  +

                                             "\nTop: " + Convert.ToInt32(dictLayout["TOP"])   +

                                             "\nWidth: " + Convert.ToInt32(dictLayout["WIDTH"]) +

                                             "\nHeight: " + Convert.ToInt32(dictLayout["HEIGHT"]) +

                                             "\nZoom: " + Convert.ToInt32(dictLayout["ZOOM"]),

                                             "Loading DISPLAY..");

                            #endif

                                // open the procbook DISPLAY and position/size it..

                                pbDisplay = app.Displays.Open(dictLayout["DISPLAY"], 1);

                                if (pbDisplay != null)

                                {

                                    pbDisplay.Left = Convert.ToInt32(dictLayout["LEFT"]);

                                    pbDisplay.Top = Convert.ToInt32(dictLayout["TOP"]);

                                    pbDisplay.Width = Convert.ToInt32(dictLayout["WIDTH"]);

                                    pbDisplay.Height = Convert.ToInt32(dictLayout["HEIGHT"]);

                                    pbDisplay.Zoom = Convert.ToInt32(dictLayout["ZOOM"]);

                                }

                            }

 

                            ///////// .piw WORKBOOKS /////////////////////////////////////

                            sDisplay = string.Empty;

                            if (dictLayout.TryGetValue("WORKBOOK", out sDisplay))

                            {

                            #if DEBUG

                                Result = MessageBox.Show("WORKBOOK: \n" + dictLayout["WORKBOOK"] +

                                             "\nLeft: " + Convert.ToInt32(dictLayout["LEFT"])   +

                                             "\nTop: " + Convert.ToInt32(dictLayout["TOP"])    +

                                             "\nWidth: " + Convert.ToInt32(dictLayout["WIDTH"])  +

                                             "\nHeight: " + Convert.ToInt32(dictLayout["HEIGHT"]),

                                             "Loading WORKBOOK..");

                            #endif

                                // open the WORKBOOK first before any Entries..

                                pbBook = (PBObjLib.ProcBook)app.ProcBooks.Open(dictLayout["WORKBOOK"], 1);

 

                                // size the WORKBOOK

                                bBool = MoveWindow(GetWindowHandle(pbBook.Path.Substring(pbBook.Path.LastIndexOf("\\") + 1)),

                                             Convert.ToInt32(dictLayout["LEFT"]),

                                             Convert.ToInt32(dictLayout["TOP"]),

                                             Convert.ToInt32(dictLayout["WIDTH"]),

                                             Convert.ToInt32(dictLayout["HEIGHT"]),

                                             true);

                            }

 

                            ///////// workbook ENTRY /////////////////////////////////////

                            sDisplay = string.Empty;

                            if (dictLayout.TryGetValue("ENTRY", out sDisplay))

                            {

                            #if DEBUG

                                Result = MessageBox.Show("ENTRY: \n" + dictLayout["ENTRY"]     +

                                             "\nLeft: " + Convert.ToInt32(dictLayout["LEFT"])  +

                                             "\nTop: " + Convert.ToInt32(dictLayout["TOP"])   +

                                             "\nWidth: " + Convert.ToInt32(dictLayout["WIDTH"]) +

                                             "\nHeight: " + Convert.ToInt32(dictLayout["HEIGHT"]),

                                             "Loading ENTRY..");

                            #endif

                                // open the ENTRY

                                bBool = pbBook.Entries.Item(dictLayout["ENTRY"]).Execute(true); 

 

                                // size the ENTRY

                                bBool = MoveWindow(GetWindowHandle(dictLayout["ENTRY"]),

                                             Convert.ToInt32(dictLayout["LEFT"]),

                                             Convert.ToInt32(dictLayout["TOP"]),

                                             Convert.ToInt32(dictLayout["WIDTH"]),

                                             Convert.ToInt32(dictLayout["HEIGHT"]),

                                             true);

                            }

 

                            // clear the dictionary (no dup keys)

                            dictLayout.Clear();

 

                        } // if (line[0] != '#')

                    }     // while ((line = sr.ReadLine()) != null)

                }         // using (StreamReader sr = new StreamReader(sFile))

                

            }

            catch (Exception ex)

            {

                // store the exeption message

                sException = ex.Message;

                return false;

            }

 

        return true;

        }

 

        ///////////////////////////////////////////////////////////////////

        // returns the window handle for the supplied window title

        // J. Martin, jmartin@bridgeenerygroup.com

        ///////////////////////////////////////////////////////////////////

        private static System.IntPtr GetWindowHandle(string sTitle)

        {

            // load a list of child window titles to search

            bBool = LoadChildWindows();

            foreach (string cTitle in ChildTitles)

            {   // this also captures any copies or 'read only' annotated displays

                if (cTitle.StartsWith(sTitle, StringComparison.OrdinalIgnoreCase))

                {

                    return ChildHandles[ChildTitles.IndexOf(cTitle)];

                }

            }

            return System.IntPtr.Zero;

        }

 

        ///////////////////////////////////////////////////////////////////

        // load a list of all child window titles and handles

        // J. Martin, jmartin@bridgeenerygroup.com

        ///////////////////////////////////////////////////////////////////

        private static bool LoadChildWindows()

        {

            // init lists

            ChildTitles.Clear();

            ChildHandles.Clear();

 

            // Main Window Handle for this thread (procbook.exe)

            System.IntPtr hMainWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;

 

            // bind callback EnumChildWindow_Handle to user32.dll.EnumChildWindows()

            EnumChildProc EnumChildWindow = new EnumChildProc(EnumChildWindow_Handle);

            EnumChildWindows(hMainWnd, EnumChildWindow, 0);

 

            return true;

        }

 

        ///////////////////////////////////////////////////////////////////

        // this function is a callback function, called on each child window by the

        // WinAPI function EnumChildWindows. It loads the ChildWindows list with elements

        // J. Martin, jmartin@bridgeenerygroup.com

        ///////////////////////////////////////////////////////////////////

        private static bool EnumChildWindow_Handle(System.IntPtr hWnd, int lparam)

        {

            try

            {

                StringBuilder s = new StringBuilder(51);

 

                // retrieve the window label for the child window

                GetWindowText(hWnd, s, 50);

 

                // query windows if procbook doesn't respond to this handle

                if (s.ToString() == string.Empty)

                {

                    SendMessage(hWnd, WM_GETTEXT, 50, s);

                }

 

                //only add non-empty strings to the lists

                if (s.ToString() != string.Empty && s.ToString() != "")

                {

                    // add the child title and handle to lists

                    ChildTitles.Add(s.ToString());

 

                    // add the child window handle. List<>.Add() method must produce

                    // identical sequential indexes for this work (looks like it does)..

                    // To find the childs handle from it's title use:

                    // System.IntPtr Handle = ChildHandles[ChildTitles.IndexOf("Title")];

                    ChildHandles.Add(hWnd);

                }

                return true;

            }

            catch

            {

                throw;

            }

        }

 

        ////////////////////////////////////////////////////////////////////

        // save layouts in a file

        //

        // This function builds a list of procbook child windows.

        // It searches this list for any open workbooks, workbook entries

        // or pdi displays. It calculates their client area coordinates

        // and adds them to a file.

        // J. Martin, jmartin@bridgeenerygroup.com

        ///////////////////////////////////////////////////////////////////

        public Boolean SaveToFile(string sFile)

        {

 

            //foreach (PBObjLib.ProcBook pbBook1 in app.ProcBooks)

            //{

            //    foreach (PBObjLib.Entry pbEntry1 in pbBook1.Entries)

            //    {

            //        disp1 = (PBObjLib.Display)pbEntry1;

            //        Result = MessageBox.Show("Top: " + disp1.Top + "\n" +

            //                                 "Left: " + disp1.Left + "\n" +

            //                                 "Width: " + disp1.Width + "\n" +

            //                                 "Height: " + disp1.Height + "\n\n" +

            //                                 "ViewTop: " + disp1.ViewTop + "\n" +

            //                                 "ViewLeft: " + disp1.ViewLeft + "\n" +

            //                                 "ViewHeight: " + disp1.ViewHeight + "\n" +

            //                                 "ViewWidth: " + disp1.ViewWidth + "\n\n" +

            //                                 "Zoom: " + disp1.Zoom + "\n");

 

            //    }

            //}

 

 

            // Main Window Handle from the thread

            System.IntPtr hMainWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;        

 

            // struct for main Procbook parent window

            RECT rectProcBook;

 

            // struct for main Procbook client area

            RECT rectClientArea;

 

            // struct for GetWindowRect()

            RECT rectChild;

 

            // Create the Layout ini file if needed.

            FileStream fs = new FileStream(sFile,

                                           FileMode.Create,

                                           FileAccess.Write,

                                           FileShare.Write);

            fs.Close();

            StreamWriter sw = new StreamWriter(sFile,

                                               true,

                                               Encoding.ASCII);

            sw.WriteLine("##################################################################");

            sw.WriteLine("# ProcessBook Layout Configuration File");

            sw.WriteLine("#");

            sw.WriteLine("# Created on: " + Environment.MachineName + " (" + Environment.UserName + ")");

            sw.WriteLine("# Created by: " + app.FullName );

            sw.WriteLine("# Created at: " + DateTime.Now);

            sw.WriteLine("#");

            sw.WriteLine("# Do not edit directly.");

            sw.WriteLine("#################################################################");

 

            // save the Main Processbook window position/size and its client area

            bBool = GetWindowRect(hMainWnd, out rectProcBook);

            bBool = GetClientRect(hMainWnd, out rectClientArea);

 

            sw.WriteLine("PARENT=PROCBOOK" +

                     "; LEFT=" + Convert.ToString(rectProcBook.Left) +

                     "; TOP=" + Convert.ToString(rectProcBook.Top) +

                     "; WIDTH=" + Convert.ToString(rectProcBook.Width) +

                     "; HEIGHT=" + Convert.ToString(rectProcBook.Height) + ";");

 

            // load a list of child window titles and handles

            bBool = LoadChildWindows();

 

            // find and store all the open workbook size/positions.

            foreach (string sTitle in ChildTitles)

            {  

                // get all the possible entries in all open workbooks.

                foreach (PBObjLib.ProcBook pbBook in app.ProcBooks)

                {

                    // are any of them a procbook child handle ?

                    if (sTitle.StartsWith(pbBook.Path.Substring(pbBook.Path.LastIndexOf("\\") + 1),

                                          StringComparison.OrdinalIgnoreCase))

                    {

                        // get the child windows screen coordinates

                        bBool = GetWindowRect(ChildHandles[ChildTitles.IndexOf(sTitle)], out rectChild);

 

                        // GetClientRect() reports all of the client area including that used

                        // by toolbar. GetWindowRect() gives screen coordinates (not client area).

                        // The following arithemtic is intended to offset screen coordinates so

                        // that they equal the procbook's child client area coordinates.

                        ChildTop = rectChild.Top - ((rectProcBook.Height - rectClientArea.Height) +

                                                    (rectProcBook.Top + ToolBarHeight) - 1);

 

                        if (ChildTop < 0)

                        {

                            ChildTop = ChildTop * -1;

                        }

 

                        // 6 is the width of the procbook main windows border (a guess)..

                        ChildLeft = (rectChild.Left - rectProcBook.Left) - 6;

                        if (ChildLeft < 0)

                        {

                            ChildLeft = ChildLeft * -1;

                        }

 

                        #if DEBUG

                            Result = MessageBox.Show("WORKBOOK=" + pbBook.Path +

                                                    "; LEFT=" + Convert.ToString(ChildLeft) +

                                                    "; TOP=" + Convert.ToString(ChildTop) +

                                                    "; WIDTH=" + Convert.ToString(rectChild.Width) +

                                                    "; HEIGHT=" + Convert.ToString(rectChild.Height) + ";"

                                                    ,"Found WORKBOOK:");

                        #endif

                        sw.WriteLine("WORKBOOK=" + pbBook.Path +

                                     "; LEFT=" + Convert.ToString(ChildLeft) +

                                     "; TOP=" + Convert.ToString(ChildTop) +

                                     "; WIDTH=" + Convert.ToString(rectChild.Width) +

                                     "; HEIGHT=" + Convert.ToString(rectChild.Height) + ";");

 

                        // find and store all the open entries in this workbook

                        // the order is important since LoadFromFile() will first

                        // create the needed workkbook object and then execute Entries from it.

                        foreach (string sTitle1 in ChildTitles)

                        {

                            foreach (PBObjLib.Entry pbEntry in pbBook.Entries)

                            {

                                if (sTitle1.StartsWith(pbEntry.Label, StringComparison.OrdinalIgnoreCase))

                                {

                                    // store the child window position/size (see comments for Workbooks..)

                                    bBool = GetWindowRect(ChildHandles[ChildTitles.IndexOf(sTitle1)], out rectChild);

 

                                    ChildTop = rectChild.Top - ((rectProcBook.Height - rectClientArea.Height) +

                                                                (rectProcBook.Top + ToolBarHeight) - 1);

                                    if (ChildTop < 0)

                                    {

                                        ChildTop = ChildTop * -1;

                                    }

 

                                    ChildLeft = (rectChild.Left - rectProcBook.Left) - 6;

                                    if (ChildLeft < 0)

                                    {

                                        ChildLeft = ChildLeft * -1;

                                    }

                                   

                                    #if DEBUG               

                                        Result = MessageBox.Show("ENTRY=" + pbEntry.Label +

                                                                "; LEFT=" + Convert.ToString(ChildLeft) +

                                                                "; TOP=" + Convert.ToString(ChildTop) +

                                                                "; WIDTH=" + Convert.ToString(rectChild.Width) +

                                                                "; HEIGHT=" + Convert.ToString(rectChild.Height) + ";"

                                                                ,"Found ENTRY:");

                                    #endif

                                    sw.WriteLine("ENTRY=" + pbEntry.Label +

                                                 "; LEFT=" + Convert.ToString(ChildLeft) +

                                                 "; TOP=" + Convert.ToString(ChildTop) +

                                                 "; WIDTH=" + Convert.ToString(rectChild.Width) +

                                                 "; HEIGHT=" + Convert.ToString(rectChild.Height) + ";");

                                }

                            }

 

                        }  // foreach (string sTitle1 in ChildTitles)

                    }      // if (sTitle.StartsWith(pbBook.Path.Substring(pbBook.Path

                }          // foreach (PBObjLib.ProcBook pbBook in app.ProcBooks)

            }              // foreach (string sTitle in ChildTitles)

 

            // loop thru every pdi display and store their positions/sizes

            foreach (PBObjLib.Display disp in app.Displays)

            {

                // don`t save minimized or maximized displays.

                if (disp.WindowState == PBObjLib.pbWindowState.pbWindowStateNormal)

                {

                    #if DEBUG

                        Result = MessageBox.Show("DISPLAY=" + disp.Path +

                                                "; LEFT=" + Convert.ToString(disp.Left) +

                                                "; TOP=" + Convert.ToString(disp.Top) +

                                                "; WIDTH=" + Convert.ToString(disp.Width) +

                                                "; HEIGHT=" + Convert.ToString(disp.Height) +

                                                "; ZOOM=" + Convert.ToString(disp.Zoom) + ";"

                                                ,"Found DISPLAY:");

                    #endif

                        sw.WriteLine("DISPLAY=" + disp.Path +

                                    "; LEFT=" + Convert.ToString(disp.Left) +

                                    "; TOP=" + Convert.ToString(disp.Top) +

                                    "; WIDTH=" + Convert.ToString(disp.Width) +

                                    "; HEIGHT=" + Convert.ToString(disp.Height) +

                                    "; ZOOM=" + Convert.ToString(disp.Zoom) + ";");

                }

            }

 

            sw.Close();     // close the streamwriter

            return true;

        }

 

    }

}