You are on page 1of 52

I didn't get this to work with a named range, or using a R1C1 type references.

IT also doesn't seem to work with docx files. :(


NOTE What you MUST get right are: the weird backward single quotes around
the sheet name AND the $ at the end of the sheet name. I didn't know how to
type the quotes so I ended up cutting and pasting them into the code ... and
was shocked when it actually WORKED!! ?cs

function openDataSource( tcDocFile, tcXLSFile, tcSheet


) && tcSheet could also be a named range

local loWord, loMerge, lcSheet


loWord = createobject( 'word.application' )
loWord.Document.Open( m.tcDocFile )

loMerge

= m.loWord.ActiveDocument.MailMerge

lcSheet = "SELECT * FROM `"+m.tcSheet+"$`"

loMerge.openDataSource(m.tcXLSFile,,,,,,,,,,,'Entire
Spreadsheet',m.lcSheet)

endfunc

In a nutshell, this explains how I automate word 2003, attach an excel 2003
data source, and complete a print merge without user or programmer
intervention. cs

This solution has a story to go along with it: I have a web site where users
keep track of participants in a medical protocol. Users produce mail merges
based results of recent treatement. The web site is on a very secure machine
where I am not allowed by network authority to install word or excel, though I
can place both file types on the secure server. However, I can access the data
on the secure server from a machine that has the complete Office 2003 suite.
Provided within the web application is a form where users can request a
particular mail merge. The form adds a record to a queue, a fox pro table. A
listner program, running on a computer with a less restrictive installation
requirement, periodically checks for new print merge request in the queue.
When a new request is discovered, the listner launches the automated print
merge routine below to produce the document, and then updates the queue to
reflect that the merge has been completed.
Complications: I needed a way to merge a Word 2003 with an Excel 2003
spreadsheet in an unattended mode, and I could not seem to avoid dialogue
boxes. Though I considered using a different file format for my data source, I
stubbornly felt I wanted to utilize the Office 2003 suite as it should work. I read
the approachs in "Microsoft Office Automation with Visual FoxPro" and also one
in the "1001 things you Wanted to Know About Visual FoxPro" I felt those
solutions were utilizing the power MS had built (clunkily) into the office suite.
So here are the things I did to set up my word templates.
1. Created a normal word document with the merge fields embedded
Created sample excel data containing fields I needed for merging with word
Created the document
Used the word interface to attach the excel data to the word document
Inserted all merge fields needed into the word document
Converted the word document to a NORMAL word document. The merge fields
remain, but if printed will print as literals, not as merged data.
Here is what Word 2003 says about converting a merge template document into
a normal document:

"By removing the associated data source from a main


document, you can convert it to a regular document.

-Open the main document from which you want to remove


the data source.

-On the View menu, point to Toolbars, and then click


Mail Merge.
-Click Main Document Setup button.
-Click Normal Word document radio selection.
-Click Okay."

2. Set my macro security lower than I like and allowed access to VBA Project
(from word look under options security follow the macro security button edit
both tabs in the "Security" dialogue that pops up enable access to the VBA
project)
3. Wrote a routine to attach a data source to a word document on the fly from
Fox and then to do the print merge.
4 Wrote a routine to check for consistency between the data source attached
and the word document. Using my methodology you can have some
inconsistencies that are show stoppers if you do not do this step. See: Data
Source Consistency Check
My methods are not without drawbacks. We should not have to use automation
to add a macro to attach a data source for the soul purpose of avoiding a
dialogue during the print merge. I did not find an easier way and would
welcome one. I was completely happy to find a way that worked at all! I also do
not like the requirement of needing to trust access to the VBA Project, however
did not find a way around it. In my particular situation that is not an issue, but
for many people that would be a show stopper.
?cs

*
------------------------------------------------------------------------------------* Print Merge

*
------------------------------------------------------------------------------------function PrintMerge( )

local lcTemplateDoc, lcTemplateCopy, lcMergeData,


llContinue
local lcTempMacro, lcMergedDoc, loWord, loDocument,
loMergedDocument

*
------------------------------------------------------------------------------------* In production these parameters are passed in from
a parameter object, spelled
* out here for clarification
*
-------------------------------------------------------------------------------------

llContinue = .t.

lcTempFolder

= [c:\temp] && temporary folder

lcTemplateDoc
= [c:\template\MergeTemplate] &&
actual word document merge template, keep templates in
a seperate folder
lcTemplateCopy
= [c:\temp\TemplateCopy] && copy
of the word document merge tempate

lcTempMacro
= [c:\temp\AttachSourceMacro.txt]
&& macro to attach source data
lcMergeData
source data

= [c:\temp\ExcelData.xls] &&

lcMergedDoc
= [c:\result\MergedDocument.doc]
&& output document in result folder

*
-------------------------------------------------------------------------------------------------------------* Checking for files omitted for simplicity
*
-------------------------------------------------------------------------------------------------------------this.createMacroTextFile(m.lcTempMacro,
m.lcMergeData )

*
------------------------------------------------------------------------------------* work with a copy of the template word document,
that way if things go
* bad you really haven't hurt anything
copy file(m.lcTemplateDoc + [.doc]) to
(m.lcTemplateCopy + [.doc])

*
------------------------------------------------------------------------------------* Error trapping code not included here, but you DO
want it since
* it is very easy for things go wrong at any of
these steps
*
-------------------------------------------------------------------------------------

*
------------------------------------------------------------------------------------* Open word automation object
loWord = createobject([word.application])

*
------------------------------------------------------------------------------------* Open copy of template
loDocument =
m.loWord.Documents.open( m.lcTemplateCopy)
template copy

&& open

*
------------------------------------------------------------------------------------* Macro security and access to VBA Project must be
enabled for this step
* See word options security tab.

loDocument.VBProject.VBComponents("ThisDocument").Code
Module.AddFromFile(m.lcTempMacro)

*
------------------------------------------------------------------------------------* attaches the text subroutine in the text file to
the word document
loWord.run("AttachSource")

*
------------------------------------------------------------------------------------* at this stage I do have a routine for checking the
document validity I run
* prior to executing the merge, it can be found
under the wiki topic: Data Source Consistency Check

*
------------------------------------------------------------------------------------* perform the merge
loDocument.MailMerge.Execute

*
------------------------------------------------------------------------------------* save and close down merged document
loMergedDocument = m.loWord.ActiveDocument
&& Focus to the merged document
loMergedDocument.saveas( m.lcMergedDoc )
&& Save with the outputfile name
loMergedDocument.close(0) && close w/o prompt

*
------------------------------------------------------

--------------------------------------------------------* save and close down copy of word template document


*
-------------------------------------------------------------------------------------------------------------if vartype(m.loDocument) = [O] && only close if it
is an object
loDocument.close(0) && close w/o prompt
endif

*
------------------------------------------------------------------------------------* Release objects
*
------------------------------------------------------------------------------------loDocument = .null.
loMergedDocument = .null.
loWord.quit
loWord = .null.
release loWord

*
------------------------------------------------------------------------------------* Throw away copy of template document and template
macro
*
------------------------------------------------------------------------------------delete file (m.lcTemplateCopy + [.doc])
delete file (m.lcTempMacro)

endfunc

function createMacroTextFile(tcFile,tcSource)
*
------------------------------------------------------------------------------------* NOTE: the SQL statement parameter:
*
*

SQLStatement:="SELECT * FROM `<>$`

*
* The "$" is very important.
took me a long time to figure

I lost it once and it

* out that I needed it. The way I created this


connection string was to record a

* macro in word where I attached an excel file to a


word document as a data source,
* I then examined the macro text generated.
*
-------------------------------------------------------------------------------------

local lcMacroText, lcSourceFile, lcSource,


lcSourceFile

* parse the Source into the path, file and path +


file + extention (always excel)
lcSource

= alltrim(tcSource)

lcSourceFile = juststem(m.lcSource)
lcSourcePath = justpath(m.lcSource)
lcSource
+ [.xls]

= m.lcSourcePath + [\] + m.lcSourceFile

*
------------------------------------------------------------------------------------* The following code puts creates a variable
containing the text of a VBA subroutine
*
-------------------------------------------------------------------------------------

* The following is the text of a VBA subroutine


that, when run in word, attaches the word
* document to the data defined in the connection
string.
*
-------------------------------------------------------------------------------------

text TO lcMacroText TEXTMERGE NOSHOW


Sub AttachSource()

ActiveDocument.MailMerge.MainDocumentType =
wdFormLetters

ActiveDocument.MailMerge.OpenDataSource Name:= _
"<>" _
, ConfirmConversions:=False, ReadOnly:=False,
LinkToSource:=True, _
AddToRecentFiles:=False, PasswordDocument:="",
PasswordTemplate:="", _
WritePasswordDocument:="",
WritePasswordTemplate:="", Revert:=False, _
Format:=wdOpenFormatAuto, Connection:= _

"Provider=Microsoft.Jet.OLEDB.4.0;Password="""";User

ID=Admin;Data Source=<>;Mode=Read;Extended
Properties=""HDR=YES;IMEX=1;"";Jet OLEDB:System
database="""";Jet OLEDB:Registry Path="""";Jet O" _
, SQLStatement:="SELECT * FROM `<>$`",
SQLStatement1:="" _
, SubType:=wdMergeSubTypeAccess

End Sub
ENDTEXT

strtofile(m.lcMacroText,m.tcFile) && store the


variable to a file on disk

debugout m.lcMacroText

endfunc

***********************+++

FoxPro Fundamentals

For Better
Functionality, Pave

the Way with OLE


Instead of forcing FoxPro to
be all things to all people,
use Object Linking and
Embedding to get the best
out of several different
applications.
By Miriam Liskin, Contributing Editor

I've been saying for years that when it comes to software


(and software companies' promotional T-shirts), one size
doesn't always fit all. That's why a couple of big guys I
know are wearing most of my FoxPro T-shirts, and that's
also why I don't try to use Visual FoxPro as
a word processor or spreadsheet.
In the old days, when you used different
programs to do tasks for which they were
best suited, you had to import, export, and
convert data to the required file formats.
Importing and exporting still play a role in
some kinds of data exchange, but you can
now use Object Linking and Embedding (OLE) to integrate
foreign objects into a Visual FoxPro application.
OLE lets you incorporate objects (such as documents,
spreadsheets, sound recordings, and video clips) into a VFP
application by storing the objects in General fields in tables,
or by defining form objects to access them. Yes, you can do
all kinds of cool multimedia stuff with OLE, but this column
won't tell you how to play music or show movies; I'll stick
with more prosaic examples based on Microsoft Word and
Excel.
Visual FoxPro knows nothing about how to display, edit, or
run OLE objects, and it doesn't have to. In fact, it doesn't
even have to know which OLE server applications exist or
which ones are installed on your computer; such
information is stored in the OLE object itself and the
Windows Registration database. When you (or the users of
your application) need to display or edit an OLE object,
Visual FoxPro invokes the server application that created it,
and this application does all the work.

Philosophically, this approach works much like client-server


database software, and you'll see the same terminology:
The application that created an object is called the OLE
server, and the application using the object (and calling up
the server to display or edit it) is called the client.
The bad news is that using OLE objects makes an
application less portable. If the server application is missing
when you try to display or edit an OLE object, you receive
an error message. For example, if you embed Microsoft
Word files in a Visual FoxPro table, then give your
application to colleagues who use WordPerfect, they won't
be able to read the document.

Using general fields to store OLE objects


As I mentioned above, you can store OLE objects in
General fields in a Visual FoxPro table. For example, in a
client tracking system, you can put a copy of the contract
document in the client table; in a project-management
application, you can store the project budget spreadsheet
in the project table. In a personnel file, you can store the
employees' photographs and signatures in the employee
table.
As the acronym suggests, OLE lets you create a link (a
reference) to the original object, or embed a copy in your
Visual FoxPro table. The deciding factors are
transportability and whether you need to update the OLE
objects outside your Visual FoxPro application. When you
create a new embedded object, the object may never exist
as an independent disk file. If you do embed an existing
file, you're storing a copy. If you (or another user) edits the
original outside your Visual FoxPro application, the changes
won't be reflected in the copy stored in the table.
If your Visual FoxPro users need to modify or see the latest
copy of documents that are also updated outside your
Visual FoxPro application (possibly by other users), you
should choose linking whenever possible. If you're working
with documents that are local to your Visual FoxPro
application, embedding lets you easily back up or distribute
the application, together with all its OLE data.
Unfortunately, the decision isn't always yours to make,
because some OLE server applications don't support
linking. In particular, applets such as Microsoft Graph
(which comes with Visual FoxPro and many other Microsoft
applications) and WordArt (which comes with Microsoft
Word) are always called from another application and can't
create new disk files; therefore, such applications don't let
you create linked objects. You'll also encounter some
standalone programs that don't support linking, even if in
theory they could.
Surprisingly, storage space isn't an issue. You won't save

much space by linking rather than embedding; the "link"


usually takes almost as much space as the entire OLE
object would if you embedded it.
Visual FoxPro exerts virtually no control over the kind of
data you put in a General field. Working interactively, you
can store different kinds of OLE objects in different records
in the same table, and link some while embedding others.
When using forms to update tables, you can take some
measures to encourage consistency. I'll demonstrate how
to do that later in this article.

Working interactively
If you haven't done much with OLE, it's a good idea to
experiment a bit from the Command window before you
begin designing forms. These interactive techniques will
also serve you well for troubleshooting OLE problems. If
something doesn't work in a form, try opening the table in
Browse and see if you can accomplish the same task
interactively.
As is the case with Memo fields, the data in General fields
isn't visible in the default Browse or Edit screen. Visual
FoxPro displays the field as "gen" if it's empty, or "Gen" if it
contains data. To view or edit the contents, tab into the
General field and press Ctrl+Home, or double-click in the
field. These commands open an editing window like the one
shown in figure 1. Usually, you'll want to leave this window
open, so you can always see the contents of the General
field in the current record as you move through the table.
OLE objects that have no meaningful static representation
(such as sound or video recordings) are displayed as the
standard Windows icon for the server program.
All the options for creating and manipulating OLE objects
are on the Edit menu. If the General field that has the
focus is empty, only Insert Object is enabled; choosing this
option calls up the dialog shown in figure 2, which lets you
embed an OLE object. (You can't create a linked object this
way; you have to use the Windows Clipboard, as I'll explain
shortly.)

Figure 1: DISPLAYING GENERAL FIELDSYou can


display the contents of a General field in Browse or Edit.

Figure 2: CREATING A NEW OLE OBJECTYou can


create a new OLE object and embed it in a General field.

Creating a new object


If you want to create a brand-new OLE object, you must
identify its type (and, indirectly, the server application) by
choosing it from the scrolling list. This list comes from the
Windows Registration database, which stores the
associations between applications and the file types they
create, so your list won't exactly match the one in figure 2;
you'll see only object types supported by OLE server
applications installed on your system. When you choose an
object type and click on OK, Visual FoxPro immediately
opens the server application to create the object.
What happens next depends on whether the OLE server
application supports in-place activation (or, as Visual
FoxPro calls it, visual editing). If so, you'll be working
within the boundaries of the window you opened to view

the General field, using a menu bar and toolbars that


contain some Visual FoxPro commands and other
commands derived from the server application. To save the
embedded object and close the server application, simply
click anywhere outside the General field window.
If you're primarily a Visual FoxPro user, you might view inplace activation as more intuitive, in that you never leave
the familiar Visual FoxPro environment. However, if you're
equally conversant with Word or Excel, you might find it
unnerving not to see the full menu bar and toolbars
normally displayed by these programs.
If the server application doesn't support in-place activation,
it opens in its own windowexactly as if you had launched
it independent of Visual FoxPro. In this case, you'll find an
Update option on the File menu in place of the usual Save
option, and the Exit command will be worded "Exit and
Return to Visual FoxPro."

Adding an existing OLE object


To embed a copy or create a link to an existing OLE object,
choose Create from File in the Insert Object dialog (figure
2). If you're working with an existing file, you don't have to
identify its type; this information is stored in the
Registration Database. In this case, use the Open File
dialog (figure 3), which lets you type the name of the file
or use the Browse button to search for it. If you want to
create a link rather than an embedded object, select the
Link check box. In this scenario, Visual FoxPro doesn't
assume you want to edit the object, so it doesn't
automatically open the server application.

Figure 3: USING AN EXISTING OLE OBJECTYou can


embed, or create a link to, an existing file.

Editing an OLE object


To edit an OLE object already stored in a General field,
simply double-click in the editing window to activate the
server application in place, or in its own window if it doesn't
support in-place activation. If you prefer to choose which

way the server application opens, use the Object option on


the Edit menu instead. (The exact wording of this menu
option depends on the object type; you might see
Document Object, Worksheet Object, Paradox 5 Object,
among many others.) The Object option displays a
submenu with two choices, as shown in figure 4. Open
always opens the application in its own window, while Edit
opens it in place if the server application supports in-place
activation.

Using the clipboard


You can also use a simple cut-and-paste technique to
embed an OLE object in a General field. To use this
method, open the server application, create or select the
data you want to place in the General field, return to Visual
FoxPro, and use the Paste or Paste Special commands on
the Edit menu to insert the data. The Paste command (or
the equivalent hot key, Ctrl+V) embeds the selected data
in the General field. The Paste Special command calls up
the dialog shown in figure 5 to let you choose the format of
the data and, if you want, specify that you want the object
linked rather than embedded. (In most cases, you'll retain
the default format.)

Figure 4: EDITING AN OLE OBJECTYou can choose


whether you want to open the OLE server application in
place or in its own program window.

Figure 5: PASTING AN OLE OBJECTYou can copy all or


part of an OLE object to the Clipboard and use the Edit |
Paste Special command to embed or link the data in a
General field.

Building forms
OK, I know you're ready to close that Browse window and
start building a form. However, all the foregoing material
wasn't just a learning exercise in interactive FoxPro. If
you're willing to use the same commands to create and edit
OLE objects in forms, building the forms is trivial: Simply
create a bound OLE control to display the General field that
stores the OLE data.
You can place unbound OLE controlsobjects not stored in
a table, such as graphs or pictureson a Visual FoxPro
form, but you use the unbound OLE Container tool to do
so.

NOTE: The unbound OLE Container and Bound OLE Control


buttons in the toolbar look quite similar, so watch for the
ToolTip if you're not sure which is which.

There are just a few properties you need to set. In


particular, the AutoActivate property determines how the
user activates the object. The settings have the effects
shown in table 1.
Table 1: AUTOACTIVE PROPERTY SETTINGSthe
AutoActIvate property determines how the user activates
the object.

Setting

Description

0 - Manual

No user action activates the object (you


must use Visual FoxPro code).

1 - GotFocus

Activate the object when it receives the

focus.
2 - DoubleClick

Activate the object when the user doubleclicks in it.

Use the server application's default


3 - Automatic activation trigger (usually either doubleclicking or giving the object the focus).
If the OLE server application supports in-place activation,
the 1 - GotFocus setting makes the OLE bound control
behave just like any other control on the form. The user
can simply tab into it and begin editing. Another good
choice (better for keyboard users who prefer to tab through
the controls on a form in sequence) is the default setting,
DoubleClick.
Be aware that you can't set the foreground or background
colors for an OLE bound control (these properties are
derived from the object itself), and you can't display a
visible border around the object. The easiest way to
simulate these effects is to place the OLE bound control on
top of a rectangle object of the desired color, with the
desired border style.

Taking the reins


When designing forms for users who aren't familiar with
Visual FoxPro or OLE, you might prefer to exert more
stringent control over the process of inserting and editing
OLE objects. You might also hope to restrict what type of
OLE object users can place in a field. You can't, as you
might have hoped, use the OLEClass property for this
purpose. Although you can query this property from a
method to determine the actual OLE class of an object (the
class name assigned to the server application that created
the object), you can't set it in the Form Designer to force
the control to accept only a particular class of OLE object.
Figure 6 shows a form called MLMAIL9, which demonstrates
some techniques for manipulating OLE objects in code. This
form updates a new version of the Maillist table from the
sample mailing list application used throughout this series,
which includes a General field called Contract.
There are two basic techniques for manipulating OLE
objects in code:
Use the APPEND GENERAL command to insert an
OLE object into the field.
Use the DoVerb method for the OLE bound control
to edit the object.
You can use the APPEND GENERAL command from the
Command window or from any Visual FoxPro program, so
you can try some simple examples without building a form.
This basic format of this command is:

APPENDGENERAL<fieldname>FROM<filename>
By default, the APPEND GENERAL command creates an
embedded object; to create a link instead, add the keyword
LINK to the command.

Figure 6: EDITING OLE OBJECTS IN A FORMYou can


update OLE objects in forms by using OLE bound controls.
If you're not sure whether the file extension is sufficient to
identify the OLE server application, you can add a CLASS
clause, like this:
APPENDGENERALContract;
FROMC:\WORD6\DOCUMENT\STDCONTR.DOC;
CLASSWord.Document.6
You can create a placeholder for a specific type of OLE
object by specifying only the class, without including a file
name:
APPENDGENERALContractCLASSWord.Document.6
If you execute this command each time you add a blank
record to the table, double-clicking an OLE bound control
based on the Contract field automatically calls up Word.
However, it won't prevent the user from deleting the Word
document and putting a spreadsheet or graphic image in its
place.
If you want to ensure that users can only manipulate OLE
objects using command buttons on your form, you can
remove the object commands from the Edit menu (if you're
using a custom menu bar for your application) and set the
AutoActivate property for the OLE bound control to 0 Manual. You must then use the DoVerb method to activate

the object.
The DoVerb method takes one numeric parameter, which
specifies which of several activation methods you want to
use. The ones you'll use most often are shown in table 2.
Table 2: DOVERB METHOD PARAMETERSSpecifies the
activation method you want to use.

Method

Description

Use the default activation method for the


object

-1

Use in-place activation if the server


application supports this.

-2

Open the server application in its own


window.

For example, you can open the server application for the
oleContract object with:
THISFORM.oleContract.DoVerb(1)
The form in figure 6 uses this command in the Click
method for the Edit button.
The Delete button uses the BLANK FIELD command to
empty the Contract field, then disable the Edit button
(because there's nothing to edit). If you haven't
encountered the BLANK FIELD command before, note that
this command can be used for any type of field (not just
General fields), but it's the only way to delete the contents
of a General field. Here's the complete command sequence:
BLANKFIELDContract
THISFORM.oleContract.Refresh()
THISFORM.cmdEdit.Enabled=.F.
Finally, the New button lets the user insert an existing
document into the Contract field or create a new document.
The best way to encourage the user to do the right thing is
to display an Open File dialog that includes only files of the
specified typein this case, Word documents:
LOCALlcFileName
lcFileName=GETFILE("DOC","Choosea
document:",;
"Open",1)
IFEMPTY(lcFileName)
RETURN
ENDIF
IFUPPER(RIGHT(lcFileName,3))<>"DOC"
=MESSAGEBOX("YoumustchooseaWorddocument
file",;
MSGOKONLY+MSGEXCLAMATION,
C_APPNAME)
RETURN

ENDIF
IF"UNTITLED"$UPPER(lcFileName)
APPENDGENERALMaillist.ContractCLASS
Word.Document.6
ELSE
APPENDGENERALMaillist.ContractFROM
(lcFileName)
ENDIF
THISFORM.cmdEdit.Enabled=.T.
THISFORM.oleContract.Refresh()
THISFORM.oleContract.DoVerb(1)
This routine calls the GETFILE() function to display the
Open File dialog like the one shown in figure 7, which
includes only files with the extension .DOC. (If you're not
familiar with this function, I covered it in the February 1995
issue.) GETFILE() returns the name of the file selected
from the dialog, or an empty string if the user exits by
clicking on the Cancel button.
The Edit routine performs some basic error-checking on the
file selected by the user. If the extension isn't .DOC, it
displays an error message and returns the user to the form
to try again. This isn't an entirely satisfactory solution,
because a Word file could have an extension other than
.DOC, and files created by other programs could have this
extension. You might prefer to simply ask the user for
confirmation, rather than rejecting the selected file
unequivocally.

Figure 7: CHOOSING A FILEYou can use the GETFILE()


function to let the user open a file of a specified type.
If the user chooses New from the Open File dialog, the
GETFILE() function returns "UNTITLED" and the method
uses an APPEND GENERAL command that specifies only the
class for the new object. Otherwise, it appends the

contents of the file the user selected.


This version of the New command button Click method
assumes the user will want to edit the object immediately
after inserting it in the Contract field, so it calls the DoVerb
method for this purpose. If you don't agree with my
assumption, just take out that method call.

What's next?
There's one more layer of power and complexity: OLE
Automation, which lets you write programs that control an
OLE server application using its own command language
(Word Basic, Visual Basic for Applications, and so on). With
this technique, you can harness all the capabilities of the
server application behind the scenes, with no user
intervention.
For example, you could construct a Word document that
performs a mail merge and prints letters and envelopes, all
done from a Visual FoxPro program. Of course, this
requires some familiarity with the command language of
the server application and, nowadays, an understanding of
its object model, and that's beyond the scope of this
column.

Advanced richtext editor : TinyMce


Published on November 23 2015 by Yousfi Benameur

this is a demo of awesome richtext editor i pointed in previous


posts(cool web app-see the link below).
TinyMCE is a platform independent web-based JavaScript WYSIWYG HTML
editor control released as open source under LGPL.TinyMCE enables to
convert HTML TEXTAREA fields or other HTML elements to editor
instances.
it can work on local files.
Can visit this web page [http://www.tinymce.com/download/download.php]
and download the tinyMCE3.5.11(there is for sure other recent
versions-it's always updated).make all things as the image below
explains.
save the prg below in the same folder of "examples"(can be another
location but must change the relative path or the tinyMCE
[src="../jscripts/tiny_mce/tiny_mce.js" ] in the script tag.it seems
the path is always relative (dont admit absolute path).

the code is javascript overlapped with vfp.it builds an


internetexplorer.application and tests the 4 skins given by the
application.
the richtext editor is very complete.can achieve the code of 2 buttons
(open file and save file as to make a complet app...)
try the various buttons of tinyMCE and see how its wonderfull.
of course can make a project, add a config.fpw and generate an
executable.
With a browser embed on a form its absolutly the same result.see the
code*2*
*can read more on http://www.tinymce.com/index.php
this code is tested on windows10 pro+vfp9SP2 and ie11 emulation.

*Before begin running the code achieve the downnload of tinyMCE 3.5.11.its free richtext web editor
for personal use.
Click on code to select [then copy] -click outside to deselect

*1*
Local m.yrep
m.yrep=Addbs(Justpath(Sys(16,1)))
Set Defa To (yrep)

Local m.x,m.myvar
m.xx=Int(Val(Inputbox("Skin 1(default),2(O2k7
skin),3(silver),4(black)","","1")))
If Empty(m.xx)
Return .F.
Endi
If !Inlist(m.xx,1,2,3,4)
m.x=1
Endi
Do Case
Case m.xx=1
TEXT to m.myvar noshow
// Default skin
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
plugins :
"autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlin
k,emotions,iespell,insertdatetime,preview,media,searchreplace,print,co
ntextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonb
reaking,xhtmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,
justifycenter,justifyright,justifyfull,styleselect,formatselect,fontse
lect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist
,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cl
eanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor"
,
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotion
s,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abb
r,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak
,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,

// Example content CSS (should be your site CSS)


content_css : "css/content.css",
// Drop lists for link/image/media/template dialogs
template_external_list_url : "lists/template_list.js",
external_link_list_url : "lists/link_list.js",
external_image_list_url : "lists/image_list.js",
media_external_list_url : "lists/media_list.js",
// Replace values for the template plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT

Case m.xx=2
TEXT to m.myvar noshow
// O2k7 skin
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotion
s,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu
,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,x
htmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,
justifycenter,justifyright,justifyfull,styleselect,formatselect,fontse
lect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist
,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cl

eanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor"
,
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotion
s,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abb
r,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak
,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,

// Example content CSS (should be your site CSS)


content_css : "css/content.css",
// Drop lists for link/image/media/template dialogs
template_external_list_url : "lists/template_list.js",
external_link_list_url : "lists/link_list.js",
external_image_list_url : "lists/image_list.js",
media_external_list_url : "lists/media_list.js",
// Replace values for the template plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT

Case m.xx=3
TEXT to m.myvar noshow
// O2k7 skin (silver)
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
skin_variant : "silver",

plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotion
s,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu
,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,x
htmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,
justifycenter,justifyright,justifyfull,styleselect,formatselect,fontse
lect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist
,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cl
eanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor"
,
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotion
s,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abb
r,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak
,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,

// Example content CSS (should be your site CSS)


content_css : "css/content.css",
// Drop lists for link/image/media/template dialogs
template_external_list_url : "lists/template_list.js",
external_link_list_url : "lists/link_list.js",
external_image_list_url : "lists/image_list.js",
media_external_list_url : "lists/media_list.js",
// Replace values for the template plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}

});
ENDTEXT
Case m.xx=4
TEXT to m.myvar noshow
// O2k7 skin (silver)
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
skin_variant : "black",
plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotion
s,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu
,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,x
htmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,
justifycenter,justifyright,justifyfull,styleselect,formatselect,fontse
lect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist
,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cl
eanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor"
,
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotion
s,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abb
r,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak
,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,

// Example content CSS (should be your site CSS)

content_css : "css/content.css",
// Drop lists for link/image/media/template dialogs
template_external_list_url : "lists/template_list.js",
external_link_list_url : "lists/link_list.js",
external_image_list_url : "lists/image_list.js",
media_external_list_url : "lists/media_list.js",
// Replace values for the template plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT
Endcase
Local m.my
TEXT to m.my textmerge noshow
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>full Richtext tinyMCE with Skins</title>
<!-- TinyMCE -->
<script type="text/javascript"
src="../jscripts/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
<<m.myvar>>
</script>
<!-- /TinyMCE -->
</head>
<body>
<form method="post" action="http://tinymce.moxiecode.com/dump.php?
example=true"
<div>
<textarea id="ytextarea" name="elm1" rows="36" cols="80"
style="width:100%"> type your texts here
</textarea><br />
</div>
<!-<input type="submit" name="save" value="Submit" /> -->
<input type="reset" name="reset" value="Reset" />
</form>
</body>
ENDTEXT
Set Safe Off

Local m.lcdest
m.lcdest=m.yrep+"ytest.html"
Strtofile(m.my,m.lcdest)
Declare Integer BringWindowToTop In user32 Integer
Publi apie
apie=Newobject("internetexplorer.application")
With apie
.menubar=0
.Toolbar=0
.StatusBar=0
.Resizable=1
.Width=900
.Height=650
.Top=(Sysmetric(2)-.Height)/2
.Left=(Sysmetric(1)-.Width)/2
.Navigate(m.lcdest)
BringWindowToTop(.HWnd)
.Visible=.T.
Endwith
retu
*i disabled the save button in the script for 4 skins.
*can try with ie browser embed on vfp form also.

this is the full app.can restrict in code the buttons shown as customization.....
Click on code to select [then copy] -click outside to deselect

*2*
*promptly said, promptly made!

*tinyMCE richtext editor on a form and with same prerequisities as


code *1*
if !_vfp.startmode=0
on shutdown quit
endi
if !file("../jscripts/tiny_mce/tiny_mce.js")
messagebox("../jscripts/tiny_mce/tiny_mce.js must
exist",16+4096,"error",2000)
return .f.
else
*messagebox("../jscripts/tiny_mce/tiny_mce.js exists ok")
endi
Publi yform
yform=Newobject("ytinyMCE")
yform.Show
Read Events
Retu
*
Define Class ytinyMCE As Form
Height = 566
Width = 905
ShowWindow = 2
AutoCenter = .T.
Caption = "yTinyMce"
BackColor = Rgb(0,0,0)
Name = "Form1"
Add Object obrowser As OleControl With ;
oleclass="shell.explorer.2", ;
Top = 37, ;
Left = 0, ;
Height = 527, ;
Width = 901, ;
Anchor = 15, ;
Name = "oBrowser"
Add Object combo1 As ComboBox With ;
Anchor = 768, ;
Height = 25, ;
Left = 36, ;
MousePointer = 15, ;
Top = 1, ;
Width = 132, ;
Name = "Combo1"

Add Object label1 As Label With ;


AutoSize = .T., ;
FontBold = .T.,;
FontSize = 10,;
Anchor = 768,;
BackStyle = 0,;
Caption = "Warning: if interactive change skin in combo
, page is not saved!",;
Height = 18,;
Left = 186,;
Top = 2,;
Width = 346,;
ForeColor = Rgb(255,255,255),;
Name = "Label1"
Procedure ybuild
Local m.x,m.myvar
m.xx=Thisform.combo1.Value
Do Case
Case m.xx=1
TEXT to m.myvar noshow
// Default skin
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
plugins :
"autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advl
ink,emotions,iespell,insertdatetime,preview,media,searchreplace,prin
t,contextmenu,paste,directionality,fullscreen,noneditable,visualchar
s,nonbreaking,xhtmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifylef
t,justifycenter,justifyright,justifyfull,styleselect,formatselect,fo
ntselect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numli
st,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,imag
e,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,back
color",

theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emoti
ons,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,a
bbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pageb
reak,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location :
"bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,

// Example content CSS (should be your


site CSS)
content_css : "css/content.css",
// Drop lists for
link/image/media/template dialogs
template_external_list_url :
"lists/template_list.js",
external_link_list_url :
"lists/link_list.js",
external_image_list_url :
"lists/image_list.js",
media_external_list_url :
"lists/media_list.js",
// Replace values for the template
plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT
Case m.xx=2
TEXT to m.myvar noshow
// O2k7 skin
tinyMCE.init({
// General options

mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emoti
ons,iespell,insertdatetime,preview,media,searchreplace,print,context
menu,paste,directionality,fullscreen,noneditable,visualchars,nonbrea
king,xhtmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifylef
t,justifycenter,justifyright,justifyfull,styleselect,formatselect,fo
ntselect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numli
st,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,imag
e,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,back
color",
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emoti
ons,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,a
bbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pageb
reak,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location :
"bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,
// Example content CSS (should be your
site CSS)
content_css : "css/content.css",
// Drop lists for
link/image/media/template dialogs
template_external_list_url :
"lists/template_list.js",
external_link_list_url :
"lists/link_list.js",
external_image_list_url :
"lists/image_list.js",

media_external_list_url :
"lists/media_list.js",
// Replace values for the template
plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT
Case m.xx=3
TEXT to m.myvar noshow
// O2k7 skin (silver)
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
skin_variant : "silver",
plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emoti
ons,iespell,insertdatetime,preview,media,searchreplace,print,context
menu,paste,directionality,fullscreen,noneditable,visualchars,nonbrea
king,xhtmlxtras,template,inlinepopups,autosave",
// Theme options
theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifylef
t,justifycenter,justifyright,justifyfull,styleselect,formatselect,fo
ntselect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numli
st,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,imag
e,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,back
color",
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emoti
ons,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,a
bbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pageb
reak,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",

theme_advanced_statusbar_location :
"bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,
// Example content CSS (should be your
site CSS)
content_css : "css/content.css",
// Drop lists for
link/image/media/template dialogs
template_external_list_url :
"lists/template_list.js",
external_link_list_url :
"lists/link_list.js",
external_image_list_url :
"lists/image_list.js",
media_external_list_url :
"lists/media_list.js",
// Replace values for the template
plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT
Case m.xx=4
TEXT to m.myvar noshow
// O2k7 skin (silver)
tinyMCE.init({
// General options
mode : "exact",
elements : "ytextarea",
theme : "advanced",
skin : "o2k7",
skin_variant : "black",
plugins :
"lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emoti
ons,iespell,insertdatetime,preview,media,searchreplace,print,context
menu,paste,directionality,fullscreen,noneditable,visualchars,nonbrea
king,xhtmlxtras,template,inlinepopups,autosave",
// Theme options

theme_advanced_buttons1 :
"save,newdocument,|,bold,italic,underline,strikethrough,|,justifylef
t,justifycenter,justifyright,justifyfull,styleselect,formatselect,fo
ntselect,fontsizeselect",
theme_advanced_buttons2 :
"cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numli
st,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,imag
e,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,back
color",
theme_advanced_buttons3 :
"tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emoti
ons,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 :
"insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,a
bbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pageb
reak,restoredraft",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location :
"bottom",
theme_advanced_resizing : true,
save_enablewhendirty: true,
// Example content CSS (should be your
site CSS)
content_css : "css/content.css",
// Drop lists for
link/image/media/template dialogs
template_external_list_url :
"lists/template_list.js",
external_link_list_url :
"lists/link_list.js",
external_image_list_url :
"lists/image_list.js",
media_external_list_url :
"lists/media_list.js",
// Replace values for the template
plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
ENDTEXT

Endcase
Local m.my
TEXT to m.my textmerge noshow
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>full Richtext tinyMCE with Skins</title>
<!-- TinyMCE -->
<script type="text/javascript"
src="../jscripts/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
<<m.myvar>>
</script>
<!-- /TinyMCE -->
</head>
<body>
<form method="post"
action="http://tinymce.moxiecode.com/dump.php?example=true"
<div>
<textarea id="ytextarea" name="elm1" rows="30"
cols="80" style="width:100%"> type your texts here
</textarea><br />
</div>
<!-<input type="submit" name="save" value="Submit"
/> -->
<input type="reset" name="reset"
value="Reset" />
</form>
</body>
ENDTEXT
Set Safe Off
Local m.lcdest
m.lcdest=m.yrep+"ytest.html"
Strtofile(m.my,m.lcdest)
Thisform.obrowser.Navigate(m.lcdest)
Endproc
Procedure Init
Publi m.yrep
m.yrep=Addbs(Justpath(Sys(16,1)))
Set Defa To (yrep)
Thisform.ybuild()
Endproc

Procedure Destroy
Erase Addbs(Sys(2023)+"ytest.html"
Clea Events
Endproc
Procedure obrowser.Init
This.silent=.T.
Endproc
Procedure combo1.Click
Thisform.ybuild()
Endproc
Procedure combo1.Init
With This
.AddItem("Skin
.AddItem("skin
.AddItem("skin
.AddItem("skin
.ListIndex=1
.Value=1
.Style=2
Endwith
Endproc
Enddefine
*
*-- EndDefine: ytinyMCE

1(default)")
2 (O2k7 )")
3(silver)")
4(black)")

With this can compile a project created to an executable application.


Click on code to select [then copy] -click outside to deselect

*3*

*this needs only a web link to tinymce.min.js , no need to


download or install anything.its a very basic richtext editor.

Local m.myvar
TEXT to m.myvar

noshow

<!DOCTYPE html>
<html>
<head>
<title> A basic tinyMCE richtext editor</title>
<script
src="http://tinymce.cachefly.net/4.3/tinymce.min.js"></script>
<script>tinymce.init({ selector:'textarea' });</script>
</head>
<body>
<textarea rows="15" cols="80" style="width:100%">Easy (and
free!) </textarea>
</body>
</html>
ENDTEXT
Set Safe Off
Local m.lcdest
m.lcdest=Addbs(Sys(2023))+"ytest.html"
Strtofile(m.myvar,m.lcdest)

Declare Integer BringWindowToTop In user32 Integer


Publi apie

apie=Newobject("internetexplorer.application")
With apie
.menubar=0
.Toolbar=0
.StatusBar=0
.Resizable=1
.Width=800
.Height=400
.Top=(Sysmetric(2)-.Height)/2
.Left=(Sysmetric(1)-.Width)/2
.Navigate(m.lcdest)
BringWindowToTop(.HWnd)
.Visible=.T.
Endwith

*tip & trick


can copy and paste any portion of a web page in the tinyMCE editor.it accepts
text+images+...the save method is not effective here , but the print is fully
capable and can print the pasted section into a pdf file with a virtual printer
(as pdfcreator for ex.).
that ensure to save only the desired section of a web page.

Grabbing cool web Apps with visual foxpro - Visual Foxpro codes

http://yousfi.over-blog.com/2015/11/grabbing-cool-web-apps-from-visualfoxpro.html
see in this post :code *8* for the initial idea of the richtext editor tinyMCE.

A VFP Html Richtext editor - Visual Foxpro codes


This is a Wysiwyg html editor drived by automation from visual foxpro. (what you
see is what you get). It uses the vfp browser (Internet Explorer).(remember to Apply
IE11 emulation for another and ...
http://yousfi.over-blog.com/2015/02/a-vfp-html-richtext-editor.html

Published on Visual foxpro, TinyMCE, Ric

After the start you can create a new database. A few sample customers
will be added automatically. Now you can add an order and to this order
add order items. The product templates for each order item can be
selected in a data based table. There are data entry forms to add new
customers, countries (to modify VAT) and products.

Multidemo Mainform

The demo integrates document management an invoice can be saved


as PDF to a database, but it does not implement versioning. In a real
application it should not be possible to change an invoice after it was sent

out. Since WPViewPDF demo was used, the printout of the PDF
includes a demo watermark.

You might also like