OpenOffice.org Forum at OOoForum.orgThe OpenOffice.org Forum
 
 [Home]   [FAQ]   [Search]   [Memberlist]   [Usergroups]   [Register
 [Profile]   [Log in to check your private messages]   [Log in

AutoSave template child doc to defined subdirectory

 
Post new topic   Reply to topic    OOoForum.org Forum Index -> OpenOffice.org Code Snippets
View previous topic :: View next topic  
Author Message
JohnV
Administrator
Administrator


Joined: 07 Mar 2003
Posts: 9183
Location: Lexinton, Kentucky, USA

PostPosted: Wed Jul 12, 2006 6:48 am    Post subject: AutoSave template child doc to defined subdirectory Reply with quote

Several people have complained about the necessity of having to traverse a long directory tree to save a file to its intended directory and I got tired of it too. If you create files from templates properly saved in OOo's template directory this macro provides a way to solve the problem. The macro should be saved in My Macros - Standard or some other global library and not in any of your templates. I have attached mine to the Ctrl+S key combo as I don't normally use that for a Save.

You need to edit the macro's first set of User Variables in the first subroutine to define a basic home (top level) directory for saving OOo files. Normally this will be your default OOo directory.

From this home directory the macro provides two methods to specify one or more subdirectory levels to which the file should be saved. Both methods can be used together.

For the first method look at the “Case” statement lines in the second set of User Variables in the first subroutine. The first such “Case” line looks this,
Case "LetterHead" : SubDir = "Letters"
where LetterHead is the name of the template used and Letters specifies the subdirectory to save it in. The second such “Case” line show an example of going 2 two subdirectories deep. There is no reason you can't go deeper, just note that you do not use a slash before or after the subdirectories but you do between them. You can add additional “Case” lines and, of course, modify the sample ones.

The first method will require you to edit the macro any time you want to add additional templates to the macro. The second method avoids this.

The second method actually defines the one or more subdirectory levels as part of the template name. In this regard note that in the first set of User Variables I have defined the back quote (upper left key, next to “1”) as the Divider. You may define a different Divider but it should be a unique character and allowable in file names for your operating system.* To achieve the same result as the two “Case” lines in the macro you would name your templates:
LetterHead`Letters
and
Invoice`Client`Invoices

* If you use a period as the Divider the the last character of the template name must be a period, e.g., LetterHead.Letters. because OOo will truncate the last period and any characters following it thinking it's an extension.

You can have the same basic template saving to several different locations with something like:
LetterHead`Personal`Letters
LetterHead`Client`Letters
LetterHead`Suppliers`Letters

If you specify one or more subdirectories levels that do not exist the macro creates them so be very careful when warned this is about to happen or you may lose track of where your files end up. For example, I used “Documents and Setting” instead of “Documents and Settings” in the definition of my home directory and missed it when warned because I actually had another new directory in play at the same time.

The macro will warn you if you try to overwrite an existing file and you can do so or change the file name or path.

If you run the macro against a file that it is not setup to handle then you will be notified of the unintended use but will be allowed to continue so a Save or Save As can be finished. (To avoid such notifications comment out the first “If” through “EndIf” code block of the SaveFile subroutine, but I suggest you let this work for a while.)

The macro doesn't notify you when it's finished but, unless you run it against an existing file and don't change the name, you will see the file name change on the top line of the screen.

The macro should handle templates of type Writer, Calc, Draw, Presentation and HTML but has mostly been tested with Writer. DataBase, Master Document and Formula files do not support templates.

The program assumes the use of OOo2.x.x but should work with OOo1.1.x if you change the file extensions in the GetFileTypeOrExtension function. I have no reason to think the macro will not work under Linux, etc., but it has only been tested under Window98 & W2K.

Please let me know if you find a bug. However, my hope is that you just get saved a lot of aggravation.
Code:

Sub AutoSaveTemplateChild 'Saves template child to defined directory.
'Version 1 July 11, 2006 by John C. Vigor, Jr. 
'>>>>>>>>>>>>>> USER VARIABLES <<<<<<<<<<<<<
REM Define some basic home directory for saving OOo files, normally
REM your default OOo directory.
HomeDir = "C:\Documents and Settings\JohnV\My Documents"
Divider = "`" 'Unique character to seperate file name from save subdirectory.
'>>> END USER VARIABLES - BUT MORE BELOW <<<
On Error Goto ErrHand
condition = 1 'Normal use of macro.
oDoc = ThisComponent
Url = oDoc.getURL
TemplateName = oDoc.DocumentInfo.Template
PathSep = getPathSeparator
b$ = " However, you may continue."
If TemplateName = "" And Url = "" then
  'Child created from template in normal directory or from OOo's
  'built in default template as opposed to a users Default Template.
  a$ = "This macro is intended to work with documents created from a"
  a$ = a$ & " template stored in OOo's template directory." & b$
  SaveUrl = HomeDir & PathSep
  SaveFile(oDoc,SaveUrl,condition,a$) : End
 ElseIF Url <> "" then
  condition = 2 'Existing file.
  a$ = "This macro is not intended for use with an exiting file." & b$
  SaveUrl = ConvertFromURL(Url)
  SaveFile(oDoc,SaveUrl,condition,a$) : End
EndIf
Select Case TemplateName
'>>>>>>>>>>>>>> USER VARIABLES <<<<<<<<<<<<<
REM "Case" statements below are case sensitive, e.g., Letterhead below would fail!
REM You may modify and/or add "Case" lines.
 Case "LetterHead" : SubDir = "Letters"
 Case "Invoice" : SubDir = "Client\Invoices"
'>>>>>>>>>>> END OF USER VARIABLES <<<<<<<<<
 Case Else
 sp = Split(TemplateName,Divider)
 If uBound(sp) > 0 then
   sp(0) = ""
  Else
   a$ = "This macro is not intended to work with a file created from the '"
   a$ = a$ & sp(0) & "' template." & b$
   SaveUrl = HomeDir & PathSep
   SaveFile(oDoc,SaveUrl,condition,a$) : End
 EndIf
 SubDir = Join(sp,PathSep)
 SubDir = Right(SubDir,Len(SubDir)-1)
End Select
SaveUrl = HomeDir &  PathSep & SubDir & PathSep
SaveFile(oDoc,SaveUrl,condition,a$)
END 'Normal program end.
ErrHand:
If Err = 423 then
 e$ = "Possibly no file is open."
EndIf
er$ = "Error #" & Err & " '" & Error$ & "' occurred in line #" & Erl
er$ = er$ & Chr(13) & Chr(13) & e$
MsgBox (er$,,"AN ERROR OCCURRED")
End Sub

Sub SaveFile(oDoc,SaveUrl,condition,a$)
If a$ <> "" then
 iAns = MsgBox(a$,1,"Unintended macro use.")
 If iAns = 2 then End
EndIf
If Not FileExists(converttoUrl(SaveURL)) then
 a$ = "Do you intend to create a new file folder(s)?" & Chr(13)
 a$ = a$ & "New folder(s) = " & SaveUrl
 iAns = MsgBox (a$,4,"WARNING!")
 If iAns = 7 then End
EndIf
GetFileName:
Do
 If condition = 2 then 'It's an existing file.
   a$ = "You may change the file name or path below."
  Else 'It's a new file.
   a$ = "Append a file name, without extension, to the path below." & Chr(13)
   a$ = a$ & Chr(13) & "You may also modify the path."
 EndIf
 Copy = SaveUrl
 SaveUrl = InputBox(a$,"PREPARING TO SAVE.", SaveUrl)
 If SaveUrl = "" then End
Loop While SaveUrl = Copy And condition <> 2
If condition <> 2 then 'It's not an existing file.
 ext = GetFileTypeOrExtension(oDoc,"Ext")
 SaveUrl = SaveUrl & ext
EndIf
If FileExists(SaveUrl) then
 condition = 2 'Existing file.
 iAns = MsgBox ("File Exists. Overwrite?",3,"WARNING!")
 If iAns = 2 then End
 If iAns = 7 then SaveUrl = Copy : goto GetFileName
EndIf
oDoc.storeAsUrl(convertToURL(SaveUrl),Array())
End Sub

Function GetFileTypeOrExtension(oDoc, Optional GetWhat as String)
If isMissing(GetWhat) then GetWhat = "Ext"
aray = oDoc.getSupportedServiceNames
d = "Document"
supported = Array("Text" & d,"Spreadsheet" & d,"Drawing" & d,"Web" & d,_
"Presentation" & d,"Database" & d,"Global" & d,"FormulaProperties")
Found = false
For I = 0 to ubound(aray)
 For II = 0 to uBound(supported)
  If Instr(aray(I),supported(II)) > 0 then Found = true : Exit For
 Next
 If Found = true then Exit For
Next
If Not Found then Msgbox("Unknown document type. Quiting.") : End
Select Case II
 Case 0 : ext = ".odt"
 Case 1 : ext = ".ods"
 Case 2 : ext = ".odg"
 Case 3 : ext = ".html"
 Case 4 : ext = ".odp"
 Case 5 : ext = ".odb" 'Templates not supported.
 Case 6 : ext = ".odm" 'Templates not supported.
 Case 7 : ext = ".odf" 'Templates not supported.
End Select
If GetWhat = "Ext" then
  GetFileTypeOrExtension = ext
 Else GetFileTypeOrExtension = Supported(II)
EndIf
End Function
Back to top
View user's profile Send private message
Ken Ryan
Newbie
Newbie


Joined: 15 Mar 2005
Posts: 2

PostPosted: Wed Jul 12, 2006 11:20 am    Post subject: Reply with quote

Hi, John!

[It doesn't "feel" like this reply is really appropriate as a thread, but since your email specifically requested I post...]

I admit I haven't tried running your macro, but it doesn't sound like it'd solve my gripes. You're essentially making a template<->directory correlation.

I have only a couple templates that I use in lots of different projects. My directories are organized by project. I therefore end up traversing from the starting point configured in OOo down to where I actually want to place the file. I mitigated this by making the default location the parent of all my project trees, which helps about halfway.

Ideally I'd like in the file path configuration box to be able to check a box saying "just remember where I saved the last one and start there". I tend to work in one project for several weeks or months, then move to another. Some windows programs have that last-used-directory behavior already. It still wouldn't be a complete solution, but I think the only way to get better than a mind-reading library.

As it is, I usually don't use templates stored in OOo. I just have a copy of the template itself in the individual project dir where I can open it in-place. (Naturally I end up with 35 versions of my template, nothing's perfect).
_________________
- ken
Back to top
View user's profile Send private message
JohnV
Administrator
Administrator


Joined: 07 Mar 2003
Posts: 9183
Location: Lexinton, Kentucky, USA

PostPosted: Thu Jul 13, 2006 2:55 pm    Post subject: Reply with quote

Quote:
As it is, I usually don't use templates stored in OOo.
Well you are a man after my own heart as I normally save my templates in the directory to which they relate. Unfortunately a child of such a template has an empty TemplateName property and an empty URL property so I couldn't use either in the macro.

You might try this. Store the equivalent of a template as an ordinary .odt file. Open the file, do what you need, do a Save As and change the Name. Save As will offer you the subdirectory that you opened the file from and your "template" will remain as it was. The macro would do the same thing for you but simply isn't needed. The only trick is to remember to do the Save As so you don't ruin the "template". I suspect you could record a Save As macro and attach it to the open document event.

Hum, may have to change my own approach. You lose the ability to edit the template sytles and apply those changes to documents previously created from a template "properly" stored but I have never found a need for that in my work.
Back to top
View user's profile Send private message
Villeroy
Super User
Super User


Joined: 04 Oct 2004
Posts: 10106
Location: Germany

PostPosted: Fri Jul 14, 2006 2:12 pm    Post subject: Reply with quote

Quote:
Well you are a man after my own heart as I normally save my templates in the directory to which they relate.

So do I. My documents and OOo profile are saved on the same Linux filesystem, so I can use hard links (undistinguishable filesystem entries, pointing to the same file content). Symbolic links (links with information about the "real file") are shown in the template-organizer when the link points to some "real file" in a template directory. In either case I get a DocumentInfo.TemplateName. The latter should work with Windows-links as well.
_________________
Rest in peace, oooforum.org
Get help on https://forum.openoffice.org
Back to top
View user's profile Send private message
JohnV
Administrator
Administrator


Joined: 07 Mar 2003
Posts: 9183
Location: Lexinton, Kentucky, USA

PostPosted: Sat Jul 15, 2006 11:42 am    Post subject: Reply with quote

Quote:
The latter should work with Windows-links as well.
Well the only link I know in Windows is a shortcut so I tried one to a template stored in a normal directory and my File > Properties > Template entry came up empty. I thought Windows was going to try to have an equivalent to Linux links but if I can do it in W2K I don't know how.

Have you used the macro with "stored were it makes sense" templates and, if so, is it of value to you or have I justed wasted my time?
Back to top
View user's profile Send private message
Villeroy
Super User
Super User


Joined: 04 Oct 2004
Posts: 10106
Location: Germany

PostPosted: Mon Jul 17, 2006 9:30 am    Post subject: Reply with quote

JohnV wrote:
Quote:
The latter should work with Windows-links as well.
Well the only link I know in Windows is a shortcut so I tried one to a template stored in a normal directory and my File > Properties > Template entry came up empty. I thought Windows was going to try to have an equivalent to Linux links but if I can do it in W2K I don't know how.

Have you used the macro with "stored were it makes sense" templates and, if so, is it of value to you or have I justed wasted my time?

To be honest: I don't like your macro because it shows too many message boxes and it requires hard coded mapping of template names to subdirectories.
The following code is a simple draft. You can set any global document-event to Sub setLastPath in order to store the path of a document in a global string var globLastDir.
sub MySaveDocumentAs shows a save-as dialog with globLastDir as default directory and template name as default file name.
If the document has a userdefined file info "TryPath" with some value like "subdir/subsubdir/", it tries to set this subdirectory as default path of the file picker.
I use to store globals in a separate module, so they survive code changes.
Code:

REM  *****  BASIC  *****
Global globLastDir$

Code:

REM  *****  BASIC  *****
Option Explicit
Sub MySaveDocumentAs()
'calls: global var globLastDir, getURLPath, getUserFieldValue, PickFileName, setLastPath
'name of a userdefined property (File>Properties>Tab:"User Defined">button "Info Fields ..."
'It's value should specifiy some preferred sub-dir in URL notation without leading slash ("sub/dir", "sub/dir/")
Const cTry$ = "TryPath"
Dim SaveUrl$,sLastDir$,subDir$,sConcat$,sTemplate$
   'if globLastDir is unset, set to user's default work-directory:
   if globLastDir = "" then globLastDir = getWorkDir
   'get a preferred sub-directory suffix:
   subDir = getUserFieldValue(thisComponent,cTry)
   If len(thisComponent.URL) > 0 then
   'if already saved then use this path as default:
      sLastDir = getURLPath(thisComponent.URL)
   else
   'use globLastDir
      sLastDir = globLastDir
   endif
   'minimal checking:
   if Not(fileexists(sLastDir)) then sLastDir = getWorkDir
   sConcat = sLastDir & subDir
   if Not(fileexists(sConcat)) then sConcat = sLastDir
   sTemplate = thisComponent.DocumentInfo.Template
   SaveUrl = PickFileName(cFolder:=sConcat,cFileName:=sTemplate ,sTitle:="MySaveDocumentAs()")
   If len(SaveUrl) > 0 then
      thisComponent.storeAsUrl(SaveUrl,Array())
   endif
End Sub
Function getWorkDir() as string
DIm oSrv
oSrv=createUnoService("com.sun.star.util.PathSettings")
getWorkDir = oSrv.Work &"/"
End Function
'set Tools>Customize>Event "Document has been saved As" and/or "Document has been saved"
Sub setLastPath()
'calls: getURLPath
Dim sURL$
   sURL = thisComponent.getURL
   if len(sURL) > 0 then globLastDir = getURLPath(sURL)
'   msgbox globLastDir,0,"setLastPath"
End Sub
Function getUserFieldValue(oDoc,sName$) as String
'calls: none
Dim i%,sVal$
with oDoc.DocumentInfo
   for i = 0 to .getUserfieldCount -1
      if .getUserfieldName(i) = sName then
         sVal = .getUserFieldValue(i)
         exit for
      endif
   next
end with
getUserFieldValue = sVal
End Function


Stolen from http://www.oooforum.org/forum/viewtopic.phtml?t=27188&highlight=save+filepicker and modified:
Code:

Function PickFileName( cFolder, cFileName, sTitle$)as string
   oFilePickerDlg = createUnoService( "com.sun.star.ui.dialogs.FilePicker" )
   sFilePickerArgs = Array(com.sun.star.ui.dialogs.TemplateDescription.FILESAVE_AUTOEXTENSION_PASSWORD )
   With oFilePickerDlg
      .setTitle(sTitle)
      sDirectory = cFolder
      .Initialize ( sFilePickerArgs() )
      .SetDisplayDirectory (sDirectory)
      .SetDefaultName (cFileName)
   '      .AppendFilter( "All files (*.*)", "*.*" )
   '     .AppendFilter( "OpenDocument Text (.odt)", "*.odt" )
   '      .AppendFilter( "OpenDocument Text Template (.ott)", "*.ott" )
   '      .SetCurrentFilter( "OpenDocument Text (.odt)" )
      .SetValue(com.sun.star.ui.dialogs.ExtendedFilePickerElementIds.CHECKBOX_AUTOEXTENSION, 0, true)   
        .SetValue(com.sun.star.ui.dialogs.ExtendedFilePickerElementIds.CHECKBOX_PASSWORD, 0, false)
        '.EnableControl(com.sun.star.ui.dialogs.ExtendedFilePickerElementIds.CHECKBOX_PASSWORD, false)
   End With
   ' Unnecessary, the dialog defaults to having multi-select turned off.   
   '   oFilePickerDlg.setMultiSelectionMode( False )
   If Len( cFolder ) > 0 Then
      oFilePickerDlg.setDisplayDirectory( ConvertToURL( cFolder ) )
   EndIf
   If Len( cFileName ) > 0 Then
      oFilePickerDlg.setDefaultName( cFileName )
   EndIf
   '   oFilePickerDlg.appendFilter( "OpenOffice.org Calc (SXC)", "sxc" )
   '   oFilePickerDlg.appendFilter( "Microsoft Excel (XLS)", "xls" )
   
   ' Change the Open button to say "Save".
   '   oFilePickerDlg.setLabel( com.sun.star.ui.dialogs.CommonFilePickerElementIds.PUSHBUTTON_OK, "Save" )
   if oFilePickerDlg.execute() > 0 then
      sFiles() = oFilePickerDlg.getFiles()
      PickFileName= sFiles(0)
   endif
End Function

Helpers, copied from other modules:
Code:

Function getURLPath(sURL$) as String
Dim structURL as new com.sun.star.util.URL
oSrv = createUnoService("com.sun.star.util.URLTransformer")
structURL.Complete = sURL
If oSrv.parseSmart(structURL,"Path") then
   getURLPath = structURL.Protocol & structURL.Path
Endif
End Function

_________________
Rest in peace, oooforum.org
Get help on https://forum.openoffice.org
Back to top
View user's profile Send private message
JohnV
Administrator
Administrator


Joined: 07 Mar 2003
Posts: 9183
Location: Lexinton, Kentucky, USA

PostPosted: Fri Jul 21, 2006 10:21 am    Post subject: Reply with quote

Villeroy,

Well that was certainly a forthright answer and with code to boot. However, it still does not solve the problem, at least in Windows, of not being able to determine the path to a template stored in a normal directory if I just go there an open the template.

I have decided to solve my own problem with templates saved in related directories by following through on my thoughts in my second post above.

All my templates already start with a back quote character so they are available near the top of my various subdirectories and I have simply stored them as normal files instead of templates. ( I left the templates in my defined work directory alone.) The following macro is now tied to the Open Document event and if a file starting with a back quote is opened it simple does the equivalent of a Save As.
Code:
Sub TemplateSaveAs
Url = ThisComponent.getURL
If Len(URL) > 0 then
  sp = Split(Url,"/")
  filename = sp(uBound(sp))
  If Left(filename,3) = "%60" then 'Represents a back quote in URL notation.
    sp(uBound(sp)) = "" : path = Join(sp,"/")
    sp = Split(Url,".") : ext = "." & sp(uBound(sp))
    a$ = "Enter file name, without extension, below." & Chr(13)
    a$ = a$ & Chr(13) & "You may also change the path."
    b$ = "'Template' file detected. Preparing to Save As."
    GetName:
    Do
     Url = ConvertToURL(InputBox(a$,b$,ConvertFromURL(path)))
     If URL = "" then End
    Loop While Url = path
    Url = Url & ext
    If FileExists(Url) then
     c$ = "File exists. Overwrite?" 
     iAns = MsgBox(c$,3,"EXISTING FILE! " & ConvertFromURL(Url)
     If iAns = 2 then End 'User clicked Cancel.
     If iAns = 7 then Goto GetName 'User clicked No.
    EndIf
   Else End
  EndIf
 Else End
EndIf   
ThisComponent.storeAsURL(Url,Array())
oFrame = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dispatcher.executeDispatch(oFrame,".uno:UpdateInputFields","",0,Array())
dispatcher.executeDispatch(oFrame, ".uno:UpdateFields", "", 0, Array())
End Sub

Initially I thought I would lose the queries for my User/Input fields but another recent post showed that could be triggered with Shift+Ctrl+F9 so I recorded that. I also use some Conditional Text fields that are dependant on the value of Input fields so I threw in the recording of the F9 key to update fields.

I have a few templates linked to a spreadsheet and I just tested one of those and it appears to work fine.

As far as I can tell the only loss is the ability to update styles in template child files but I have never used that anyway. If I ever needed it I could import the styles.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    OOoForum.org Forum Index -> OpenOffice.org Code Snippets All times are GMT - 8 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group