Jump to content
czardas

Recent Files Logic

Recommended Posts

czardas

The following code is intended to illustrate a method of adding recent files to the File menu, once files have been opened or saved. This is not a UDF, nor a universal solution which fits all requirements. It is just the solution I made for myself and I thought it worth sharing. I haven't noticed much about this subject. The example only emulates opening and saving files: nothing is actually written to disk. I have not included any code for opening or saving files - only the File menu and Title bar are updated. The code is intended to illustrate the logic I used. Perhaps you can improve on it or get some ideas from this example.

#include <GUIConstants.au3>
#include <MsgBoxConstants.au3>

Example()

#Region ; core functions

Func UpdateRecentFiles(ByRef $aRecentFiles, $ahMenu, $sNewPath, $iMenuInsertPos)
    Local $iMaxFiles = UBound($aRecentFiles) -1

    For $i = 1 To $aRecentFiles[0][0] ; check to see if the path was accessed recently
        If $aRecentFiles[$i][0] = $sNewPath Then ; file is already in the list
            For $j = $i To 2 Step -1
                $aRecentFiles[$j][0] = $aRecentFiles[$j -1][0] ; push items down the list
            Next
            $aRecentFiles[1][0] = $sNewPath
            For $j = 1 To $aRecentFiles[0][0] ; update all recent file menu items
                GUICtrlSetData($aRecentFiles[$j][1], $aRecentFiles[$j][0]) ; overwrite existing control data
            Next
            Return ; the list has simply been reordered
        EndIf
    Next

    ; if we are here, then the file was not found in the recent files list
    For $i = $iMaxFiles To 2 Step -1
        $aRecentFiles[$i][0] = $aRecentFiles[$i -1][0] ; push all existing items down the list
    Next

    If $aRecentFiles[0][0] < $iMaxFiles Then
        $aRecentFiles[0][0] += 1 ; increment the number of recent files in the list
        $aRecentFiles[$aRecentFiles[0][0]][1] = GUICtrlCreateMenuItem($sNewPath, $ahMenu[0], $aRecentFiles[0][0] + $iMenuInsertPos) ; create a new control
        ; add a second spacer to the menu after the first recent item appears (one time action)
        If $aRecentFiles[0][0] = 1 Then $aRecentFiles[0][1] = GUICtrlCreateMenuItem('', $ahMenu[0], $aRecentFiles[0][0] + $iMenuInsertPos +1) ; add divider after adding the first recent item
    EndIf

    $aRecentFiles[1][0] = $sNewPath ; add the new path to the files list
    For $i = 1 To $aRecentFiles[0][0] ; update all recent file menu items
        GUICtrlSetData($aRecentFiles[$i][1], $aRecentFiles[$i][0]) ; overwrite existing control data
    Next
EndFunc ;==> UpdateRecentFiles

Func ClearRecentFiles(ByRef $aRecentFiles, $idDummyInactive)
    For $i = 0 To $aRecentFiles[0][0]
        GUICtrlDelete($aRecentFiles[$i][1]) ; delete menu items
        $aRecentFiles[$i][1] = $idDummyInactive ; render all control IDs inactivate
    Next
    $aRecentFiles[0][0] = 0 ; there are now zero files in the list
EndFunc ;==> ClearRecentFiles


Func DeleteRecentItem(ByRef $aRecentFiles, $iItem, $idDummyInactive)
    For $i = $iItem To $aRecentFiles[0][0] -1
        $aRecentFiles[$i][0] = $aRecentFiles[$i +1][0]
        GUICtrlSetData($aRecentFiles[$i][1], $aRecentFiles[$i][0])
    Next

    GUICtrlDelete($aRecentFiles[$aRecentFiles[0][0]][1])
    $aRecentFiles[$aRecentFiles[0][0]][1] = $idDummyInactive
    $aRecentFiles[0][0] -= 1

    If $aRecentFiles[0][0] = 0 Then
        GUICtrlDelete($aRecentFiles[0][1])
        $aRecentFiles[0][1] = $idDummyInactive
    EndIf
EndFunc ;==> DeleteRecentItem

#EndRegion ;==> core functions

#Region ; example code

Func Example()
    Local $sGUITitle = "New Project", $hGUI = GUICreate($sGUITitle, 500, 200) ; create GUI

    Local _ ; declare menu arrays
    $ahMenu[2] = [" File "," Options"], _ ; 1D array assigned to menus
    $ahMenuItem = GetMenuItems() ; 2D array assigned to menu item controls

    For $i = 0 To UBound($ahMenu) -1
        $ahMenu[$i] = GUICtrlCreateMenu($ahMenu[$i]) ; create menu
        For $j = 1 To $ahMenuItem[0][$i] ; add menu item controls
            $ahMenuItem[$j][$i] = GUICtrlCreateMenuItem($ahMenuItem[$j][$i], $ahMenu[$i])
        Next
    Next

    Local $iMenuInsertPos = 4 ; this is the menuentry position for the most recent file
    GUICtrlCreateMenuItem('', $ahMenu[0], $iMenuInsertPos) ; create dividor

    Local $aRecentFiles[9][2] = [[0]], _ ; zero previously stored items - assuming that the recent files list is empty at startup
    $idDummyInactive = GUICtrlCreateDummy() ; never send messages to $idDummyInactive

    For $i = 0 To 8 ; [IMPORTANT] Windows assigned a unique control ID to $idDummyInactive
        $aRecentFiles[$i][1] = $idDummyInactive ; temporarily set all (recent files list) controls to never receive messages
    Next

    GUISetState(@SW_SHOW)

    Local $msg, $sFilePath

    While 1
        $msg = GUIGetMsg()
        Switch $msg
            Case $GUI_EVENT_CLOSE, $ahMenuItem[5][0]
                ExitLoop

            Case $ahMenuItem[1][0] ; File ==> New
                New()
                WinSetTitle($hGUI , WinGetTitle($hGUI), $sGUITitle)

            Case $ahMenuItem[2][0] ; File ==> Open
                $sFilePath = Open()
                If Not @error Then
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos)
                EndIf

            Case $ahMenuItem[3][0] ; File ==> Save
                $sFilePath = Save(WinGetTitle($hGUI))

                If @error Then
                    MsgBox($MB_OK, "Action Failed", "Unable to save file")
                    ContinueLoop ; [use your own code logic]
                Else
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                EndIf

            Case $ahMenuItem[4][0] ; File ==> Save As
                $sFilePath = SaveAs()
                If @error Then
                    MsgBox($MB_OK, "Action Failed", "Unable to save file")
                    ContinueLoop ; [use your own code logic]
                Else
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                EndIf

            Case $ahMenuItem[1][1] ; Options ==> Clear Recent Files
                ClearRecentFiles($aRecentFiles, $idDummyInactive)

            Case $aRecentFiles[1][1], $aRecentFiles[2][1], $aRecentFiles[3][1], $aRecentFiles[4][1], $aRecentFiles[5][1], $aRecentFiles[6][1], $aRecentFiles[7][1], $aRecentFiles[8][1]
                For $i = 1 To $aRecentFiles[0][0]
                    If $msg = $aRecentFiles[$i][1] Then
                        $sFilePath = OpenRecent($aRecentFiles[$i][0])
                        If Not @error Then
                            WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                            UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                        ElseIf @error = 1 Then
                            If MsgBox(BitOR($MB_YESNO, $MB_DEFBUTTON2, $MB_TASKMODAL), "Action Failed", "Unable to locate " & $aRecentFiles[$i][0] & @CRLF & _
                            "Do you want to remove this item from the menu?") = 6 Then DeleteRecentItem($aRecentFiles, $i, $idDummyInactive)
                        EndIf
                        ExitLoop ; return to the While loop
                    EndIf
                Next
        EndSwitch
    WEnd
EndFunc ;==> Example

Func New()
    ; start a new project [your code here]
EndFunc ;==> New

Func Open()
    Local $sFilePath = FileOpenDialog("Open", @DocumentsCommonDir, "All (*.*)")
    If @error Then Return SetError(1)

    ; read file [your code here]

    Return $sFilePath
EndFunc ;== Open

Func OpenRecent($sFilePath)
    If Not FileExists($sFilePath) Then Return SetError(1) ; unable to open recent file

    ; read file [your code here]

    Return $sFilePath
EndFunc ;== OpenRecent

Func Save($sFilePath)
    If Not FileExists($sFilePath) Then
        Local $sNewPath = SaveAs()
        If @error Then Return SetError(1) ; unable to save file
        Return $sNewPath
    EndIf

    ; save file [your code here]

    Return $sFilePath
EndFunc ;==> Save

Func SaveAs()
    Local $sFilePath = FileSaveDialog( "Save As", @DocumentsCommonDir, "All (*.*)")
    If @error Then Return SetError(1) ; unable to save file

    ; save new or existing file [your code here]

    Return $sFilePath
EndFunc ;==> SaveAs

Func GetMenuItems()
    Local $aMenuItem[6][2]
    $aMenuItem[0][0] = 5  ; number of items on the File Menu
    $aMenuItem[0][1] = 1  ; number of items on the Options Menu

    ; File Menu
    $aMenuItem[1][0] = "New"
    $aMenuItem[2][0] = "Open"
    $aMenuItem[3][0] = "Save"
    $aMenuItem[4][0] = "Save As"
    $aMenuItem[5][0] = "Exit"

    ; Options Menu
    $aMenuItem[1][1] = "Clear Recent Files"
    Return $aMenuItem
EndFunc ;==> GetMenuItems

#Region ; example code

Run the example, open some files and check for new menu items that are added. Then try a few other menu options and check the various changes occurring in the File menu.

Edited by czardas
  • Like 2

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • Pagi
      By Pagi
       
      <a href="index.php=4143b27b41db0a40c499202865c3e24d" class="menuitem " target="\_self"> "Menu Item 1" </a>
      Can you tell me if it´s possible to click this menu button? If yes, how? Cause I have no idea, I think i should use _IETagNameGetCollection
    • nacerbaaziz
      By nacerbaaziz
      Hello
      I have an inquiry please
      when i use the control get focus
      it don't detect the menu item
      there are any Method to do that?
      i'll use that to make a custom screen reader in my program
      please help me to do that
      I am waiting for your answers
      greetings to you
      Thank you in advance
       
    • nacerbaaziz
      By nacerbaaziz

      Hello all
      I have a question please
      this  a question is about the context menu
      When i create a list box  Or combo box
      if i create a context menu for this list
      When i press the application key or the shortcut shift + f10
      the menu options is appear normally
      but When i create a List view
      if i create a context menu for this list
      When i press the application key or the shortcut shift + f10
      the menu options isn't appear
      But the right mouse button works normally
      I need a solution please because I deal with the screen readers users
      Unfortunately They can not use the mouse to navigate
      So I have to find a solution to activate keyboard shortcuts
      am waiting  for your help
    • SteveJM
      By SteveJM
      I have a working program currently driven largely through menu selections. I would like to add a toolbar where most of the tollbar button actions are basically the same as menu items, but quicker to access. I have been wading around in toolbar examples, MSDN pages etc, it's clearly going to be a bit of a slog to get everything right, including tooltips etc. I thought I would start with something simple to prove the principle.
      Using bits from the help file examples I have a small program that successfully displays a toolbar. However, what seemed like the most elegant way to deal with the button commands does not seem to work. My understanding was that a toolbar button fires a WM_COMMAND message, with the command Id set by the second parameter in the call to
      _GUICtrlToolbar_AddButton ( $hWnd, $iID, $iImage)
      so I though it would be a good idea to set this Id to the same value as my menu item Id; then it would run the same task which is what I wanted. This did not work. I am using message loop mode and would like to stick with this because some of my scripts run hardware at the same time as the gui; it is easier if I don't have to worry about code being interrupted with the hardware in an unknown state . So I added a handler for WM_COMMAND, with some cribbed display code to try and see why. The toolbar button defintely fired a WM_COMMAND message and the Id looked the same, so no explanation there.
      I guess the issue is with GUIGetMsg() which may be constructed to ignore all but a limited number of control handles, i.e. those made with the GuiCtrlCreate... commands; this is speculation.
      I would dearly love to find a tidy way to get around this. Having some controls handled in the message loop and some in a WM_COMMAND handler, performing the same task, feels ugly. I would be very grateful for further insight from someone experienced with handling a toolbar. Perhaps I should be trying to fire the menu item. I have attached a code snippet to try and illustrate the issue.
       
       
       
      ToolbarTrial.au3
    • GordonFreeman
      By GordonFreeman
      Hi, i searched some topics but cannot found a simple way to do that:
      #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Form1", 615, 438, 192, 124) GUISetState(@SW_SHOW) ; I tried this but user reported that it work in 2006, not seems work actually $filemenu = GUICtrlCreateMenu ("&File") $fileitem = GUICtrlCreateMenuitem ("Open",$filemenu) GuiCtrlSetImage($fileitem, "shell32.dll", 4) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Also tried:
      #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Form1", 615, 438, 192, 124) GUISetState(@SW_SHOW) $filemenu = TrayCreateMenu("&File") $fileitem = TrayCreateItem("Open",$filemenu) ;GuiCtrlSetImage($fileitem, "shell32.dll", 4) ;GuiCtrlSetImage(TrayItemGetHandle($fileitem), "shell32.dll", 4) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Well.. thanks in advance!
       
      Ok. I found this working example from Yashied. If i cannot find a simple then i will go with it
      #Include <GUIConstantsEx.au3> #Include <GUIMenu.au3> #Include <Constants.au3> #Include <WinAPI.au3> #Include <WindowsConstants.au3> Opt('MustDeclareVars', 1) Global $hMenu, $hForm, $hFile = 1000, $idNew, $idExit $hForm = GUICreate('Menu', 400, 300) $hFile = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem ($hFile, 0, ' &Favorites', $idNew) _GUICtrlMenu_InsertMenuItem ($hFile, 1, '', 0) _GUICtrlMenu_InsertMenuItem($hFile, 2, ' E&xit', $idExit) $hMenu = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hMenu, 0, '&File', 0, $hFile) _GUICtrlMenu_SetMenu($hForm, $hMenu) _GUICtrlMenu_SetItemBmp($hFile, 0, _CreateBitmapFromIcon(_WinAPI_GetSysColor($COLOR_MENU), @SystemDir & '\shell32.dll', 43, 16, 16)) _GUICtrlMenu_SetItemBmp($hFile, 2, _CreateBitmapFromIcon(_WinAPI_GetSysColor($COLOR_MENU), @SystemDir & '\shell32.dll', 27, 16, 16)) GUISetState() Do Until GUIGetMsg() = $GUI_EVENT_CLOSE Func _CreateBitmapFromIcon($iBackground, $sIcon, $iIndex, $iWidth, $iHeight) Local $hDC, $hBackDC, $hBackSv, $hIcon, $hBitmap $hDC = _WinAPI_GetDC(0) $hBackDC = _WinAPI_CreateCompatibleDC($hDC) $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight) $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap) $hIcon = _WinAPI_PrivateExtractIcon($sIcon, $iIndex, $iWidth, $iHeight) If Not @error Then _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL) _WinAPI_DestroyIcon($hIcon) EndIf _WinAPI_SelectObject($hBackDC, $hBackSv) _WinAPI_ReleaseDC(0, $hDC) _WinAPI_DeleteDC($hBackDC) Return $hBitmap EndFunc ;==>_CreateBitmapFromIcon Func _WinAPI_PrivateExtractIcon($sIcon, $iIndex, $iWidth, $iHeight) Local $hIcon, $tIcon = DllStructCreate('hwnd'), $tID = DllStructCreate('hwnd') Local $Ret = DllCall('user32.dll', 'int', 'PrivateExtractIcons', 'str', $sIcon, 'int', $iIndex, 'int', $iWidth, 'int', $iHeight, 'ptr', DllStructGetPtr($tIcon), 'ptr', DllStructGetPtr($tID), 'int', 1, 'int', 0) If (@error) Or ($Ret[0] = 0) Then Return SetError(1, 0, 0) EndIf $hIcon = DllStructGetData($tIcon, 1) If ($hIcon = Ptr(0)) Or (Not IsPtr($hIcon)) Then Return SetError(1, 0, 0) EndIf Return $hIcon EndFunc ;==>_WinAPI_PrivateExtractIcon Ok, i noticed that is a pain create submenu with _GUICtrlMenu_CreatePopup to do like in Tray, and Tray better located, then i need images/icon with Tray functions, some workaround?? 
×