--[[ @title: Clean Up Notes @author: Jane Taubman @lastupdated: 5 Feb 2021 @version: 1.3 @description: Optionally 1. Removes Blank lines from the start of Notes 2. Converts Note Records with a Single Link to a Local Note. Updates 1.1 Updated Progress Bar to latest version. 1.2 V7 Compatibility 1.3 Correct problems with Rich Text Notes with source citations in V7. ]] require("iuplua"); iup.SetGlobal("CUSTOMQUITMESSAGE","YES"); --------------------------------------- -- Main Logic --------------------------------------- function main() -- Prompt for confirmation using iup.GetParm %b are tick boxes -- set initial values premoveblanks = 1 pconvertnotes = 1 -- Prompt User to Confirm Options ret, premoveblanks, pconvertnotes = iup.GetParam("Family Historian Note Tidy", param_action, "Remove Blank Start Lines: %b \n".. "Convert single use Notes to Local: %b \n", premoveblanks, pconvertnotes) if (ret == false) then return end -- Create Pointers for Main Routine ptrNote = fhNewItemPtr() ptrNoteText = fhNewItemPtr() ptrTemp = fhNewItemPtr() ptrParent = fhNewItemPtr() ptrLink = fhNewItemPtr() ProgressDisplay.Start('Cleaning Notes') ProgressDisplay.SetMessage('Building Cross Reference Table . . ') istep = 1 / CountNotes() -- Get the step size for the Bar tblList = BuildNoteList() gaugeProgress.value = gaugeProgress.value + istep -- Process All Notes ptrNote:MoveToFirstRecord('NOTE') gaugeProgress.value = 0 -- Do All Local Notes if premoveblanks == 1 then CleanLocalNotes() end ProgressDisplay.SetMessage('Processing Note Records') -- Process All Note Records while ptrNote:IsNotNull() do ProgressDisplay.Step(istep) -- allow the dialog to process any messages iup.LoopStep() -- notice the user wanting to cancel and throw error so FH prompts to undo changes if ProgressDisplay.Cancel() then error('User Cancelled') end if premoveblanks == 1 then -- Remove Blank Lines from Start ptrTemp:MoveTo(ptrNote,'~.TEXT') -- Get Text for Note if ptrTemp:IsNotNull() then local type = fhGetValueType(ptrTemp) if type == 'richtext' then local rt = fhGetValueAsRichText(ptrTemp) local sReplace = RemoveLeadingBlanklines(rt:GetText()) if sReplace ~= rt:GetText() then rt:SetText(sReplace,true) fhSetValueAsRichText(ptrNote2,rt) end else fhSetValueAsText(ptrTemp,RemoveLeadingBlanklines(fhGetValueAsText(ptrTemp))) end end end fhUpdateDisplay() if pconvertnotes == 1 then ptrTemp:SetNull() -- Clear delete pointer iLinkCount = fhCallBuiltInFunction('LinksTo',ptrNote) if iLinkCount == 1 then -- Found a note with only one link. -- Convert Linked Note and flag Note Record for delete. ptrLink = tblList[fhGetRecordId(ptrNote)][1] if ptrLink:IsNotNull() then ptrParent:MoveToParentItem(ptrLink) -- Get Notes Parent ptrNote2 = fhCreateItem('NOTE2', ptrParent) -- Create NOTE2 for Parent (Local Note) ptrTemp:MoveTo(ptrNote,'~.TEXT') -- Get Text for Note local type = fhGetValueType(ptrTemp) if type == 'richtext' then local rt = fhGetValueAsRichText(ptrTemp) fhSetValueAsRichText(ptrNote2,rt) else fhSetValueAsText(ptrNote2,fhGetValueAsText(ptrTemp)) end CopyChildren(ptrNote, ptrNote2) -- Copy all the child items from the NOTE CopyChildren(ptrTemp, ptrNote2) -- Copy all the child items from the NOTE ptrTemp = ptrNote:Clone() -- Hold on to Note Record so it can be deleted. end end end ptrNote:MoveNext() -- Next Note if ptrTemp:IsNotNull() then -- Delete Original Note fhDeleteItem(ptrTemp) end end ProgressDisplay.Close() -- Clean up Progress Bar fhMessageBox('Note Replacement Complete','MB_OK','MB_ICONINFORMATION') end ----------------------------------------------------------- Start Progress Display --[[ @Title: Progress Display (drop in) @Author: Jane Taubman @LastUpdated: July 2011 @Description: Allows easy adding of a Progress Bar to any long running Plugin ]] ProgressDisplay = { Start = function(strTitle,intMax) -- Create and start the Progress Display window controls cancelflag = false local cancelbutton = iup.button{ title="Cancel", size="50x20", action = function() cancelflag = true -- Signal that Cancel button was pressed return iup.CLOSE end } gaugeProgress = iup.progressbar{ expand="YES", max=intMax } -- Set progress bar maximum range messageline = iup.label{ title=" ", expand="YES", alignment="ACENTER" } dlgProgress = iup.dialog{ title=strTitle, size="QUARTER", dialogframe="YES", -- Remove Windows minimize/maximize menu iup.vbox{ alignment="ACENTER", gap="10", messageline, gaugeProgress, cancelbutton } } dlgProgress.close_cb = cancelbutton.action -- Windows Close button acts as Cancel button dlgProgress:showxy(iup.CENTER, iup.CENTER) -- Show the Progress Display dialogue window end, SetMessage = function(strMessage) -- Set the progress message messageline.title = strMessage end, Step = function(iStep) -- Step the Progress Bar forward gaugeProgress.value = gaugeProgress.value + iStep local val = tonumber(gaugeProgress.value) local max = tonumber(gaugeProgress.max) if val > max then gaugeProgress.value = 0 end iup.LoopStep() end, Reset = function() -- Reset progress bar gaugeProgress.value = 0 end, Cancel = function() -- Check if Cancel button pressed return cancelflag end, Close = function() -- Close the dialogue window dlgProgress:destroy() end } ----------------------------------------------------------- End Progress Display --------------------------- -- Count Notes (for gaugeProgress) --------------------------- function CountNotes() local ptrCount = fhNewItemPtr() local count = 0 ptrCount:MoveToFirstRecord('NOTE') while ptrCount:IsNotNull() do count = count + 1 ptrCount:MoveNext() end -- Return as reciprical to step the gaugeProgress return count end --------------------------- -- BuildNoteList --------------------------- function BuildNoteList() tblLinks = {} iCount = fhGetRecordTypeCount() -- Get Count of Record types local ii = 0 local ptrLLink = fhNewItemPtr() local ptrLCheck = fhNewItemPtr() for ii =1,iCount do strType = fhGetRecordTypeTag(ii) ptrLLink:MoveToFirstRecord(strType) while ptrLLink:IsNotNull() do if fhGetDataClass(ptrLLink) == 'link' then ptrLCheck = fhGetValueAsLink(ptrLLink) if fhGetTag(ptrLCheck) == 'NOTE' then recordid = fhGetRecordId(ptrLCheck) if tblLinks[recordid] == nil then tblLinks[recordid] = {} end table.insert(tblLinks[recordid],ptrLLink:Clone()) end end ptrLLink:MoveNextSpecial() end end return tblLinks end --------------------------- -- Function: Clean Local Notes --------------------------- function CleanLocalNotes () local iCount = fhGetRecordTypeCount() -- Get Count of Record types -- Loop through Record Types, looking for link -- There can be only one. local ii = 0 local ptrLLink = fhNewItemPtr() local ptrLCheck = fhNewItemPtr() ProgressDisplay.SetMessage('Cleaning Local Notes') ProgressDisplay.Reset() local iStep = 1 / iCount for ii =1,iCount do strType = fhGetRecordTypeTag(ii) ptrLLink:MoveToFirstRecord(strType) while ptrLLink:IsNotNull() do if fhGetTag(ptrLLink) == 'NOTE2' then -- If Rich process differently local type = fhGetValueType(ptrLLink) if type == 'richtext' then local rt = fhGetValueAsRichText(ptrLLink) local sReplace = RemoveLeadingBlanklines(rt:GetText()) if sReplace ~= rt:GetText() then rt:SetText(sReplace,true) fhSetValueAsRichText(ptrLLink,rt) end else fhSetValueAsText(ptrLLink,RemoveLeadingBlanklines(fhGetValueAsText(ptrLLink))) end ProgressDisplay.Step(0) end ptrLLink:MoveNextSpecial() end -- notice the user wanting to cancel and do something meaningful if ProgressDisplay.Cancel() then error('User Cancelled') end ProgressDisplay.Step(iStep) end return false, nil end -- Copy Notes Childen function CopyBranch(ptrSource,ptrTarget) local strTag = fhGetTag(ptrSource) if strTag == "_FMT" then return end -- Skip rich text format code if strTag == "_FIELD" then -- Substitute metafield shortcut strTag = fhGetMetafieldShortcut(ptrSource) end local ptrNew = fhCreateItem(strTag,ptrTarget,true) if ptrNew:IsNull() then return end -- Escape if item not created fhSetValue_Copy(ptrNew,ptrSource) CopyChildren(ptrSource,ptrNew) end -- function CopyBranch function CopyChildren(ptrSource,ptrTarget) local ptrFrom = fhNewItemPtr() ptrFrom = ptrSource:Clone() ptrFrom:MoveToFirstChildItem(ptrFrom) while ptrFrom:IsNotNull() do CopyBranch(ptrFrom,ptrTarget) ptrFrom:MoveNext() end end -- function CopyChildren --------------------------------------- -- Function: RemoveLeadingBlanklines --------------------------------------- function RemoveLeadingBlanklines (str) -- Strip all Blank Lines from the Start of the String while string.find(str,'^\n') == 1 do str = str:gsub('^\n','') end return str end main()