Author Topic: Using Dynamic Dialogs  (Read 8010 times)

0 Members and 1 Guest are viewing this topic.

Arnold

  • Hero Member
  • *****
  • Posts: 941
Using Dynamic Dialogs
« on: January 02, 2018, 01:53:37 AM »
Hi Charles,

the new year has begun just in time to start a new project. Maybe it will be good enough some time to get a home in the Oxygenbasic\inc folder.
When I found that Oxygenbasic can deal very well with resources from a .rc file, I got interested in dialogs created during runtime in memory using templates. I had access to these readings:

Win32.chm (Dialog Boxes)
MASM32 SDK (dialogs.inc)
and this link of the Freebasic Forum was very helpful:
https://www.freebasic.net/forum/viewtopic.php?t=5667

The trick is to find a way to create a sequence which contains various items and arrays of variable length  and to respect some word and dword boundaries. I have started to create a basic dialogs.inc for Oxygenbasic based on the code of MichaelW, which should be improved and completed with some more constants and winapi functions but I think it already works quite nice.

Perhaps there is a more elegant way to create the sequence with the members and strings of variable length? This is something of general interest, not only for DLGTEMPLATE and DLGITEMTEMPLATE extended structures.
I used getmemory for allocating space. Would it be possible to allocate the memory space in sub Dialog()? I did not manage to do this. Would it also be possible to resize the allocated space, similar to the WinApi function GlobalReAlloc? This would be interesting in function CreateModalDialog, CreateModelessDialog.

Dealing with in-memory dialogs is a very interesting matter and there would be some advantages. I am curious how far dialogs.inc could be extended.

Roland
« Last Edit: March 19, 2018, 12:57:03 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #1 on: January 02, 2018, 02:03:48 AM »
Here are some small examples for dialogs.inc (will be continued)

Edit: adapted for modified dialogs.inc

SimpleModal.o2bas:
Code: (o2) [Select]
'====================================================================
' Simple modal dialog as main.
'====================================================================

'% review

uses dialogs
namespace

==============================================

'MAIN CODE
=============================================
 
'dim nCmdline as asciiz ptr, hInstance as sys
'&nCmdline = GetCommandLine
'hInstance = GetModuleHandle(NULL)


function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as int callback
  select case uMsg
 
    case WM_INITDIALOG
      return true

    case WM_COMMAND
      select case loword(wParam)
        case IDCANCEL
           EndDialog( hDlg, null )
      end select
     
    case WM_CLOSE
      EndDialog( hDlg, null )
               
  end select

  return 0
end function

sub winmain()

  sys lpdt
 
  'provide memory for DLGTEMPLATE structure etc   
'  dyn::init(lpdt,nBytes)
  dyn::init(lpdt) '1024

  dyn::Dialog( 1,  0, 0, 200, 100, "Memory based modal dialog using OxygenBasic", lpdt,
          WS_OVERLAPPEDWINDOW or DS_CENTER )
  dyn::PushButton( "Close" , IDCANCEL, 80, 70, 40, 12 )

  dyn::CreateModalDialog( null, @DialogProc, 0, lpdt )
end sub

winmain()

NestedModal.o2bas:
Code: (o2) [Select]
'====================================================================
' Nested modal dialog demo, modal dialog as main.
'====================================================================

'%review

uses dialogs
namespace


function NestedDialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  select case uMsg

    case WM_COMMAND
      if loword(wParam) = IDCANCEL then
        EndDialog( hDlg, null )
      end if

    case WM_CLOSE
      EndDialog( hDlg, null )

  end select

  return 0
end function

'====================================================================

function MainDialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as sys callback

  select case uMsg

    case WM_COMMAND

      select case loword(wParam)
        case 100
          sys lpdt
          dyn::init(lpdt)
         
          dyn::Dialog( 1, 0, 0, 120, 90, "Modal Nested Dialog", lpdt,
                       WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )
          dyn::DefPushButton( "Close", IDCANCEL, -1, 60, 40, 12  )
          dyn::CreateModalDialog( hDlg, @NestedDialogProc, 0, lpdt )

        case IDCANCEL
          EndDialog( hDlg, null )

      end select

    case WM_CLOSE
      EndDialog( hDlg, null )

  end select

  return 0
end function

'====================================================================

sys lpdt
dyn::init(lpdt)

dyn::Dialog( 2,  0, 0, 150, 100, "Main Dialog", lpdt,
             WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )

dyn::DefPushButton( "Go", 100, 30, 70, 40, 12 )
dyn::PushButton( "Close", IDCANCEL, 80, 70, 40, 12 )

dyn::CreateModalDialog( 0, @MainDialogProc, 0, lpdt )

'====================================================================

NestedModeless.o2bas:
Code: (o2) [Select]
'====================================================================
' Nested modeless dialog demo, modeless dialog as main.
'====================================================================

'#define review

uses dialogs
namespace


sys g_hNestedDlg


function NestedDialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  select case uMsg

    case WM_COMMAND
      if loword(wParam) = IDCANCEL then
        DestroyWindow( hDlg )
      end if

    case WM_CLOSE
      DestroyWindow( hDlg )

      'Reset so main can determine if nested dialog is open.
      g_hNestedDlg = 0

  end select

  return 0
end function

'====================================================================

function MainDialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as sys callback
  select case uMsg

    case WM_COMMAND

      select case loword(wParam)
        case 100
          'Open nested dialog only if not already open.
          if IsWindow( g_hNestedDlg ) = 0 then
            sys lpdt
            dyn::init(lpdt)
           
            dyn::Dialog( 1, 0, 0, 120, 90, "Modeless Nested Dialog", lpdt,
                         WS_OVERLAPPED or WS_SYSMENU or DS_CENTER or WS_VISIBLE )
            dyn::DefPushButton( "Close", IDCANCEL, -1, 60, 40, 12 )
            g_hNestedDlg = dyn::CreateModelessDialog( hDlg, @NestedDialogProc, 0, lpdt )
          end if

        case IDCANCEL
          DestroyWindow( hDlg )

      end select

    case WM_CLOSE
      DestroyWindow( hDlg )

    case WM_DESTROY
      PostQuitMessage( null )

  end select

  return 0
end function

'====================================================================

sys lpdt
dyn::init(lpdt)

sys hDlg
MSG wMsg

dyn::Dialog( 2, 0, 0, 150, 100, "Main Dialog", lpdt,
        WS_OVERLAPPED or WS_SYSMENU or DS_CENTER or WS_VISIBLE )

dyn::DefPushButton(  "Go", 100, 30, 70, 40, 12 )
dyn::PushButton( "Close", IDCANCEL, 80, 70, 40, 12 )

hDlg = dyn::CreateModelessDialog( 0, @MainDialogProc, 0, lpdt )

while GetMessage( @wMsg, null, 0, 0 ) <> 0
  if IsDialogMessage( hDlg,  @wMsg ) = 0 then
    if IsDialogMessage( g_hNestedDlg,  @wMsg ) = 0 then
      TranslateMessage( @wMsg )
      DispatchMessage( @wMsg )
    end if
  end if
wend

'====================================================================

ButtonGrid.o2bas:
Code: (o2) [Select]
'====================================================================
' Button grid demo, modal dialog as main.
'====================================================================
' Minesweeper is next

'% review

uses dialogs
namespace


function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback

  sys id, event
  static int state[100]

  select case uMsg
           
    case WM_COMMAND
      id=loword(wParam)
      event=hiword(wParam)

      if id=IDCANCEL then EndDialog( hDlg, null)
         
      if event = BN_CLICKED then     
        if state[id+100] then
          SetDlgItemText( hDlg, id, "" )
          state[id+100] = 0
        else
          SetDlgItemText( hDlg, id, "X" )
          state[id+100] = 1
        end if
      end if
       
    case WM_CLOSE
      EndDialog( hDlg, null )
   
  end select

  return 0
end function

'====================================================================

'must supply sufficient space
sys lpdt
'dyn::init(lpdt)
dyn::init(lpdt, 3650)

short id, r, c

dyn::Dialog( 100, 0, 0, 122, 130, "Button Grid Demo", lpdt,
             WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )

id = 100
for c = 10 to 100 step 10
  for r = 10 to 100 step 10
    if id > 100 then
      dyn::PushButton( "", id, r, c, 9, 9)
    else
      dyn::DefPushButton( "", id, r, c, 9, 9)
    end if
    id += 1
  next
next

dyn::CreateModalDialog( 0, @DialogProc, 0, lpdt )

'====================================================================
« Last Edit: January 20, 2018, 01:06:53 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #2 on: January 02, 2018, 02:36:35 AM »
These are some more extended examples:

Edit: adapted for modified dialogs.inc

Statusbar.o2bas:
Code: (o2) [Select]
'====================================================================
' Status bar demo, modal dialog as main.
'====================================================================

'% review

uses dialogs
namespace


function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
indexbase 0

  static sys hWndSB
  static RECT rcDlg, rcSB

      hWndSB = GetDlgItem( hDlg, 100 )
     
  select case uMsg

    case WM_INITDIALOG

      int widths[3]

      hWndSB = GetDlgItem( hDlg, 100 )

      GetClientRect( hDlg, @rcDlg )
      widths[0] = rcDlg.right \ 4
      widths[1] = rcDlg.right * 2 \ 4
      widths[2] = rcDlg.right * 3 \ 4
      widths[3] = -1              '' part extends to window border

      SendMessage( hWndSB, SB_SETPARTS, 4, @widths )

      return true

    case WM_SIZE

      int sbHeight
      string status

      GetClientRect( hDlg, @rcDlg )
      GetWindowRect( hWndSB, @rcSB )
      sbHeight = rcSB.bottom - rcSB.top + 1

      MoveWindow( hWndSB, 0, rcDlg.bottom - sbHeight ,
                  loword(lParam), rcDlg.bottom - sbHeight, false )

      '' For a simple status bar with just one part,
      '' the text can be set with WM_SETTEXT.
      ''

      status = " " & str(rcDlg.right) & "x" & str(rcDlg.bottom)
      SendMessage( hWndSB,SB_SETTEXT,0, status)
      SendMessage( hWndSB,SB_SETTEXT,1," part 1")
      SendMessage( hWndSB,SB_SETTEXT,2," part 2")
      SendMessage( hWndSB,SB_SETTEXT,3," part 3 ")

      rcDlg.bottom -= sbHeight
      InvalidateRect( hDlg, @rcDlg, true )

    case WM_CLOSE

      EndDialog( hDlg, null )

  end select

  return 0
end function

'====================================================================

sys lpdt : dyn::init(lpdt)

dyn::init_common_controls()

dyn::Dialog( 1, 0, 0, 120, 90, "Status Bar Demo", lpdt,
        WS_OVERLAPPEDWINDOW or DS_CENTER )

' Instead of trying to anticipate the position and/or size of
' the controls, just use zeros and set the correct values in
' the WM_SIZE handler.
'
dyn::Control( "", 100, STATUSCLASSNAME, 0, 0, 0, 0 )

dyn::CreateModalDialog( null, @DialogProc, 0, lpdt )

'====================================================================

Toolbar.o2bas:
Code: (o2) [Select]
'====================================================================
' Toolbar demo, with tooltips, modeless dialog as main.
'====================================================================

'% review

uses dialogs
namespace


% HINST_COMMCTRL=-1
% IDB_STD_SMALL_COLOR=0
% STD_FILENEW=6
% STD_FILEOPEN=7
% STD_FILESAVE=8
% STD_FIND=12
% STD_COPY=1
% STD_CUT=0
% STD_PASTE=2
% STD_PRINT=14
% TBSTATE_ENABLED=4
% TBSTYLE_BUTTON=0
% TBSTYLE_SEP=1
% TBSTYLE_TOOLTIPS=0x100
% TBSTYLE_FLAT=0x800
% TTF_DI_SETITEM=0x8000
% TB_ADDBITMAP=0x413
% TB_ADDBUTTONS=0x414
% TB_BUTTONSTRUCTSIZE=0x41E
% TB_GETTOOLTIPS=0x423
% TTN_GETDISPINFO=  -520


type TBADDBITMAP
  sys       hInst
  sys  nID
end type

type TBBUTTON
  int       iBitmap
  int       idCommand
  BYTE      fsState
  BYTE      fsStyle
  dword     dwData
  sys       iString
end type

type NMTTDISPINFO
  NMHDR     hdr
  char*     lpszText
  zstring   szText[80]
  sys       hinst
  uint      uFlags
  sys       lParam
end type
typedef NMTTDISPINFO TOOLTIPTEXT 


'====================================================================

function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
indexbase 0

  static sys hTT
  static sys hWndTB

  select case uMsg

    case WM_INITDIALOG

      dim as TBADDBITMAP tbab
      dim as TBBUTTON tbb[6]

      hWndTB = GetDlgItem( hDlg, 100 )

      ' Specify the structure size so the system can determine
      ' which version of the common control DLL is being used.
      '
      SendMessage( hWndTB,TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON),0 )

      ' Add the system-defined bitmap button images to the
      ' list of available images.
      '
      tbab.hInst = HINST_COMMCTRL
      tbab.nID = IDB_STD_SMALL_COLOR
      SendMessage( hWndTB, TB_ADDBITMAP, 0, @tbab)

      ' For each button, specify the button image index,
      ' the associated command identifier, and the button
      ' state and style.
      '
      tbb(0).iBitmap = STD_FILENEW
      tbb(0).idCommand = 1000
      tbb(0).fsState = TBSTATE_ENABLED
      tbb(0).fsStyle = TBSTYLE_BUTTON
      tbb(1).iBitmap = STD_FILEOPEN
      tbb(1).idCommand = 1001
      tbb(1).fsState = TBSTATE_ENABLED
      tbb(1).fsStyle = TBSTYLE_BUTTON
      tbb(2).iBitmap = STD_FILESAVE
      tbb(2).idCommand = 1002
      tbb(2).fsState = TBSTATE_ENABLED
      tbb(2).fsStyle = TBSTYLE_BUTTON
      tbb(3).iBitmap = 0
      tbb(3).fsState = TBSTATE_ENABLED
      tbb(3).fsStyle = TBSTYLE_SEP
      tbb(4).iBitmap = STD_CUT
      tbb(4).idCommand = 1003
      tbb(4).fsState = TBSTATE_ENABLED
      tbb(4).fsStyle = TBSTYLE_BUTTON
      tbb(5).iBitmap = STD_COPY
      tbb(5).idCommand = 1004
      tbb(5).fsState = TBSTATE_ENABLED
      tbb(5).fsStyle = TBSTYLE_BUTTON
      tbb(6).iBitmap = STD_PASTE
      tbb(6).idCommand = 1005
      tbb(6).fsState = TBSTATE_ENABLED
      tbb(6).fsStyle = TBSTYLE_BUTTON

      ' Add the buttons to the toolbar.
      '
      SendMessage( hWndTB,TB_ADDBUTTONS,7, @tbb )

      ' Get the handle to the ToolTip control associated
      ' with the toolbar (by the TBSTYLE_TOOLTIPS style).
      '
      hTT = SendMessage( hWndTB,TB_GETTOOLTIPS,0,0 )

      return true

    case WM_COMMAND

      select case loword(wParam)
        case IDCANCEL
           DestroyWindow(hDlg)
        case 1000
          MessageBox( hDlg, "New", "", 0 )
        case 1001
          MessageBox( hDlg, "Open", "", 0 )
        case 1002
          MessageBox( hDlg, "Save", "", 0 )
        case 1003
          MessageBox( hDlg, "Cut", "", 0 )
        case 1004
          MessageBox( hDlg, "Copy", "", 0 )
        case 1005
          MessageBox( hDlg, "Paste", "", 0 )
      end select

    case WM_NOTIFY

      NMHDR pnm at lParam

      ' This necessary because TTN_GETDISPINFO is not
      ' fully defined in commctrl.bi (0.16b stable).
      '
'      #define _TTN_GETDISPINFO -520

      if pnm.hwndFrom = hTT then
        if pnm.code = TTN_GETDISPINFO then

          TOOLTIPTEXT pdi at lParam
'          TOOLTIPTEXT *pdi
'          @pdi = lParam

          ' Now know that pnm is actually pdi, and the ToolTip
          ' control is requesting information that it needs to
          ' display a tooltip. Note that the hdr member of the
          ' TOOLTIPTEXT structure is a NMHDR structure.
          '
          select case pdi.hdr.idFrom
            case 1000
              pdi.szText = "New"
            case 1001
              pdi.szText = "Open"
            case 1002
              pdi.szText = "Save"
            case 1003
              pdi.szText = "Cut"
            case 1004
              pdi.szText = "Copy"
            case 1005
              pdi.szText = "Paste"
          end select

          ' This causes the ToolTip control to retain
          ' the information after the first request.
          '
          pdi.uFlags = pdi.uFlags or TTF_DI_SETITEM

        end if
      end if

    case WM_SIZE

      RECT rcTB

      GetWindowRect( hWndTB, @rcTB )

      MoveWindow( hWndTB, 0, 0, loword(lParam), _
                  rcTB.bottom - rcTB.top + 1, false )

    case WM_CLOSE

      DestroyWindow( hDlg )

    case WM_DESTROY

      PostQuitMessage( null )

  end select

  return 0

end function

'====================================================================

sys lpdt : dyn::init(lpdt)

sys hDlg
MSG wMsg

dyn::init_common_controls()

dyn::Dialog( 1, 0, 0, 120, 90, "Toolbar Demo", lpdt,
        WS_OVERLAPPEDWINDOW or DS_CENTER or WS_VISIBLE )

' Instead of trying to anticipate the position and/or size of
' the controls, just use zeros and set the correct values in
' the WM_SIZE handler.
'
dyn::Control( "", 100, TOOLBARCLASSNAME, TBSTYLE_TOOLTIPS or TBSTYLE_FLAT, 0, 0, 0, 0 )

hDlg = dyn::CreateModelessDialog( 0, @DialogProc, 0, lpdt )

while GetMessage( @wMsg, null, 0, 0 ) <> 0
  if IsDialogMessage( hDlg,  @wMsg ) = 0 then
    TranslateMessage( @wMsg )
    DispatchMessage( @wMsg )
  end if
wend

'====================================================================

TabControl.o2bas:
Code: (o2) [Select]
'modified from a fsw example in the FBEdit samples

'% review

uses dialogs
namespace

% IDD_DLG0 1000
% IDC_BTN1 1002
% IDC_TAB1 1001
% IDD_TAB1 1100
% CHK_1 1101
% EDT_1 1102
% IDD_TAB2 1200
% CHK_2 1201
% CHK_3 1202
% EDT_2 1001
% EDT_3 1002
% EDT_4 1003


% TCIF_PARAM=8
% TCIF_TEXT=1
% TCM_GETITEM=0x1305
% TCM_INSERTITEM=0x1307
% TCM_GETCURSEL=0x130B
% TCN_SELCHANGE=   -551
% TCN_SELCHANGING= -552

type TCITEM
  int   mask,dwState,dwStateMask
  char* pszText
  int   cchTextMax,iImage
  sys   lParam
end type
typedef TCITEM TC_ITEM

'=================================================

sys hInstance


function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
    return FALSE
end function

function Tab1Proc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
    return FALSE
end function

function Tab2Proc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
    return FALSE
end function


function DlgProc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
    sys id, event
        TCITEM ts
    NMHDR lpNMHDR
    sys hTab

    select case uMsg
       case WM_INITDIALOG
          sys lpdt1 : dyn::init(lpdt1)
          sys lpdt2 : dyn::init(lpdt2)

          'Get handle of tabstrip
          hTab=GetDlgItem(hDlg,IDC_TAB1)
          ts.mask=TCIF_TEXT Or TCIF_PARAM

          'Create Tab1 child dialog
          ts.pszText="Tab1"
          dyn::Dialog ( 2, 6,25,200,103, "Tab1", lpdt1, WS_CHILD or WS_VISIBLE)
          dyn::Autocheckbox ("Checkbox", CHK_1, 14,38,172,9, &h50010003)
          dyn::Edittext     ("Edit", EDT_1, 14,49,172,13, &h50010000, WS_EX_CLIENTEDGE)
          ts.lparam = dyn::Createmodelessdialog(hdlg,@Tab1Proc,0, lpdt1)

          SendMessage(hTab,TCM_INSERTITEM,0, @ts)

          ' Create Tab2 child dialog
          ts.pszText="Tab2"
          dyn::Dialog ( 3, 6,25,200,103, "Tab2", lpdt2,WS_CHILD)
          dyn::Edittext ("edit1", EDT_2, 8,55,174,13, &h50010000, WS_EX_CLIENTEDGE)
          dyn::Edittext ("edit2", EDT_3, 8,73,174,13, &h50010000)
          dyn::Edittext ("edit3", EDT_4, 8,92,174,13, &h50010000, WS_EX_CLIENTEDGE)
          ts.lParam= dyn::Createmodelessdialog(hdlg,@Tab2Proc,0, lpdt2)
          SendMessage(htab,TCM_INSERTITEM,1, @ts)

       case WM_NOTIFY
          NMHDR lpNMHDR at lParam
             if lpNMHDR.code=TCN_SELCHANGING Then
               ' Hide the currently selected dialog
               id=SendMessage(lpNMHDR.hwndFrom,TCM_GETCURSEL,0,0)
               ts.mask=TCIF_PARAM
               SendMessage(lpNMHDR.hwndFrom,TCM_GETITEM,id, @ts)
               ShowWindow(ts.lParam,SW_HIDE)
             elseif lpNMHDR.code=TCN_SELCHANGE Then
               ' Show the currently selected dialog
               id=SendMessage(lpNMHDR.hwndFrom,TCM_GETCURSEL,0,0)
               ts.mask=TCIF_PARAM
               SendMessage(lpNMHDR.hwndFrom,TCM_GETITEM,id, @ts)
               ShowWindow(ts.lParam,SW_SHOW)
             endif
     '
       case WM_CLOSE
          EndDialog(hDlg, 0)
     '
       case WM_COMMAND
          id=loword(wParam)
          event=hiword(wParam)
          select case id
             case IDC_BTN1
                EndDialog(hDlg, 0)

             case IDCANCEL
                ' This allows user to close dialog
                ' with Escape key;
                EndDialog(hDlg, 0)
          end select
     '
       case else
          return FALSE

    end select

    return TRUE
end function

'============================================================

dyn::init_common_controls()

'hInstance=GetModuleHandle(NULL)

sys lpdt : dyn::init(lpdt)

'WS_CLIPSIBLINGS required for parent window and tab control.
dyn::Dialog(2, 6,6,248,188, "Tab Demo",  lpdt,
       WS_OVERLAPPED Or WS_SYSMENU Or DS_CENTER or WS_CLIPSIBLINGS or DS_SETFONT, 10, "MS Sans Serif" )

' Base style defined in Control procedure is WS_CHILD or WS_VISIBLE.
dyn::Control( "", IDC_TAB1,  WC_TABCONTROL, WS_CLIPSIBLINGS, 2, 3, 244, 157 )

dyn::PushButton( "Cancel", IDC_BTN1, 176,160,64,17, WS_TABSTOP )

dyn::CreateModalDialog( 0, @DlgProc, 0, lpdt)

'============================================================

Calendar.o2bas:
Code: (o2) [Select]
'====================================================================
' MONTHCAL_CLASS common control demo, modal dialog as main.
'====================================================================
' a little bit modified

'% review

uses dialogs
namespace

macro MonthCal_SetColor(hmc,iColor,clr) (SendMessaGE(hmc,MCM_SETCOLOR,iColor,clr))

% MCM_SETCOLOR=0x100A
% MCSC_BACKGROUND=0
% MCSC_MONTHBK=4
% MCSC_TEXT=1
% MCSC_TITLEBK=2
% MCSC_TITLETEXT=3
% MCSC_TRAILINGTEXT=5

'====================================================================

function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as int callback
  sys calendar = GetDlgItem(hDlg,100)

  select case uMsg

    case WM_INITDIALOG
       MonthCal_SetColor(calendar, MCSC_TITLETEXT    , RGB(0,0,0))
       MonthCal_SetColor(calendar, MCSC_TITLEBK      , RGB(245,175,0))
       MonthCal_SetColor(calendar, MCSC_BACKGROUND   , RGB(175,175,175))
       MonthCal_SetColor(calendar, MCSC_MONTHBK      , RGB(248,245,225))
       MonthCal_SetColor(calendar, MCSC_TEXT         , RGB(0,0,225))
       MonthCal_SetColor(calendar, MCSC_TRAILINGTEXT , RGB(0,225,0))
   
    case WM_COMMAND
       if loword(wParam) = IDCANCEL then
         EndDialog( hDlg, null )
       end if

    case WM_CLOSE
       EndDialog( hDlg, null )

    case WM_SIZE
       cxClient = LOWORD (lParam)
       cyClient = HIWORD (lParam)
       SetWindowPos( calendar, 0, 0, 0, cxClient, cyClient, SWP_NOMOVE or SWP_NOREDRAW )

  end select

  return 0
end function

'====================================================================

sys lpdt
dyn::init(lpdt)

dyn::init_common_controls()

dyn::Dialog( 1, 0, 0, 120, 110, "Nice Calendar", lpdt,
        WS_OVERLAPPEDWINDOW or DS_CENTER )
dyn::Control( "", 100,  MONTHCAL_CLASS, 0, -1, 0, 120, 100 )

dyn::CreateModalDialog( 0, @DialogProc, 0, lpdt )

'====================================================================
« Last Edit: January 20, 2018, 01:09:42 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #3 on: January 03, 2018, 02:47:53 AM »
Hi Charles,

I assume the new release of Oxygen is a bit more stringent with syntax / semantics checking which is certainly helpful. If I modify in dialogs.inc:

macro make_ustring(text,memptr) 
  int count = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED,
                               text,
                               -1,
                               memptr,
                               len(text)+1 )
  memptr += count*2
end macro

everything would work as expected. Is this approach ok?

Roland

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 4276
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #4 on: January 03, 2018, 05:03:04 AM »
Hi Roland,

I have not had the opportunitiy to try your code yet, but I am sure it will be a valuable addition to our code distribution.

To extend or reduce memory, strings make very useful buffers. They use the same system as getmemory/freememory but they are also garbage-collected when out of scope, and can be manipulated with all the string functions. Redim is a macro that uses them, overlaying dynamic arrays onto varname_buffer.

I have added this macro to inc/generics.inc

it takes the name of any pointered variable, and the number of bytes to allocate, as arguments:

Code: [Select]

  def buffer string
  =================


  def bufptr strptr
  =================

  macro SetBufMem( v,n,  ,de,le)
  ==============================
  #ifndef v##_buffer
    buffer v##_buffer
  #endif
  int le=len(v##_buffer)
  int de=n-le
  if de>0
    v##_buffer += nuls(de)
  elseif de<0
    v##_buffer = left( v##_buffer,le+de )
  end if
  @v=bufptr(v##_buffer)
  end macro


Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #5 on: January 05, 2018, 07:32:13 AM »
This is a small test, which creates a main Window via CreateWindowEx and then creates an in-memory Dialog.

DlgFromWin.o2bas:
Code: (o2) [Select]
'====================================================================
' Create a Main Window in Winmain() and call Modeless Dialog
'====================================================================

'% review

uses dialogs
namespace


hInstance = GetModuleHandle(0)


% ID_BUTTON1=100
% ID_BUTTON2=101

string g_szClassName = "WinDemo"

sys hButton1, hButton2
sys g_hDialog

function DialogDlgProc(sys hwnd, Message, wParam, lParam) as bool callback

   select Message   
        case WM_COMMAND

            select loword(wParam)       
                case IDOK
                    MessageBox(hwnd, "Oh yeah!", "This feels good!",
                        MB_OK or MB_ICONASTERISK)
               
                case IDCANCEL
                    MessageBox(hwnd, "Outch!", "This hurts!",
                        MB_OK or MB_ICONEXCLAMATION)               
            end select
       
        case else
            return FALSE

    end select

    return TRUE
end function   


function WndProc(sys hwnd, Message, wParam, lParam) as sys callback

    select Message   
        case WM_COMMAND
            select loword(wParam)
                           
                case ID_BUTTON1               
                    ShowWindow(g_hDialog, SW_SHOW)
               
                case ID_BUTTON2               
                    ShowWindow(g_hDialog, SW_HIDE)               
            end select
       
        case WM_CLOSE
            DestroyWindow(hwnd)
       
        case WM_DESTROY
            DestroyWindow(g_hDialog)
            PostQuitMessage(0)
       
        case else
            return DefWindowProc(hwnd, Message, wParam, lParam)
   
    end select
   
    return 0
end function


function WinMain(sys nCmdShow) as sys

    WNDCLASSEX wc
    MSG Msg

    sys hwnd

    wc.cbSize        = sizeof(WNDCLASSEX)
    wc.style         = 0
    wc.lpfnWndProc   = @WndProc
    wc.cbClsExtra    = 0
    wc.cbWndExtra    = 0
    wc.hInstance     = hInstance
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION)
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW)
    wc.hbrBackground = COLOR_WINDOW+1
    wc.lpszMenuName  = NULL
    wc.lpszClassName = strptr(g_szClassName)
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION)

    if not RegisterClassEx(&wc) then
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION or MB_OK)
        return 0
    end if

    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Call dynamic Dialog",
        WS_OVERLAPPEDWINDOW,
        300, 300, 450, 140,
        NULL,
        NULL,
        hInstance, NULL)

    if hwnd = NULL then
        MessageBox(NULL, "Window Creation Failed!", "Error!",
                   MB_ICONEXCLAMATION or MB_OK)
        return 0
    end if       

    hButton1 = CreateWindowEx (0,"button", "Show Dialog",
                               WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
                               40, 20, 100, 24, hwnd, ID_BUTTON1,
                               hInstance, NULL)
    hButton2 = CreateWindowEx (0,"button", "Hide Dialog",
                               WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
                               40, 50, 100, 24, hwnd, ID_BUTTON2,
                               hInstance, NULL)
    sys dt
    dyn::init(dt)

    dyn::Dialog( 2, 100, 0, 98, 52, "Simple Dialog", dt,
                 WS_CHILD)
    dyn::PushButton( "Press this button", IDOK, 7, 7, 84, 14 )
    dyn::PushButton( "Don't press this button", IDCANCEL, 7,31, 84, 14 )

    g_hDialog = dyn::CreateModelessDialog( hwnd, @DialogDlgProc, 0, dt )
    if g_hDialog!=null  then         
      ShowWindow(g_hDialog, SW_SHOW)           
    else           
      MessageBox(hwnd, "CreateDialog returned NULL", "Warning!",   
                 MB_OK or MB_ICONINFORMATION)
    end if
                         
    ShowWindow(hwnd, nCmdShow)
    UpdateWindow(hwnd)

    sys bRet

    do while (bRet := GetMessage(&Msg, NULL, 0, 0)) != 0
      if bRet = -1 then
        'show an error message
        print "Error in Message Loop"
        end       
      else
          if not IsDialogMessage(g_hDialog, &Msg) then   
            TranslateMessage(&Msg)
            DispatchMessage(&Msg)
        end if
      end if       
    wend
   
    return Msg.wParam
end function

'WINDOWS START
'=============

WinMain(SW_NORMAL)
« Last Edit: January 20, 2018, 01:11:39 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #6 on: January 05, 2018, 08:50:29 AM »
Hi Charles,

I experimented a bit with SetBufMem and I managed to use the macro with dialogs.inc but after all I found that using getmemory will be perfect to achieve my goals. I used a macro init to allocate memory space of 1024 bytes and if I would need more memory I can still use getmemory.

I also applied namespace to hide some variables in dialogs.inc a little bit. Hopefully I did everything properly. So far the examples work quite well. There are some more examples in the Freebasic Forum, and I am sure they can be easily ported to Oxygenbasic and extended further.

I am curious if it is possible to add menus/submenues and accelerators too in some simplified way to dialogs.inc. (similar to a .rc resource file). At the moment I have no idea how this should work, but if it is possible, then dialogs.inc would be a really powerful tool.

Roland
« Last Edit: January 14, 2018, 11:00:42 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs - macros
« Reply #7 on: January 07, 2018, 11:56:08 PM »
Hi Charles,

in dialogs.inc I use a macro to allocate 1024 bytes for an DLGTEMPLATE structure. I now tried to overlay the macro:

macro init(tpl) {tpl=getmemory 1024}
macro init(tpl,b) {tpl=getmemory b}

and this seems to work (see ButtonGrid.o2bas):

'must supply sufficient space
sys lpdt
dyn::init(lpdt, 5000)

But I am not sure if I did this correctly. Would there be another way, e.g. could I use an optional parameter with a macro?

Roland

Edit: I can see that in DlgFromWin.o2bas this does not work. I would need:
    dyn::init(dt,1024)
to run the program.  So I do not know what would be the best way.

« Last Edit: January 08, 2018, 12:03:35 AM by Arnold »

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 4276
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #8 on: January 08, 2018, 01:34:42 AM »

Macros do not support overloading but you can check for optional params quite easily, and the macro will deliver the right code:

Code: [Select]
macro init(tpl,b)
#ifdef b
  tpl=getmemory(b)
#else
  tpl=getmemory(1024)
#endif
end macro

sys v,w
#show init(v)
#show init(v,2048)

One further enhancement to prevent memory leaks when a variable is repeatedly init:

Code: [Select]
macro init(tpl,b)
if tpl then freememory tpl
#ifdef b
  tpl=getmemory(b)
#else
  tpl=getmemory(1024)
#endif
end macro

sys v,w
#show init(v)
#show init(v,2048)



Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #9 on: January 08, 2018, 02:25:20 AM »
Thank you Charles, for your help. The macro works really great. In the meantime I have started to revise the default styles for the controls a little bit and to add some common controls which are also often used. Extended styles can have an impact to controls sometimes too. It is experimenting a little bit but it is also fun.

Roland

John

  • Hero Member
  • *****
  • Posts: 3643
Re: Using Dynamic Dialogs
« Reply #10 on: January 12, 2018, 08:02:45 AM »
O2 is like a Lego 3D printer.  :D

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #11 on: January 14, 2018, 09:07:39 AM »
Yes, it is amazing what nowadays can be done with LEGO and with Oxygen. But Oxygen is less expensive.

This is what O2 can do with OpenGl in a dialog.
Unfortunately at the moment it is only 2D.  :(
But soon it will be 3D.  :)

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #12 on: January 14, 2018, 10:58:55 AM »
I have added some small modifications to dialogs.inc:

Revised the default styles for some controls a little bit.
Added some names for controls with special styles. At the moment these are: TriCheckBox, MultiLineText, Icon, Bitmap, SortedCombo, SimpleCombo, DropDownList, HScroll, VScroll.
I also added some warning messages in dialogs.inc which should help if CreateModalDialog or CreateModelessDialog will fail. If the symbol: review is defined, a console will open to show a little bit what happens and which can be used to add own printl statements to the code of the application.

Attached is the zip file with the 15 examples which I use for testing at the moment. Some are samples from the Freebasic Forum, extended a little bit, some are new (at least with dialog templates using O2).

The next step will be to concentrate on menus. I learned that there are several ways to create menus and submenus, but I only need a procedure which should be practicable and not too laborious.

Arnold

  • Hero Member
  • *****
  • Posts: 941
Re: Using Dynamic Dialogs
« Reply #13 on: January 16, 2018, 05:13:20 PM »
Hi Charles,

somehow I am in a strange situation and I am a bit confused.

This project is a learning process for me, like everything I did with Oxygenbasic. When I started it, my goal was to achieve the results of the Freebasic or MASM examples and I used the existing representation of the function's arguments. But when I tried to convert James' example .rc files to dialog templates I found that I could use the syntax of the .rc file directly, only by adjusting the order of the arguments. This way I could simply copy the code for the controls of the resource file (in 99 % of the cases).
At the moment I only use a different name for the procedures. The difference would be e.g:
Old:
   dyn::CONTROL IDC_STC20,303,5,60,8,"Record No.",0,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_RIGHT
   dyn::CONTROL IDC_INDEX,366,4,25,10,"0 / 0",0,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_SUNKEN|SS_CENTERIMAGE|SS_CENTER
New:
  dyn::CONTROL_rc "Record No.",IDC_STC20,"Static",0x50000202,303,5,60,8
  dyn::CONTROL_rc "0 / 0",IDC_INDEX,"Static",0x50001201,366,4,25,10

James applies the resulting hex codes of the styles in the .rc file, but this is no problem.

Another example would be:

Old:     dyn::LText       ( IDC_STATIC,164,18,46,9,"Current time is:",SS_CENTERIMAGE, WS_EX_TRANSPARENT )
New:   dyn::LTEXT_rc    "Current time is:",IDC_STATIC,164,18,46,9,SS_CENTERIMAGE, WS_EX_TRANSPARENT

I am not sure if there is much interest in using dialog templates. Personally, I think it is a fascinating subject. Would there be any objection if I modify the arguments of the subroutines to the more .rc like syntax? I would not need different namings and could save about 90 lines of code in the include file. The code of the applications would look more solid. And I would not need to modify the created code of the .rc file.

Roland

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 4276
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #14 on: January 18, 2018, 02:31:33 AM »
Hi Roland,

Many thanks for those comprehensive dialog examples. We can include them in the examples folder, if that is okay with you. I think this will be of great interest to anyone developing GUI-based Windows applications. I think the same kind of strategy could be used for other kinds of objects - in games and simulations for example.

If it makes the code clearer, then by all means go ahead and restructure the functions to match rc format.