
/*
-------------------------------------------------------------------------
This file is part of WxWidgetsExtensions library.
-------------------------------------------------------------------------

WxExtLib (WxWidgetsExtensions) library
-----------------------------

COPYRIGHT NOTICE:

WxExtLib library Copyright (c) 2003-2007 Daniel Käps

The WxWidgetsExtensions library and associated documentation files (the
"Software") is provided "AS IS".  The author(s) disclaim all
warranties, expressed or implied, including, without limitation, the
warranties of merchantability and of fitness for any purpose.  The
author(s) assume no liability for direct, indirect, incidental,
special, exemplary, or consequential damages, which may result from
the use of or other dealings in the Software, even if advised of the
possibility of such damage.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this Software, to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

 1. The origin of this source code must not be misrepresented.
 2. Altered versions must be plainly marked as such and must not be
    misrepresented as being the original source.
 3. This Copyright notice may not be removed or altered from any 
    source or altered source distribution.

End of WxExtLib library Copyright Notice

-------------------------------------------------------------------------
*/

#ifndef _NO_HEADER_INCLUDE

#include "WxExtLibConfig.h"

#if defined(__GNUG__) && (!defined(__APPLE__)) && (!(defined M_NoPragmaInterface))
#   pragma implementation "MessageExtDialog.h"
#endif

// For compilers that support precompilation, includes "wx.h".
#include <wx/wxprec.h>

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// include MessageExtDialog.h with definitions for both, C and C++
// interface:
#define M_MessageExtDialog_EnableCInterface
#define M_MessageExtDialog_EnableCxxInterface
#include "MessageExtDialog.h"

#include <wx/statline.h>
#include <wx/image.h>
#include <wx/artprov.h>

#include "WxMisc.h"
#include "StatusValidators.h"

#endif // _NO_HEADER_INCLUDE

//-------------------------------------------------------------------------

double wxMessageExtDialog::s_GlobalMessageExtDialogZoom = 1.05;
long wxMessageExtDialog::s_GlobalFlags = wxMessageExtDialog::DisplayColourBar;
wxString wxMessageExtDialog::s_GlobalTitlePostfix;

//=========================================================================

BEGIN_EVENT_TABLE (wxMessageExtDialog, wxExtDialog)
    // EVT_MENU   (wxID_HELP, wxMessageExtDialog::OnHelp)
    EVT_BUTTON (wxID_ANY, wxMessageExtDialog::OnButtonEvent)
    EVT_CHAR   (wxMessageExtDialog::OnChar)
    EVT_KEY_DOWN   (wxMessageExtDialog::OnKeyDown)
END_EVENT_TABLE()

//-------------------------------------------------------------------------

wxMessageExtDialog::wxMessageExtDialog()
{
}

wxMessageExtDialog::wxMessageExtDialog (wxWindow *ParentWindow,
                                        const wxString & MessageString,
                                        const wxString & TitleString)
  : wxExtDialog (getUsedParentWindow (ParentWindow),
                 -1, "" /*TitleString*/,
                 wxDefaultPosition, wxDefaultSize,
                 wxDEFAULT_DIALOG_STYLE | wxWANTS_CHARS)
{
    init (MessageString, TitleString);
}

void wxMessageExtDialog::Create (wxWindow *ParentWindow,
                                 const wxString & MessageString,
                                 const wxString & TitleString)
{
    wxExtDialog::Create (getUsedParentWindow (ParentWindow),
                         -1, "" /* TitleString */,
                         wxDefaultPosition, wxDefaultSize,
                         wxDEFAULT_DIALOG_STYLE | wxWANTS_CHARS);
    
    init (MessageString, TitleString);
}


void wxMessageExtDialog::init (const wxString & MessageString, const wxString & TitleString)
{
    m_IsControlsCreated = FALSE;

    m_TitleString = TitleString;

    m_DefaultButton = NULL;
    m_LastAddedButton = NULL;
    m_LastAddedAutoButtonIdent = FirstAutoButtonIdent - 1;
    m_CurrentButtonIndex = 0;

    m_Style = MessageStyle;

    // m_TextCtrl = NULL;

    //-------------------------------------------------------------------------

    m_StatusMessageTarget = NULL;
    // wxWindow * PanelWnd = this;
    m_PanelSizer = new wxBoxSizer (wxVERTICAL);

    setDialogZoom (s_GlobalMessageExtDialogZoom);

    //-------------------------------------------------------------------------
    m_DialogMessageString = MessageString;

    //-------------------------------------------------------------------------

    m_TextCtrlFont  = createDialogFont(getDialogFontSize(), 
#if defined(__WXMOTIF__)
                                       // wxTextCtrl under Motif seems to ignore requests
                                       // to set a different font, even if the font is of
                                       // type wxMODERN (should be a fixed-pitch font)
                                       wxMODERN /* family */,
#else
                                       wxDEFAULT /* family */,
#endif
                                       wxNORMAL  /* upright/slant/italic */, 
                                       wxNORMAL  /* weight */,
                                       FALSE  /* underlined? */, 
                                       _T("") /* face name */,
                                       wxFONTENCODING_DEFAULT /* wxFONTENCODING_ISO8859_1 */
                                       );
    // create the button font: shall be a little larger than the normal
    // dialog font:
#if defined(__WXMSW__)
    int ButtonFontSize = getDialogFontSize();

    // under MSW, the font created with the bold attribute will appear
    // higher and wider with the normal dialog font size already
    // (scope: the default dialog font, things may be different for other
    // fonts and/or true type fonts)
    // so reduce (nominal) font size by one point
    if (ButtonFontSize > 8 /* DefaultDialogFontSize */)
      --ButtonFontSize;

    m_ButtonFont  = createDialogFont(ButtonFontSize, 
                                     wxDEFAULT /* family */, 
                                     wxNORMAL /* upright/slant/italic */, 
                                     wxBOLD /* weight */,
                                     FALSE /* underlined? */, 
                                     _T("") /* face name */,
                                     wxFONTENCODING_DEFAULT /* wxFONTENCODING_ISO8859_1 */);
#else // e.g. __WXGTK__
    m_ButtonFont  = createDialogFont(getDialogFontSize(), 
                                     wxDEFAULT /* family */, 
                                     wxNORMAL /* upright/slant/italic */,
                                     wxBOLD /* weight */,
                                     FALSE /* underlined? */, 
                                     _T("") /* face name */,
                                     wxFONTENCODING_DEFAULT /* wxFONTENCODING_ISO8859_1 */);
#endif

    m_ButtonFlexGridSizer = new wxFlexGridSizer (1, 0, _B(0), _B(10));

}

//-------------------------------------------------------------------------

void wxMessageExtDialog::setDialogMessage (const wxString & MessageString)
{
    m_DialogMessageString = MessageString;
    m_MessageMultiLineText.setText (m_DialogMessageString);
}

//-------------------------------------------------------------------------

wxMessageExtDialog & wxMessageExtDialog::addStatusTextCtrl ()
{
    if (m_StatusMessageTarget == NULL)
      {
        m_StatusMessageTarget 
          = new wxStatusMessageTarget (createErrorMessageCtrl (& m_ErrorMessageMultiLineText), "");
      }

    return *this;
}

//-------------------------------------------------------------------------

wxMessageExtDialog & wxMessageExtDialog::addButton (const wxString & Label,
                                                int ButtonIdent)
{
    if (m_CurrentButtonIndex > 0)
      {
        m_ButtonFlexGridSizer -> Add (1, 1);
        m_ButtonFlexGridSizer -> AddGrowableCol (m_CurrentButtonIndex * 2 - 1);
      }

    wxSizerAdder ButtonSizerAdder (m_ButtonFlexGridSizer);

    wxButton * Button;
    // WARN don't know if syntax for function pointer conversion is correct
    wxExtDialog::addButton (& Button, ButtonIdent, (wxCommandEventFunction) (& wxMessageExtDialog::OnButtonEvent), 
                            Label, 
                            & ButtonSizerAdder);

    Button -> SetFont (m_ButtonFont);

    int MajorVersionInt = 0;
    int MinorVersionInt = 0;
    int OsSpecifierInt = wxGetOsVersion (& MajorVersionInt, & MinorVersionInt);
    // WinXP: do not set button's background colour because this prevents
    // WinXP from using new WinXP button style (Win version 5.1)
    if (!(OsSpecifierInt == wxWINDOWS_NT 
          && ((MajorVersionInt > 5) 
              || (MajorVersionInt == 5 && MinorVersionInt >= 1))))
      {
        Button -> SetBackgroundColour (wxColour (224, 224, 224));
      }

//     if (!m_LastAddedButton)
//    Button -> SetFocus();

    m_LastAddedButton = Button;
    ++m_CurrentButtonIndex;

    return *this;
}

int wxMessageExtDialog::addButton (const wxString & Label)
{
    int ButtonIdent = ++m_LastAddedAutoButtonIdent;
    addButton (Label, ButtonIdent);

    return ButtonIdent;
}

void wxMessageExtDialog::makeLastAddedButtonDefault ()
{
    if (m_LastAddedButton)
      {
        m_DefaultButton = m_LastAddedButton;
      }
}

//-------------------------------------------------------------------------

void wxMessageExtDialog::OnOK(wxCommandEvent& Event) 
{
    handleButtonEvent (Event);
}

void wxMessageExtDialog::OnCancel(wxCommandEvent& Event)
{
    handleButtonEvent (Event);
}

void wxMessageExtDialog::OnButtonEvent(wxCommandEvent& Event)
{
    handleButtonEvent (Event);
}

void wxMessageExtDialog::handleButtonEvent (wxCommandEvent& Event)
{
    // events with event-id == wxID_CANCEL will be accepted
    // only if the message dialog has a cancel button, or if the 
    // message dialog only contains a single OK button 
    // - otherwise the meaning of pressing the <Esc> button or clicking
    //   the window-close icon of the dialog would be unclear to the user 
    // NOTE 
    // - OnCancel() is generated automatically e.g. when 
    //   pressing the <Esc> key even if the dialog doesn't have a 
    //   Cancel button

    if (Event.GetId() == wxID_CANCEL)
      {
        wxWindow * CancelButton = FindWindowById (wxID_CANCEL, this);
        wxWindow * OkayButton = FindWindowById (wxID_OK, this);
    
        if (CancelButton != NULL            
            || (m_CurrentButtonIndex == 1 && 
                OkayButton != NULL))
          {
            if (m_CurrentButtonIndex == 1 && OkayButton != NULL)
              {
                // modify to event object to simulate that the only
                // available button was pressed:
                Event.SetId (OkayButton -> GetId());
                Event.SetEventObject (OkayButton);
              }
          }
        else
          {
            // ignore event:
            return;
          }
      }

    int ButtonIdent = Event.GetId(); // Event.m_id
    if (ButtonIdent == wxID_OK)
      {
        // WARN since wxWidgets 2.8 or so, OnOK() doesn't exist enymore, what
        // can we do, except for disabling OnOK()?
        // - solution is to provide our own OnOK(), OnCancel() by wxExtDialog

        wxExtDialog::OnOK (Event);
        // // EndModal (wxID_OK);
      }
    else if (ButtonIdent == wxID_CANCEL)
      {
        wxExtDialog::OnCancel (Event);
      }
    else
      {
        EndModal (ButtonIdent);
      }
}

//-------------------------------------------------------------------------

// BUG for whatever reason, the OnChar() and OnKeyDown() message handlers
// are never called
void wxMessageExtDialog::OnChar (wxKeyEvent & KeyEvent)
{
    wxButton * Button = NULL; // TEST findButtonForKey (KeyEvent.KeyCode());

    if (Button != NULL)
      {
        if (Button -> IsEnabled())
          {
            // we must generate a command event to simulate the button click:
            wxCommandEvent CommandEvent (wxEVT_COMMAND_BUTTON_CLICKED, Button -> GetId());
            CommandEvent.SetEventObject (Button);
            // Button -> InitCommandEvent (CommandEvent);
            Button -> GetEventHandler() -> ProcessEvent (CommandEvent);
          }
      }
    else
      {
        KeyEvent.Skip ();
      }
}

void wxMessageExtDialog::OnKeyDown (wxKeyEvent & KeyEvent)
{
    KeyEvent.Skip ();
}

//-------------------------------------------------------------------------

int wxMessageExtDialog::ShowModal()
{
    if (!m_IsControlsCreated)
      {
        createControls ();
        m_IsControlsCreated = true;
      }

    return wxExtDialog::ShowModal();
}

//-------------------------------------------------------------------------

void wxMessageExtDialog::createControls()
{
    // wxColour BackgroundColor (192, 192, 192);
    // this -> SetBackgroundColour (BackgroundColor);
    wxColour BackgroundColor = this -> GetBackgroundColour ();

    //-------------------------------------------------------------------------
    // create colour bar if required

    int Style = (m_Style & StyleMask);
    bool IsColourBar = (((s_GlobalFlags & DisplayColourBar) != 0) 
                        && (Style != MessageStyle));
    bool IsImage = (((s_GlobalFlags & DisplayImage) != 0)
                    && (Style != MessageStyle));
                    
    wxBoxSizer * GroupAllBoxSizer = new wxBoxSizer (wxHORIZONTAL);
    m_PanelSizer -> Add (GroupAllBoxSizer, 0, wxEXPAND, _B(0));

    if (IsColourBar)
      {
        int Style = m_Style & StyleMask;

        wxColour BarColour = wxColour (255, 255, 255);
        if (Style == InformationStyle)
          BarColour = wxColour (255, 255, 255);
        else if (Style == WarningStyle)
          BarColour = wxColour (128, 160, 32);
        else if (Style == ErrorStyle)
          BarColour = wxColour (160, 64, 64);
        else if (Style == CriticalErrorStyle)
          BarColour = wxColour (192, 64, 96);
        else if (Style == QuestionStyle)
          BarColour = wxColour (32, 160, 160);

        wxWindow * Window = new wxColourBarWindow (this, -1, BarColour);

        GroupAllBoxSizer -> Add (Window, 0, wxEXPAND, _B(0));
        GroupAllBoxSizer -> SetItemMinSize (Window, _B(20), _B(0));
        GroupAllBoxSizer -> Add (new wxStaticLine (this, -1, 
                                                   wxDefaultPosition, wxDefaultSize,
                                                   wxVERTICAL), 
                                 0, wxEXPAND, _B(0));

        GroupAllBoxSizer -> Add (_B(5), _B(0));
      }

    // sizer for message text and buttons
    wxBoxSizer * GroupContentBoxSizer = new wxBoxSizer (wxVERTICAL);
    GroupAllBoxSizer -> Add (GroupContentBoxSizer, 0, wxLEFT|wxRIGHT|wxTOP|wxEXPAND, _B(14));

    wxBoxSizer * GroupSubContentBoxSizer = new wxBoxSizer (wxHORIZONTAL);
    GroupContentBoxSizer -> Add (GroupSubContentBoxSizer, 0, wxLEFT|wxRIGHT|wxTOP|wxEXPAND, _B(0));

    //-------------------------------------------------------------------------
    // setup image control

    if (IsImage)
      {
        wxArtID ArtId 
          = Style == InformationStyle ? wxART_INFORMATION
          : Style == WarningStyle     ? wxART_WARNING
          : Style == ErrorStyle       ? wxART_ERROR
          : Style == CriticalErrorStyle ? wxART_ERROR
          : Style == QuestionStyle    ? wxART_QUESTION
          : wxEmptyString;

        wxASSERT (ArtId != wxEmptyString);

        if (ArtId != wxEmptyString)
          {
            wxBitmap Bitmap = wxArtProvider::GetBitmap (ArtId, wxART_MESSAGE_BOX, wxDefaultSize);
            wxWindow * Window 
              = new wxStaticBitmap (this, -1, 
                                    Bitmap,
                                    wxDefaultPosition, wxSize (Bitmap.GetWidth(), Bitmap.GetHeight()),
                                    0 /* style */);

            GroupSubContentBoxSizer -> Add (Window, 0, wxEXPAND, _B(0));
            GroupSubContentBoxSizer -> Add (_B(10), _B(0));
          }
      }

    //-------------------------------------------------------------------------
    // setup text control for message

    // to improve readability, the background is set to white 
    // for longer message texts - a message is considered to be "long"
    // if it contains at least one newline character or if it contains
    // more than 300 characters
    bool IsNewlineInMessage = (m_DialogMessageString.Find ('\n') != -1);
    bool IsLongMessage = IsNewlineInMessage || (m_DialogMessageString.Len() >= 300);

    wxSize TextCtrlSize = wxSize (getAdjustedTextCtrlWidth (IsLongMessage ? 400 : 350),
                                  getAdjustedTextCtrlHeight (1, 1));

    // wxMultiLineText: create without border because the dialog is so simple
    // that borders as structuring elements are not really needed
    m_MessageMultiLineText.create (this, -1, 
                                   IsLongMessage ? wxBORDER_SIMPLE : wxBORDER_NONE, 
                                   TextCtrlSize,
                                   (m_Style & IsHtmlMode) ? wxMultiLineText::wxHTML_MODE 
#if wxCHECK_VERSION (2, 5, 0)
                                   // HACK: use Html window always because of wxTextCtrl size
                                   // setting problem with new wxWidgets version (2.5.2)
                                   : wxMultiLineText::wxFORCE_HTMLWINDOW
#else
                                   : 0
#endif
                                   );
    if (m_MessageMultiLineText.getTextCtrl() != NULL)
      {
        m_MessageMultiLineText.getTextCtrl() -> SetFont (m_TextCtrlFont);
      }
    else if (m_MessageMultiLineText.getHtmlWindow() != NULL)
      {
        setHtmlWindowFonts (m_MessageMultiLineText.getHtmlWindow(),
                            m_TextCtrlFont.GetPointSize(),
                            true,
                            FALSE);
        m_MessageMultiLineText.getHtmlWindow() -> SetBorders (5);
      }
    m_MessageMultiLineText.getWindow() -> SetBackgroundColour (IsLongMessage ? *wxWHITE : BackgroundColor);
    m_MessageMultiLineText.setText (m_DialogMessageString);
    adjustMultiLineTextSize (m_MessageMultiLineText, 3, 10);

    GroupSubContentBoxSizer -> Add (m_MessageMultiLineText.getWindow(), 0, wxEXPAND, _B(0));

    //-------------------------------------------------------------------------
    GroupContentBoxSizer -> Add (new wxStaticLine (this, -1), 0, 
                                 wxTOP|wxEXPAND, _B(14));

    //-------------------------------------------------------------------------
    // add sizer for buttons

    int AdditionalButtonSizerFlags = (m_CurrentButtonIndex >= 2)
      ? cast_enum (wxEXPAND) : cast_enum (wxALIGN_CENTRE_HORIZONTAL);

    GroupContentBoxSizer -> Add (m_ButtonFlexGridSizer, 0, 
                                 wxTOP
                                 | AdditionalButtonSizerFlags, _B(14));
    
    // add message text control
    if (m_StatusMessageTarget 
        && (m_ErrorMessageMultiLineText.getWindow() != NULL))
      GroupContentBoxSizer -> Add (m_ErrorMessageMultiLineText.getWindow(),
                                   0, wxTOP|wxEXPAND, _B(14));

    GroupContentBoxSizer -> Add (0, _B(14));

    // append postfix to title
    if (s_GlobalTitlePostfix != wxEmptyString)
      {
        if (m_TitleString != wxEmptyString)
          m_TitleString.Append (" - ");
        m_TitleString.Append (s_GlobalTitlePostfix);
      }
    SetTitle (m_TitleString);

    //-------------------------------------------------------------------------
    SetSizerAndFit (m_PanelSizer);

    enableWatchedControlChangeNotification (true);

    if (m_DefaultButton)
      {
        m_DefaultButton -> SetDefault();
        m_DefaultButton -> SetFocus();
      }
}

//-------------------------------------------------------------------------

void wxMessageExtDialog::setStyle (/* EStyle */ long Style)
{
    m_Style = Style;
}

void wxMessageExtDialog::setGlobalFlags (/* EFlags */ long Flags)
{
    s_GlobalFlags = Flags;
}

//-------------------------------------------------------------------------

void  wxMessageExtDialog::setGlobalTitlePostfix (const wxString & String) // (static)
{
    s_GlobalTitlePostfix = String;
}

void  wxMessageExtDialog::getGlobalTitlePostfix (wxString & String) // (static)
{
    String = s_GlobalTitlePostfix;
}

void wxMessageExtDialog::setGlobalMessageExtDialogZoom (double ZoomValue) // (static)
{
     s_GlobalMessageExtDialogZoom = ZoomValue;
}

double wxMessageExtDialog::getGlobalMessageExtDialogZoom () // (static)
{
    return s_GlobalMessageExtDialogZoom;
}

void wxMessageExtDialog::setGlobalGetDefaultParentWindowFunc (wxDefaultParentWindowFunc Func)
{
    wxSetGlobalDefaultParentWindowFunc (Func);
}

wxWindow * wxMessageExtDialog::getUsedParentWindow (wxWindow * ParentWindow)
{
    return wxGetDefaultParentWindow (ParentWindow);
}

//-------------------------------------------------------------------------

wxButton * wxMessageExtDialog::findButtonForKey (char KeyChar)
{
    wxWindowList::Node *CurrentWindowNode = GetChildren().GetFirst();
    wxString SearchString = "&";
    SearchString.Append (KeyChar);

    wxButton * FoundButton = NULL;
    bool IsDuplicatedKey = FALSE;

    while (CurrentWindowNode != NULL)
    {
        wxWindow *Window = CurrentWindowNode->GetData();

        if (Window -> IsKindOf (CLASSINFO(wxButton)))
          {
            wxString LabelString = Window -> GetLabel ();
            int CharIndex = LabelString.Find (SearchString);
            if (CharIndex != -1 
                && (CharIndex == 0 || LabelString.GetChar (CharIndex-1) != '&'))
              {
                if (FoundButton == NULL)
                  FoundButton = (wxButton *) Window;
                else
                  IsDuplicatedKey = true;
              }
          }

        CurrentWindowNode = CurrentWindowNode->GetNext();
    }

    return IsDuplicatedKey ? NULL : FoundButton;
}

//-------------------------------------------------------------------------

wxButton * wxMessageExtDialog::getButton (int ButtonIdent)
{
    wxWindow * Window = FindWindowById (ButtonIdent, this);

    bool IsButton = Window -> IsKindOf (CLASSINFO (wxButton));
    // wxCHECK_MSG (IsButton, FALSE,
    //           _T("getButton(): FindWindowById() did not return a valid button"));

    return IsButton ? (wxButton *) Window : NULL;
}

void wxMessageExtDialog::enableButton (int ButtonIdent, bool IsEnable)
{
    wxWindow * Window = FindWindowById (ButtonIdent, this);
    if (Window)
      {
        Window -> Enable (IsEnable);
      }
}

void wxMessageExtDialog::setButtonLabel (int ButtonIdent, const wxString & LabelString)
{
    wxButton * Button = getButton (ButtonIdent);
    if (Button)
      {
        Button -> SetLabel (LabelString);
      }
}

//=========================================================================

int MessageExtDialog (wxWindow * ParentWindow,
                     const wxString & MessageString,
                     const wxString & TitleString,
                     /* wxMessageExtDialog::EStyle */ long Style)
{
    wxMessageExtDialog MessageExtDialog (ParentWindow, MessageString, TitleString);
    MessageExtDialog.addButton (_("&OK"), wxID_OK);
    MessageExtDialog.makeLastAddedButtonDefault ();
    MessageExtDialog.setStyle (Style);
    int PressedButtonIdent = MessageExtDialog.ShowModal ();
    return PressedButtonIdent;
}

int MessageExtDialog (const wxString & MessageString,
                     const wxString & TitleString,
                     /* wxMessageExtDialog::EStyle */ long Style)
{
    return MessageExtDialog (NULL, MessageString, TitleString, Style);
}

int MessageExtDialog (const wxString & MessageString,
                     /* wxMessageExtDialog::EStyle */ long Style)
{
    return MessageExtDialog (NULL, MessageString, wxEmptyString, Style);
}

int MessageExtDialog (const wxString & MessageString)
{
    return MessageExtDialog (NULL, MessageString, wxEmptyString, wxMessageExtDialog::MessageStyle);
}

//=========================================================================

extern "C" int MessageExtDialog_ (const char * MessageString,
                                 EMessageExtDialogStyle Style)
{
    // wxMessageBox (MessageString);
    return MessageExtDialog (MessageString, 
                            Style);
}

//=========================================================================
