iBinx Corp.
contact usshisen-sho

Source listing: . Return to the main page

/* -*- C++ -*-
 *******************************************************************
 *
 *
 * Shisen-Sho for Windows
 *
 * dialogs.cpp - Dialog implementations
 *
 *
 *******************************************************************
 *
 * A japanese game similar to mahjongg
 *
 *******************************************************************
 *
 * Created 2000-12-28 by Jim Mason <jmason@sirius.com>
 *
 * Game engine ported from Mario Weilguni's <mweilguni@sime.com>
 * original game for KDE, KSHISEN.  If you are interested in the
 * KDE version of the game, see http://www.kde.org/kdegames/
 *
 *******************************************************************
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *******************************************************************
 */

#include "stdafx.h"
#include "dialogs.h"
#include <windowsx.h>
#include <commctrl.h>
#include <commdlg.h>
#include "shisen.h"
#include "registry.h"
#include "bmputil.h"


/////////////////////////////////////////////////////////////////////////////
// PlayerName dialog

PlayerName::PlayerName(Window* pParent) :
    Dialog(IDD, pParent) {
}

BOOL PlayerName::OnCommand(UINT nID, UINT nEvent, HWND hSource) {
    if(nID == IDOK) {
        // Save dialog item data
        GetDlgItemText(m_hWnd, IDC_NAME, m_szName.GetBuffer(256), 256);
        m_szName.ReleaseBuffer();
    }
    return Dialog::OnCommand(nID, nEvent, hSource);
}


/////////////////////////////////////////////////////////////////////////////
// ShowScores dialog

ShowScores::ShowScores(Window* pParent) :
    Dialog(IDD, pParent) {
}

#define FUDGE_FACTOR 8

BOOL ShowScores::OnInitDialog() {
    // Let superclass do its thing
    Dialog::OnInitDialog();

    // Create MS San Serif bold 12 point font
    LOGFONT lf;
    memset(&lf, 0, sizeof(LOGFONT));
    HDC hDC = GetDC();
    lf.lfHeight = -MulDiv(12/*pt size*/, GetDeviceCaps(hDC, LOGPIXELSY), 72);
    lf.lfWeight = FW_BOLD;
    _tcscpy(lf.lfFaceName, _T("MS Sans Serif"));
    HFONT hBigBold = CreateFontIndirect(&lf);

    // Setup caption control with the font
    m_caption.SetFont(hBigBold, TRUE);     // m_caption destructor will delete font
    m_caption.SubclassDlgItem(IDC_CAPTION, this);


    // Setup columns

    // Column geometry policy is as follows:
    //        Rank and Score are sized to label width
    //        Time and Size each get 1/4 the remaining list width
    //        Name gets the remainder (roughly 1/2 the list width)
    HWND hList = GetDlgItem(IDC_LIST1);
    CStdString szRank, szScore, szLabel;
    int nRank, nScore, nRem, nTime, nSize;
    RECT rcList, rcText;
    ::GetClientRect(hList, &rcList);

    szRank.LoadString(IDS_RANK);
    DrawText(hDC, szRank, -1, &rcText, DT_CALCRECT|DT_LEFT);
    nRank = rcText.right - rcText.left + FUDGE_FACTOR;
    szScore.LoadString(IDS_SCORE);
    DrawText(hDC, szScore, -1, &rcText, DT_CALCRECT|DT_LEFT);
    nScore = rcText.right - rcText.left + FUDGE_FACTOR*2;
    nRem = rcList.right - rcList.left - nRank - nScore;
    nTime = nSize = nRem/4;
    nRem -= nTime + nSize;
    ReleaseDC(hDC);

    LVCOLUMN lvCol;
    lvCol.mask = LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
    lvCol.fmt = LVCFMT_RIGHT;
    lvCol.pszText = (LPTSTR)(LPCTSTR)szRank;
    lvCol.cx = nRank;
    ListView_InsertColumn(hList, 0, &lvCol);             // Rank
    szLabel.LoadString(IDS_NAME);
    lvCol.pszText = (LPTSTR)(LPCTSTR)szLabel;
    lvCol.cx = nRem;
    ListView_InsertColumn(hList, 1, &lvCol);             // Name
    szLabel.LoadString(IDS_TIME);
    lvCol.cx = nTime;
    ListView_InsertColumn(hList, 2, &lvCol);             // Time
    szLabel.LoadString(IDS_SIZE);
    lvCol.cx = nSize;
    ListView_InsertColumn(hList, 3, &lvCol);             // Size
    lvCol.pszText = (LPTSTR)(LPCTSTR)szScore;
    lvCol.cx = nScore;
    ListView_InsertColumn(hList, 4, &lvCol);             // Score

    // Populate the list
    if(m_pHighScore)
        for(int i = 0; i < 10; i++)
            if(i < (*m_pHighScore).size()) {
                LVITEM item;
                CStdString s;
                HighScore hs = (*m_pHighScore)[i];   

                // Add the item
                s.Format("%d", i+1);
                item.mask = LVIF_TEXT;
                item.iItem = i;
                item.iSubItem = 0;
                item.pszText = (LPTSTR)(LPCTSTR)s;
                ListView_InsertItem(hList, &item);

                // insert name
                ListView_SetItemText(hList, i, 1, hs.name);

                // insert time
                s.Format("%02d:%02d:%02d", hs.seconds/3600,
                                    (hs.seconds/60)%60, hs.seconds%60);
                ListView_SetItemText(hList, i, 2, (LPTSTR)(LPCTSTR)s);

                // insert size
                s.Format("%d x %d", hs.x, hs.y);
                ListView_SetItemText(hList, i, 3, (LPTSTR)(LPCTSTR)s);

                // insert score
                s.Format("%d%c", Frame::GetScore(hs), hs.gravity?'*':' ');
                ListView_SetItemText(hList, i, 4, (LPTSTR)(LPCTSTR)s);
            }

    // Optionally highlight an item
    if(m_iHotItem >= 0)
        ListView_SetHotItem(hList, m_iHotItem);

    return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
// SelectTileset dialog

SelectTileset::SelectTileset(Window* pParent) :
    Dialog(IDD, pParent) {
}

BOOL SelectTileset::OnCommand(UINT nID, UINT nEvent, HWND hSource) {
    switch(nEvent) {
    case CBN_SELCHANGE:
        switch(nID) {
        case IDC_TILESET:
            OnChangeTileset();
            break;
        case IDC_BACKGROUND:
            OnChangeBackground();
            break;
        }
        break;
    case BN_CLICKED:
        switch(nID) {
        case IDC_BROWSETILESET:
            OnBrowseTileset();
            break;
        case IDC_BROWSEBACKGROUND:
            OnBrowseBackground();
            break;
        case IDOK:
            OnOK();
            break;
        }
        break;
    default:
        return Dialog::OnCommand(nID, nEvent, hSource);
    }

    Dialog::OnCommand(nID, nEvent, hSource);
    return TRUE;
}

BOOL SelectTileset::OnInitDialog() {
    // Let superclass do its thing
    Dialog::OnInitDialog();

    // Setup the board
    RECT rc;
    SetRect(&rc, 0, 0, 0, 0);
    if (!m_board.Create(0, _T("Shisen Board"), _T("Shisen Tile Selector"),
            WS_CHILD|WS_VISIBLE, rc, m_hWnd, IDW_MAIN)) {
        // Failed to create board window; bail
        EndDialog(m_hWnd, IDCANCEL);
        return TRUE;
    }
    m_board.setNoEvents();
    m_board.setSolvableFlag(FALSE);
    m_board.setShuffle(4);
    m_board.setSize(4, 2);

    // Position the board
    RECT rcFrame;
    SIZE sizeBoard = m_board.sizeHint();
    ::GetWindowRect(GetDlgItem(IDC_FRAME), &rcFrame);
    MapWindowPoints(HWND_DESKTOP, m_hWnd, (LPPOINT)&rcFrame, 2);
    m_board.MoveWindow(rcFrame.left, rcFrame.top,
                        sizeBoard.cx, sizeBoard.cy, TRUE);

    m_imageFiles.resize(0);

    // Setup the tileset combo
    m_tileset = GetDlgItem(IDC_TILESET);

    // Add default item
    ComboBox_AddString(m_tileset, _T("Default"));
    ComboBox_SetItemData(m_tileset, 0, -1);

    CStdString szCurrent = App::GetProfile()->GetProfileString(_T("Tilesets"),
                                                    _T("Tileset"), _T(""));
    if(szCurrent.IsEmpty())
        // Current tileset is the default, otherwise...
        ComboBox_SetCurSel(m_tileset, 0);
    else
        // ...prime board with the current tileset
        m_board.loadMahjonggTileset(szCurrent);

    // Populate tileset combo
    for(int i=1; TRUE; i++) {
        CStdString s, e;
        s.Format("Tileset_%d", i);
        e = App::GetProfile()->GetProfileString(_T("Tilesets"), s, _T(""));

        // Have we enumerated all the cached tilesets?
        if(e.IsEmpty())
            break;

        // Skip this image if it is invalid
        if(!CBmpUtil::LoadBmpImage(e))
            continue;

        // Add image pathname to the imageFiles array
        m_imageFiles.push_back(e);
        int iArrayPos = m_imageFiles.size()-1;

        // Extract and nicely format the basename for the combo
        int iStart = e.ReverseFind('\\');
        int iEnd = e.ReverseFind('.');
        s = e.Mid(iStart<0?0:iStart+1,
            (iEnd>0 && iEnd>iStart)?iEnd-iStart-1:e.GetLength());
        s.SetAt(0, toupper(s.GetAt(0)));

        // Set the basename in the combo, selecting it if appropriate
        int iComboPos = ComboBox_AddString(m_tileset, s);
        ComboBox_SetItemData(m_tileset, iComboPos, iArrayPos);
        if(!szCurrent.CompareNoCase(e))
            ComboBox_SetCurSel(m_tileset, iComboPos);
    }

    // Remember the index of the last tileset.  This is where we will
    // begin adding any new tileset which needs to be flushed.
    m_iNextTileset = i;

    // Setup the background combo
    m_background = GetDlgItem(IDC_BACKGROUND);

    // Add default item
    ComboBox_AddString(m_background, _T("Default"));
    ComboBox_SetItemData(m_background, 0, -1);

    szCurrent = App::GetProfile()->GetProfileString(_T("Tilesets"),
                                            _T("Background"), _T(""));
    if(szCurrent.IsEmpty())
        // Current background is the default, else...
        ComboBox_SetCurSel(m_background, 0);
    else
        // ...prime board with the current background
        m_board.loadBackground(szCurrent);

    // Populate background combo
    for(i=1; TRUE; i++) {
        CStdString s, e;
        s.Format("Background_%d", i);
        e = App::GetProfile()->GetProfileString(_T("Tilesets"), s, _T(""));

        // Have we enumerated all the cached backgrounds?
        if(e.IsEmpty())
            break;

        // Skip this image if it is invalid
        if(!CBmpUtil::LoadBmpImage(e))
            continue;

        // Add image pathname to the imageFiles array
        m_imageFiles.push_back(e);
        int iArrayPos = m_imageFiles.size()-1;

        // Extract and nicely format the basename for the combo
        int iStart = e.ReverseFind('\\');
        int iEnd = e.ReverseFind('.');
        s = e.Mid(iStart<0?0:iStart+1,
            (iEnd>0 && iEnd>iStart)?iEnd-iStart-1:e.GetLength());
        s.SetAt(0, toupper(s.GetAt(0)));

        // Set the basename in the combo, selecting it if appropriate
        int iComboPos = ComboBox_AddString(m_background, s);
        ComboBox_SetItemData(m_background, iComboPos, iArrayPos);
        if(!szCurrent.CompareNoCase(e))
            ComboBox_SetCurSel(m_background, iComboPos);
    }

    // Remember the index of the last background.  This is where
    // we will being adding any new background which needs to be
    // flushed.
    m_iNextBkgnd = i;

    // Remember the last cached image file.  This will allow us
    // to figure out later whether a new tileset or bitmap needs
    // to be flushed.
    m_iNewFile = m_imageFiles.size();

    return TRUE;
}

void SelectTileset::OnChangeTileset() {
    CStdString szFileName;
    int iSel;
    if((iSel = ComboBox_GetCurSel(m_tileset)) != -1) {
        int iIndex =ComboBox_GetItemData(m_tileset, iSel);
        if(iIndex == -1) {
            // Load default tileset
            m_board.loadMahjonggTileset("");
            m_board.InvalidateRect(NULL, TRUE);
            return;
        } else {
            szFileName = m_imageFiles.at(iIndex);

            // Test whether image file is valid
            if(!CBmpUtil::LoadBmpImage(szFileName)) {
                // Error
                return;
            }

            // Select tileset into board
            m_board.loadMahjonggTileset(szFileName);
            m_board.InvalidateRect(NULL, TRUE);
        }
    }
}

void SelectTileset::OnBrowseTileset() {
    int iPos;
    CStdString e, szAppDir;
    GetModuleFileName(NULL, szAppDir.GetBuffer(1024), 1024);
    szAppDir.ReleaseBuffer(-1);
    if((iPos = szAppDir.ReverseFind('\\')) > 0)
        szAppDir = szAppDir.Left(iPos);
    
    // Setup the Open dialog
    OPENFILENAME ofn;
    memset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = m_hWnd;
    ofn.hInstance = App::GetHInstance();
    ofn.lpstrFilter = _T("Tileset Files (*.tileset)\0*.tileset\0\0");
    ofn.lpstrInitialDir = szAppDir;
    ofn.lpstrFile = e.GetBuffer(1024);
    ofn.nMaxFile = 1024;
    ofn.Flags = OFN_FILEMUSTEXIST;
    int iSuccess = GetOpenFileName(&ofn);
    e.ReleaseBuffer(-1);

    if(iSuccess) {
        // Is file valid?
        HBITMAP hBmp;
        if(CBmpUtil::LoadBmpImage(e, &hBmp)) {
            BITMAP bm;
            GetObject(hBmp, sizeof(BITMAP), &bm);
            DeleteObject(hBmp);
            if(!(bm.bmWidth == 360 && bm.bmHeight == 280)) {
                // Tileset has incorrect dimensions
                e.Format(IDS_INVALID_FILE);
                MessageBox(m_hWnd, e, App::GetAppName(), MB_OK|MB_ICONEXCLAMATION);
                return;
            }
        } else {
            // File does not exist or cannot be loaded
            e.Format(IDS_INVALID_FILE);
            MessageBox(m_hWnd, e, App::GetAppName(), MB_OK|MB_ICONEXCLAMATION);
            return;
        }

        // Is it already cached?
        for(int i = 0; i<m_imageFiles.size(); i++) {
            if(!e.CompareNoCase(m_imageFiles.at(i))) {
                // It's already cached; find it
                for (int j = 0; j<ComboBox_GetCount(m_tileset); j++)
                    if((int)ComboBox_GetItemData(m_tileset, j) == i) {
                        // Select it and activate
                        ComboBox_SetCurSel(m_tileset, j);
                        SetFocus(m_tileset);
                        OnChangeTileset();
                        return;
                    }
            }
        }

        // Add it to the combo
        m_imageFiles.push_back(e);
        int iArrayPos = m_imageFiles.size()-1;
        int iStart = e.ReverseFind('\\');
        int iEnd = e.ReverseFind('.');
        e = e.Mid(iStart<0?0:iStart+1,
            (iEnd>0 && iEnd>iStart)?iEnd-iStart-1:e.GetLength());
        e.SetAt(0, toupper(e.GetAt(0)));
        int iComboPos = ComboBox_AddString(m_tileset, e);
        ComboBox_SetItemData(m_tileset, iComboPos, iArrayPos);
        ComboBox_SetCurSel(m_tileset, iComboPos);
        SetFocus(m_tileset);
        OnChangeTileset();
    }
}

void SelectTileset::OnChangeBackground() {
    CStdString szFileName;
    int iSel;
    if((iSel = ComboBox_GetCurSel(m_background)) != -1) {
        int iIndex = ComboBox_GetItemData(m_background, iSel);
        if(iIndex == -1) {
            // Load default background
            m_board.loadBackground("");
            m_board.InvalidateRect(NULL, TRUE);
            return;
        } else {
            szFileName = m_imageFiles.at(iIndex);

            // Test whether image file is valid
            if(!CBmpUtil::LoadBmpImage(szFileName)) {
                // Error
                return;
            }

            // Select tileset into board
            m_board.loadBackground(szFileName);
            m_board.InvalidateRect(NULL, TRUE);
        }
    }
}

void SelectTileset::OnBrowseBackground() {
    CStdString e, szWindowsDir;
    GetWindowsDirectory(szWindowsDir.GetBuffer(256), 256);
    szWindowsDir.ReleaseBuffer(-1);

    // Setup the Open dialog
    OPENFILENAME ofn;
    memset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = m_hWnd;
    ofn.hInstance = App::GetHInstance();
    ofn.lpstrFilter = _T("Background Files (*.bmp)\0*.bmp\0\0");
    ofn.lpstrInitialDir = szWindowsDir;
    ofn.lpstrFile = e.GetBuffer(1024);
    ofn.nMaxFile = 1024;
    ofn.Flags = OFN_FILEMUSTEXIST;
    int iSuccess = GetOpenFileName(&ofn);
    e.ReleaseBuffer(-1);

    if(iSuccess) {
        // Is file valid?
        if(!CBmpUtil::LoadBmpImage(e)) {
            // Error
            e.LoadString(IDS_INVALID_FILE);
            MessageBox(m_hWnd, e, App::GetAppName(), MB_OK|MB_ICONEXCLAMATION);
            return;
        }

        // Is it already cached?
        for(int i = 0; i<m_imageFiles.size(); i++) {
            if(!e.CompareNoCase(m_imageFiles.at(i))) {
                // It's already cached; find it
                for (int j = 0; j<ComboBox_GetCount(m_background); j++)
                    if((int)ComboBox_GetItemData(m_background, j) == i) {
                        // Select it and activate
                        ComboBox_SetCurSel(m_background, j);
                        SetFocus(m_background);
                        OnChangeBackground();
                        return;
                    }
            }
        }

        // Add it to the combo
        m_imageFiles.push_back(e);
        int iArrayPos = m_imageFiles.size()-1;
        int iStart = e.ReverseFind('\\');
        int iEnd = e.ReverseFind('.');
        e = e.Mid(iStart<0?0:iStart+1,
            (iEnd>0 && iEnd>iStart)?iEnd-iStart-1:e.GetLength());
        e.SetAt(0, toupper(e.GetAt(0)));
        int iComboPos = ComboBox_AddString(m_background, e);
        ComboBox_SetItemData(m_background, iComboPos, iArrayPos);
        ComboBox_SetCurSel(m_background, iComboPos);
        SetFocus(m_background);
        OnChangeBackground();
    }
}

void SelectTileset::OnOK() {
    // See if tileset is cached
    int iPos, iSel = ComboBox_GetCurSel(m_tileset);
    if(iSel > 0 && ((iPos = ComboBox_GetItemData(m_tileset, iSel)) != -1) &&
                iPos >= m_iNewFile) {
        // Cache the tileset
        CStdString s;
        s.Format(_T("Tileset_%d"), m_iNextTileset);
        App::GetProfile()->WriteProfileString(_T("Tilesets"), s, m_imageFiles.at(iPos));
    }

    // Set current tileset
    App::GetProfile()->WriteProfileString(_T("Tilesets"), _T("Tileset"),
                    (m_szTileset = (iSel > 0)?m_imageFiles.at(iPos):""));

    // See if background is cached
    iSel = ComboBox_GetCurSel(m_background);
    if(iSel > 0 && ((iPos = ComboBox_GetItemData(m_background, iSel)) != -1) &&
                iPos >= m_iNewFile) {
        // Cache the background
        CStdString s;
        s.Format(_T("Background_%d"), m_iNextBkgnd);
        App::GetProfile()->WriteProfileString(_T("Tilesets"), s, m_imageFiles.at(iPos));
    }

    // Set current background
    App::GetProfile()->WriteProfileString(_T("Tilesets"), _T("Background"),
        (m_szBackground = (iSel > 0)?m_imageFiles.at(iPos):""));
}


/////////////////////////////////////////////////////////////////////////////
// About dialog

About::About(Window* pParent) :
    Dialog(IDD, pParent) {
}


/////////////////////////////////////////////////////////////////////////////
// StaticText control

StaticText::StaticText() {
    m_hFont = NULL;                     // default font
    m_rgb = RGB(0, 0, 0);               // black text
    m_nFormat = DT_CENTER|DT_VCENTER;   // centre text in control
    m_bOwnFont = FALSE;
}

LRESULT StaticText::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
    switch(nMsg) {
    case WM_PAINT:
        OnPaint();
        return 0L;
    }
    return Window::WndProc(nMsg, wParam, lParam);
}

void StaticText::OnDestroy() {
    // If we own the font, we're responsible for deleting it
    if(m_bOwnFont)
        DeleteObject(m_hFont);
    m_bOwnFont = FALSE;

    Window::OnDestroy();
}

void StaticText::OnPaint() {
    // Get the control text
    CStdString szText;
    GetWindowText(m_hWnd, szText.GetBuffer(1024), 1024);
    szText.ReleaseBuffer(-1);

    // Get the client rect
    RECT rcText;
    GetClientRect(&rcText);

    // Prime the DC for drawing
    HFONT oldFont;
    PAINTSTRUCT ps;
    BeginPaint(&ps);
    COLORREF oldrgb = SetTextColor(ps.hdc, m_rgb);
    int iOldMode = SetBkMode(ps.hdc, TRANSPARENT);
    if(m_hFont)
        oldFont = (HFONT)SelectObject(ps.hdc, m_hFont);

    // Render the text
    DrawText(ps.hdc, szText, -1, &rcText, m_nFormat);

    // Cleanup the DC
    if(m_hFont)
        SelectObject(ps.hdc, oldFont);
    SetTextColor(ps.hdc, oldrgb);
    SetBkMode(ps.hdc, iOldMode);
    EndPaint(&ps);
}
<< Back to Shisen-Sho