Jump to content

Recent Files Logic

Recommended Posts

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>


#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
            $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
            Return ; the list has simply been reordered

    ; 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

    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

    $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
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
    $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])

    $aRecentFiles[$aRecentFiles[0][0]][1] = $idDummyInactive
    $aRecentFiles[0][0] -= 1

    If $aRecentFiles[0][0] = 0 Then
        $aRecentFiles[0][1] = $idDummyInactive
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])

    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


    Local $msg, $sFilePath

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

            Case $ahMenuItem[1][0] ; File ==> 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)

            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]
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here

            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]
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here

            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)
                        ExitLoop ; return to the While loop
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

    ; 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

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

    • By nacerbaaziz
      hi dears, i have a question please
      am now working for an audio player for the blind users
      and i added an option to open a file from the right click context menu.
      but here there is a small problem, i hope that you can help me to find a solution for it.
      the problem is as follow :
      when i added the problem into the context menu it work with successfuly if the user select just one file.
      but if the user selected more then one file, here the problem
      a multiple sections of the program will open.
      my question is :
      how i can detect the multiple selection from the context menu?
      i know that it so  hard, but i know also that you are harder than it
      for that dears please try to give me an solution for that problem.
      thanks in advance
    • By wolflake
      I've used PowerPro for years and it had a feature that let you turn an array into a menu.  It also let you know if it got a right click. I liked the right click feature because it doubled the menu item usage ie left/right on the same menu item.  I tried recreate that usage with a context menu.   With _ArrayToMenu you can pass menu items for 1 and 2 dimensional arrays. Note you can indicate sub-menu but using a -> at the end of the item.  Use a - to make a separater. Optionally you can pass Tooltips by passing a second array. This array must have the same dimensions as the items array even if not all the elements contain tips. The UDF returns an array with this index of row (and if submenus column), the third element is 1 if right clicked otherwise 0 and the fourth element of the array is the label of the menu item. If the user escapes without choosing an item the the first element returns a -1.  Same thing if the user clicks on another window.   @error returns 1 if there was no array passed, 2 if the second array has different dimensions than the first.   If you use the same label in two parts of your menu you will have to distinguish them.  You can do that by additionally making sure that the selection matches the index number(s).  Note that you'll have to match the index number plus label before you match just the label because Switch will use the first match it can find.  In the example below I am using a check on which column the data is in.  If it's not in the first column then it must be the other "Beef".   Beef
      Col2 > Beef
                  Bread   $aR = _ArrayToMenu($aM) Switch $aR[3]
      Case "Beef" and $aR[2] = 0
       Case "Beef"
      EndSwitch   On a technical note I attached the context menu to the window itself not a dummy control and I didn't use _GUICtrlMenu_TrackPopupMenu.  Instead I launched the context menu with "send shift-F10" and waited for GuiGetMsg() to give me the selection. Right click is picked up by GUIRegisterMsg WM_RBUTTONUP and Tooltips are done with GUIRegisterMsg WM_MENUSELECT. The whole thing is done with 3 functions. 
      I won't tell you how long it took me to figure this out but I'll say that on one of my early attempts it had two windows running at once and one was just to recieve the right click an tell the other it got it.  Suffice it to say I'm no wiz at Autoit but I really appreciate the support the community offers and I hope someone finds this useful. BTW I wrote a script to produce 1d and 2d auotit array code from excel in case you want to model your menu in excel. Here is the link.
      _ArrayToMenu() UDF
      ;ArrayToMenu with submenus, tooltips, rightclick and esc to close. ; #INDEX# ======================================================================================================================= ; Title .........: _ArrayToMenu ; AutoIt Version : ; Description ...: Show an array as a popup menu optionally with tooltips and right click. ; Author(s) .....: Rick Sharp ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name..........: _ArrayToMenu($aArray_menu[,$aArray_tooltips]) ; Description...: Display an array as a menu and return the users choice, display tooltips(optional), return right click. ; Syntax........: _ArrayToMenu($aArray_menu[,$aArray_tooltips]) ; ; Parameters....: ; Required......: A 1d or 2d array of menu items ; Use a minus sign in the item to indicate a menu separator. ; Use -> at the end of an item to indicate a sub-menu. ; Optional......: A 1d or 2d array of tooltips. The array must use the same dimensions as the menu items array. ; ; Return values.: An Array ; $aArray[0] is index of the row (-1 if exited with no choice) ; $aArray[1] is index of the column ; $aArray[2] is 1 if right clicked ; $aArray[3] is the selected item (if any) ; Notes.........: If the user clicks on another window the ArrayToMenu returns as if esc were pressed. ; Sub-Menus are limited to 10 levels if you need more change $ahM[10] ; =============================================================================================================================== ; #VARIABLES# =================================================================================================================== ; Global $__g_iRT = 0, $__g_aTT1, $__g_ahi ; "$__g_iRT" for right click flag, "$__g_aTT1" for tips, "$__g_ahi" for index of id's in menu and tips ; =============================================================================================================================== ; #@error# ====================================================================================================================== ; 1 - First parameter is Not an array ; 2 - The Menu/Items array and the Tips array are not the same number of dimensions ; =============================================================================================================================== #include-once #include <WindowsConstants.au3> #include <GuiMenu.au3> #include <array.au3> #include <misc.au3> Func _ArrayToMenu($aMenu, $att = "") If Not IsArray($aMenu) Then Return SetError(1, 0, -1) Global $__g_iRT = 0 Local $ahM[10], $iCcnt = UBound($aMenu, 2), $iRcnt = UBound($aMenu), $iRow, $iCol, $b_Esc If UBound($aMenu, 2) = 0 Then _ArrayColInsert($aMenu, 1) ;if 1d array make it 2d EndIf ;Prep Loop to make Menus and Sub-Menus $iRcnt = UBound($aMenu) ;Count of Rows/Items $iCcnt = UBound($aMenu, 2) ;Count of Cols/Menus GUIRegisterMsg($WM_RBUTTONUP, "WM_RBUTTONUP") ;handles Right Click If IsArray($att) Then If UBound($att, 2) = 0 Then _ArrayColInsert($att, 1) If UBound($att, 2) <> $iCcnt Or UBound($att) <> $iRcnt Then Return SetError(2, 0, -1) ;$amenu and $att not same dimensions Global $__g_aTT1 = $att ;added $__g_aTT1 because $att was not seen by WM_MenuSelect for tooltips GUIRegisterMsg($WM_MENUSELECT, "WM_MENUSELECT") ;handles tooltips EndIf Local $mPos = MouseGetPos() #Region ### START Koda GUI section ### Form= $hMenu = GUICreate("C_menu", 10, 10, $mPos[0], $mPos[1], $WS_POPUP, $WS_EX_TOPMOST) GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### ;Build Menus Global $__g_ahi[$iRcnt + 1][$iCcnt + 1] ;array to hold Menu Item id's $iMcnt = 0 ;Menu count $ahM[$iMcnt] = GUICtrlCreateContextMenu() ;if the array element is null then it falls through and nothing happens For $j = 0 To $iCcnt - 1 ;for each Column/Menu For $i = 0 To $iRcnt - 1 ;for each Row/Item If StringRight($aMenu[$i][$j], 2) = "->" Then ;Sub-Menu $aMenu[$i][$j] = StringTrimRight($aMenu[$i][$j], 2) ;remove -> $iMcnt += 1 $ahM[$iMcnt] = GUICtrlCreateMenu($aMenu[$i][$j], $ahM[$j]) ; $__g_ahi[$i][$j] = $ahM[$iMcnt] ElseIf $aMenu[$i][$j] > "" Then ;Normal item If StringLeft($aMenu[$i][$j], 1) = "-" Then $aMenu[$i][$j] = "" $__g_ahi[$i][$j] = GUICtrlCreateMenuItem($aMenu[$i][$j], $ahM[$j]) EndIf Next Next Send("+{F10}") ;sends right click to open context menu While 1 $nMsg = GUIGetMsg() If $nMsg > 0 Then ;ConsoleWrite("nMsg= " & $nMsg & @CRLF) $iRow = _ArraySearch($__g_ahi, $nMsg) $iCol = _ArraySearch($__g_ahi, $nMsg, 0, 0, 0, 0, 0, $iRow, True) ExitLoop EndIf If _IsPressed("1B") = 1 Or WinActive("C_menu") = 0 Then $b_Esc = -1 ExitLoop EndIf WEnd ;*** Done *** GUIDelete($hMenu) Local $aAr1[4] $aAr1[0] = $iRow $aAr1[1] = $iCol $aAr1[2] = $__g_iRT If Not $b_Esc = -1 Then $aAr1[3] = $aMenu[$aAr1[0]][$aAr1[1]] Else $aAr1[0] = -1 EndIf Return $aAr1 EndFunc ;==>_ArrayToMenu ;Check for Right Click Func WM_RBUTTONUP($hMenu, $iMsg, $iwParam, $ilParam) $__g_iRT = 1 ;Mark as rclicked Send("{Enter}") ;choose the item EndFunc ;==>WM_RBUTTONUP ;Tooltips Func WM_MENUSELECT($hMenu, $iMsg, $iwParam, $ilParam) Local $idMenu = BitAND($iwParam, 0xFFFF) Local $iRow, $iCol If $idMenu > 0 Then $iRow = _ArraySearch($__g_ahi, $idMenu) If $iRow > -1 Then $iCol = _ArraySearch($__g_ahi, $idMenu, 0, 0, 0, 0, 0, $iRow, True) EndIf If $iCol > -1 And $iRow > -1 And $__g_aTT1[$iRow][$iCol] > " " Then ToolTip($__g_aTT1[$iRow][$iCol]) Else ToolTip("") EndIf EndIf EndFunc ;==>WM_MENUSELECT Example 1 Simple 1d array with tooltips, item separator and right click.
      #include "ArrayToMenu.au3" ;Simple 1d array with tooltips item separator and right click. $aM = StringSplit("Zero,One,-,Two/Two_R", ",", 3) ;make an array for the menu items $aT = StringSplit("Zero,One,-,", ",", 3) ;make an array for the menu Tooltips $aR = _ArrayToMenu($aM,$aT) if @error then ConsoleWrite(@error & @CRLF) EndIf ConsoleWrite("R: " & $aR[0] & " " & "C: " & $aR[1] & " " & "Rclick: " & $aR[2] & " " & "Item: " & $aR[3] & @CRLF) If $aR[0] = -1 Then ;either hit escape or clicked on another window ConsoleWrite("Esc" & @CRLF) Exit EndIf ;_ArrayDisplay($aR) Switch $aR[3] Case "Zero" Zero() Case "One" One() Case "Two/Two_R" And $aR[2] = 0 ;No Rclick Two() Case "Two/Two_R" And $aR[2] = 1 ;Rclick Two_R() EndSwitch Func Zero() ConsoleWrite("You chose: Zero" & @CRLF) EndFunc ;==>Zero Func One() ConsoleWrite("You chose: One" & @CRLF) EndFunc ;==>One Func Two() ConsoleWrite("You chose: Two" & @CRLF) EndFunc ;==>Two Func Two_R() ConsoleWrite("You chose: Two_R" & @CRLF) EndFunc ;==>Two_R Example 2 2d array with sub-menu
      #include "ArrayToMenu.au3" ;2d array with a sub-menu dim $aM[4][2] = [["Beef", "Orange"], ["Pork", "Apple"], ["Chicken", "Grape"], ["Fruit->", ""]] ;Note you don't need a tooltip for every item but you at least need a place holder in the array dim $aT[4][2] = [["Red Meat", "Fruit"], ["Other white meat", "Fruit"], ["White meat", "Fruit"], ["", ""]] $aR = _ArrayToMenu($aM,$aT) if @error Then ConsoleWrite(@error & @CRLF) Exit EndIf If $aR[0] = -1 Then ConsoleWrite("Esc" & @CRLF) Exit EndIf ConsoleWrite("R: " & $aR[0] & " " & "C: " & $aR[1] & " " & "Rclick: " & $aR[2] & " " & "Item: " & $aR[3] & @CRLF) Switch $aR[3] Case "Beef" Beef() Case "Pork" Pork() Case "Chicken" Chicken() Case "Orange" ConsoleWrite("Oranges are good for you!" & @CRLF) ConsoleWrite("Oranges" & " $aR[0] = " & $aR[0] & " $aR[1] = " & $aR[1] & @CRLF) Case "Apple" ConsoleWrite("Apples are good for you!" & @CRLF) Case "Grape" ConsoleWrite("Grapes are good for you!" & @CRLF) EndSwitch Func Beef() ConsoleWrite("Beef" & " $aR[0] = " & $aR[0] & " $aR[1] = " & $aR[1] & @CRLF) EndFunc ;==>Beef Func Pork() ConsoleWrite("Pork" & " $aR[0] = " & $aR[0] & " $aR[1] = " & $aR[1] & @CRLF) EndFunc ;==>Pork Func Chicken() ConsoleWrite("Chicken" & @CRLF) EndFunc ;==>Chicken  
    • By nacerbaaziz
      Hello my friends
      Can we create a single context menu on more than one item?
      For example, a context menu includes standardized options on more than one control, such as buttons or check boxes
      to Create a context menu on one item am using this function
      GUICtrlCreateContextMenu ($ HWND)
      How to link it with more than one element please?
      or if we can't do that, please give me a solution
      so i tried to add an context menus to all the controls but the script will be long, for that if their are any solutions i hope to give it to me
      thanks in advanced
    • 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
    • By nacerbaaziz
      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
  • Create New...