Autosave

This set of functions works as an independent module to provide an AutoSave command on the File menu (Mac OS) or Data menu (Windows, because it doesn't allow modifying the File menu). Selecting the menu item opens a dialog box through which you can select which of 3 periodic actions it will take:
1) Save the existing experiment in place
2) Save a copy of the experiment, with a sequence number appended to its name
3) Display a reminder dialog.

Note it doesn't actually do anything until you manually select the command and turn on auto-save, so it's not completely "auto".

More details in the comments. Because it includes the independentModule pragma, this file won't appear on the list of Procedure windows. If you want to experiment with the code, remove that pragma to make it easier to work with the file.

#pragma rtGlobals=1     // Use modern global access method.
#pragma Version=1.00        // 2011-3-30 DRD
#pragma independentModule = HOBIAutoSave

//--------------------------------------------------------------------------
// Implements automatic background saving or copying of the current experiment file.
// Places a dynamic auto-save item in the file menu.  When auto-save is not already active,
// the auto-save command opens a dialog for selecting the period and mode.  When it is active,
// the text of the menu item shows how much time remains before the next save.
//
// Mode is either overwrite, meaning each save replaces the existing experiment file, or copy,
// meaning a new copy is created each time, or reminder, which just puts up an alert box.
//  Note that in copy mode, files that are not included in the packed experiment file will NOT be backed up.
//  In copy mode, each copy of the file has a sequence number appended to its name.
//
// This creates a data folder that holds just two settings, the mode and the count value used to auto-name
// files in backup mode.  The period and status of the background task are stored by Igor's background
// scheduler.
//
// David Dana, 2011/3/30
//--------------------------------------------------------------------------
#ifdef WINDOWS
    Menu "Data", dynamic
        "-"
        ASMenuItem(), /Q, ASMenuAction()
    End Menu
#else
    Menu "File", dynamic
        "-"
        ASMenuItem(), /Q, ASMenuAction()
    End Menu
#endif

Constant kOverwriteMode = 1
Constant kBackupMode = 2
Constant kRemindMode = 3
StrConstant ASSettingsFolder = "root:Packages:HOBIAutoSave"

//--------------------------------------------------------------------------
//  Returns a string for the Auto-save menu item.  The text of the menu item depends on the status of the
//  background task.
//--------------------------------------------------------------------------
Function/S ASMenuItem()
    if (ASIsActive())   // If it's active, display the amount of time remaining before the next save
        variable remaining = ASSecondsRemaining()
        string remainingStr
        if (remaining > 60)
            remaining = round(remaining/60)
            remainingStr = "in " + num2str(remaining) + " min"
        else
            remainingStr = "in " + num2str(round(remaining)) + " sec"
        endif
        string menuStr
        if (ASMode() == kBackupMode)
            menuStr = CheckMark() + "Auto-Backup "
        elseif (ASMode() == kRemindMode)
            menuStr = CheckMark() + "Auto-Remind "
        else
            menuStr =  CheckMark() + "Auto-Save "
        endif
        return menuStr + remainingStr
    else
        return "Auto-Save..."
    endif
End Menu
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  This is called when the menu item is selected.  Depending on whether the background task is running,
//  either stops it or asks the user to set it up
//--------------------------------------------------------------------------
Function ASMenuAction()
    GetLastUserMenuInfo
    if (ASIsActive())
        ASStop()
    else
        ASSetup()
    endif
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Stops the background task
//--------------------------------------------------------------------------
Function ASStop()
    CtrlNamedBackground AutoSave, stop
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Starts the background task, with the given period in minutes
//--------------------------------------------------------------------------
Function ASStart(minutes)
variable minutes

    if (minutes < 1)
        minutes = 1
    endif
    CtrlNamedBackground AutoSave, proc=ASTask, dialogsOK = 0, burst = 0
    CtrlNamedBackground AutoSave, period=minutes*3600
    CtrlNamedBackground AutoSave, start=ticks+minutes*3600
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Background task that does the actual work.  Taks a WMBackgroundStruct, as required for any named
//  background task, but doesn't use it.  This routine must also return 0; nonzero tells Igor to halt the
//  taks.
//--------------------------------------------------------------------------
Function ASTask(s)
STRUCT WMBackgroundStruct &s        // required by Igor but unused

        // First check whether a save is even necessary
    ExperimentModified      // this only works in overwrite mode; saving a copy doesn't change the "modified" status
    if (v_flag == 0)        // the experiment is unmodified; no need to save
        return 0
    endif

    if (ASMode() == kOverwriteMode)
        print "Auto-saving " + time()   // print in history
        SaveExperiment
    elseif (ASMode() == kBackupMode)
        ASSetBackupCount(ASBackupCount() + 1)
        string newName = IgorInfo(1) + "_" + Num2Str(ASBackupCount())
        print "Auto-saving \"" + newName + "\",", time()    // print in history
        SaveExperiment/C/P=home as newName
    else
        ASRemind()
    endif
    return 0
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Handles the setup and user input necessary to start auto-save.
//--------------------------------------------------------------------------
Function ASSetup()
   
    PathInfo home       // First check whether the experiment has previously been saved
    if (v_flag == 0)    // the home path doesn't exist; the experiment has never been saved
        DoAlert 1, "To use auto-save, you must first give the experiment a name and location.  Save it now?"
        if (v_flag != 1)        // 1 = yes clicked
            return 0
        endif
        SaveExperiment      // will prompt user for name and location
        PathInfo home
        if (v_flag == 0)    // still not saved; user must have cancelled
            DoAlert 0, "Auto-save cancelled."
            return 0
        endif
    endif
   
    // Prepare to get user input
    CtrlNamedBackground AutoSave, status        // Find the current period setting of the task
    variable period = NumberByKey ("PERIOD", s_info) / 3600
    if (period < 1) // not yet initialized
        period = 10
    endif
    // put the period into a user-readable string, with units.  This string should match one of the choices in the popup
    // menu below, so that the popup will appear with the current setting selected
    string perStr
    if (period > 1)
        perStr = num2str(period) + " minutes"
    else
        perStr = num2str(period) + " minute"
    endif
    // Make and display a simple dialog with popup menus display the period and mode choices
    Prompt perStr, "Auto-save Interval", popup "1 minute;2 minutes;5 minutes;10 minutes;30 minutes;60 minutes;120 minutes"
    variable mode = ASMode()
    Prompt mode, "Replace,  copy, or show reminders?", popup "Replace original with each save;Make new backup copy with each save;Do not save, only show reminders"
    DoPrompt/HELP="Note that in copy mode, notebooks, procedures and other files that are separate from the experiment file will not be backed up." "Auto-save Settings", perStr, mode
    if (v_flag == 1)    // cancel was clicked; never mind
        ASStop()
        return 0
    endif
    ASSetMode(mode)
    period = str2num(perStr)
    ASStart(period)     // start the task
    return period
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Returns a reference to the data folder containing the auto-save settings.  If it doesn't yet exist, creates
//  it.  The actual name of the folder is contained in a string constant defined at the top of the file
//--------------------------------------------------------------------------
Function/DF ASSettingsFolder()
    DFREF dfr = $ASSettingsFolder
    if (DataFolderRefStatus (dfr) == 0)
        NewDataFolder/O root:Packages
        NewDataFolder/O $ASSettingsFolder
        dfr = $ASSettingsFolder
        Variable/G dfr:SaveMode = kOverwriteMode
        Variable/G dfr:BackupCount      // Igor will initialize to zero, or leave alone if it already exists
    endif
    dfr = $ASSettingsFolder
    return dfr
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
// Returns the two-character prefix for a checked menu item
//--------------------------------------------------------------------------
Function/S Checkmark()
    return "!" + Num2Char(18)
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Returns 1 if the background task is running, 0 if not
//--------------------------------------------------------------------------
Function ASIsActive()
    CtrlNamedBackground AutoSave, status
    if (stringmatch(s_info, "*RUN:1*"))
        return 1
    else
        return 0
    endif
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
//  Returns number of seconds remaining before the background task next runs.  Answer is meaningless if
//  task isn't running (most likely negative)
//--------------------------------------------------------------------------
Function ASSecondsRemaining()
    variable remaining
    CtrlNamedBackground AutoSave, status
    remaining = NumberByKey ("NEXT", s_info) - ticks
    remaining /= 60
    return remaining
End Function
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
//  Returns value of the mode variable stored in the auto-save folder
//--------------------------------------------------------------------------
Function ASMode()
    DFREF dfr = ASSettingsFolder()
    NVAR mode = dfr:SaveMode
    return mode
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
//  Sets the value of the mode variable stored in the auto-save folder, and returns its new value
//--------------------------------------------------------------------------
Function ASSetMode(newmode)
variable newMode

    DFREF dfr = ASSettingsFolder()
    NVAR mode = dfr:SaveMode
    mode = newmode
    return mode
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
//  Returns the value of the backup count variable stored in the auto-save folder.  This is used for naming
//  multiple backup files.
//--------------------------------------------------------------------------
Function ASBackupCount()
    DFREF dfr = ASSettingsFolder()
    NVAR count = dfr:BackupCount
    return count
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
//  Sets the value of the backup count variable stored in the auto-save folder, and returns its new value
//--------------------------------------------------------------------------
Function ASSetBackupCount(newCount)
variable newcount

    DFREF dfr = ASSettingsFolder()
    NVAR count = dfr:BackupCount
    count = newCount
    return count
End Function
//--------------------------------------------------------------------------


//--------------------------------------------------------------------------
//  Display a small non-modal reminder window.  User can dismiss it and also turn off future reminders
//--------------------------------------------------------------------------
Function ASRemind()
    if (WinType("ASReminderPanel") == 7)
        DoWindow/F ASReminderPanel
    else
        NewPanel /k=1/W=(84,77,366,174)/N=ASReminderPanel  as "Reminder"
        ModifyPanel fixedSize=1
        TitleBox title0,pos={47,15},size={184,16},title="Remember to save your work"
        TitleBox title0,font="Lucida Grande",fSize=13,frame=0
        Button OKButton,pos={21,52},size={70,22},title="OK"
        Button StopButton,pos={109,52},size={150,22},title="Stop Reminders"
        Button OKButton, proc=ASRemind_OKProc
        Button StopButton,proc=ASRemind_StopProc
    endif
    beep
    return 0
End Function


Function ASRemind_OKProc(ctrlName) : ButtonControl
    String ctrlName
    DoWindow/K ASReminderPanel
End

Function ASRemind_StopProc(ctrlName) : ButtonControl
    String ctrlName
    ASStop()
    DoWindow/K ASReminderPanel
End
//--------------------------------------------------------------------------

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More