gui/gui-doc/scribblings/framework/editor.scrbl
2015-11-27 10:41:57 -06:00

480 lines
17 KiB
Racket

#lang scribble/doc
@(require scribble/manual scribble/extract)
@(require (for-label framework))
@(require (for-label scheme/gui))
@title{Editor}
@definterface[editor:basic<%> (editor<%>)]{
Classes matching this interface support the basic
@racket[editor<%>]
functionality required by the framework.
@defmethod*[(((has-focus?) boolean?))]{
This function returns @racket[#t] when the editor has the keyboard
focus. It is implemented using:
@method[editor<%> on-focus]
}
@defmethod*[(((local-edit-sequence?) boolean?))]{
Indicates if this editor is in an edit sequence. Enclosing buffer's
edit-sequence status is not considered by this method.
See
@method[editor<%> begin-edit-sequence]
and
@method[editor<%> end-edit-sequence]
for more info about edit sequences.
}
@defmethod*[(((run-after-edit-sequence (thunk (-> void?)) (tag (or/c symbol? #f) #f)) void?))]{
This method is used to install callbacks that will be run after any
edit-sequence completes.
The procedure @racket[thunk] will be called immediately if the edit is
not in an edit-sequence. If the edit is in an edit-sequence, it will
be called when the edit-sequence completes.
If @racket[tag] is a symbol, the @racket[thunk] is keyed on that symbol, and
only one thunk per symbol will be called after the
edit-sequence. Specifically, the last call to
@method[editor:basic<%> run-after-edit-sequence]'s argument will be called.
}
@defmethod*[(((get-top-level-window) (or/c #f (is-a?/c top-level-window<%>))))]{
Returns the
@racket[top-level-window<%>]
currently associated with this buffer.
This does not work for embedded editors.
}
@defmethod*[(((save-file-out-of-date?) boolean?))]{
Returns @racket[#t] if the file on disk has been modified, by some other program.
}
@defmethod*[(((save-file/gui-error (filename (or/c path? #f) #f) (format (or/c (quote guess) (quote standard) (quote text) (quote text-force-cr) (quote same) (quote copy)) (quote same)) (show-errors? boolean? #t)) boolean?))]{
This method is an alternative to
@method[editor<%> save-file]. Rather than showing errors via the original stdout, it
opens a dialog with an error message showing the error.
The result indicates if an error happened (the error has
already been shown to the user). It returns @racket[#t] if
no error occurred and @racket[#f] if an error occurred.
}
@defmethod*[(((load-file/gui-error (filename (or/c string? #f) #f) (format (or/c (quote guess) (quote standard) (quote text) (quote text-force-cr) (quote same) (quote copy)) (quote guess)) (show-errors? boolean? #t)) boolean?))]{
This method is an alternative to
@method[editor<%> load-file]. Rather than showing errors via the original stdout, it
opens a dialog with an error message showing the error.
The result indicates if an error happened (the error has
already been shown to the user). It returns @racket[#t] if
no error occurred and @racket[#f] if an error occurred.
}
@defmethod*[(((on-close) void?))]{
This method is called when an editor is closed.
Typically, this method is called when the frame
containing the editor is closed, but in some cases an
editor is considered ``closed'' before the frame it is
in is closed (e.g., when a tab in DrRacket is closed), and
thus @method[editor:basic<%> on-close] will be called at that point.
See also @method[editor:basic<%> can-close?] and @method[editor:basic<%>
close].
Does nothing.
}
@defmethod*[(((can-close?) boolean?))]{
This method is called to query the editor if is okay to
close the editor. Although there is no visible effect
associated with closing an editor, there may be some cleanup
actions that need to be run when the user is finished with
the editor (asking if it should be saved, for example).
See also
@method[editor:basic<%> on-close] and
@method[editor:basic<%> close].
Returns @racket[#t].
}
@defmethod*[(((close) boolean?))]{
This method is merely
@racketblock[
(if (can-close?)
(begin (on-close) #t)
#f)]
It is intended as a shorthand, helper method for closing
an editor. See also
@method[editor:basic<%> can-close?]
and
@method[editor:basic<%> on-close].
}
@defmethod*[(((get-filename/untitled-name) string?))]{
Returns the printed version of the filename for this
editor. If the editor doesn't yet have a filename, it
returns a symbolic name (something like "Untitled").
}
@defmethod[(get-pos/text [event (is-a?/c mouse-event%)])
(values (or/c false/c number?)
(or/c false/c (is-a?/c editor<%>)))]{
Calls @method[editor:basic<%> get-pos/text-dc-location] with
the x and y coordinates of @racket[event].
}
@defmethod[(get-pos/text-dc-location [x exact-integer?] [y exact-integer?])
(values (or/c false/c number?)
(or/c false/c (is-a?/c editor<%>)))]{
This method's first result is @racket[#f] when the mouse
event does not correspond to a location in the editor.
If the second result is a @racket[text%] object, then the
first result will be a position in the editor and
otherwise the first result will be @racket[#f].
The @racket[editor<%>] object will always be the nearest
enclosing editor containing the point (@racket[x], @racket[y]).
}
}
@defmixin[editor:basic-mixin (editor<%>) (editor:basic<%>)]{
This provides the basic editor services required by the rest of the
framework.
The result of this mixin uses the same initialization arguments as the
mixin's argument.
Each instance of a class created with this mixin contains a private
@racket[keymap%] that is chained to the global keymap via:
@racket[(send keymap chain-to-keymap (keymap:get-global) #f)].
This installs the global keymap @racket[keymap:get-global] to
handle keyboard and mouse mappings not handled by @racket[keymap]. The
global keymap is created when the framework is invoked.
@defmethod*[#:mode augment (((can-save-file? (filename string?) (format symbol?)) boolean?))]{
Checks to see if the file on the disk has been modified out
side of this editor, using
@method[editor:basic<%> save-file-out-of-date?].
If it has, this method prompts the user to be sure they want to save.
}
@defmethod*[#:mode augment (((after-save-file (success? boolean?)) void?))]{
If the current filename is not a temporary filename, this method calls
@racket[handler:add-to-recent]with the current filename.
to add the new filename to the list of recently opened files.
Additionally, updates a private instance variable with the
modification time of the file, for using in implementing
@method[editor:basic<%> save-file-out-of-date?].
}
@defmethod*[#:mode augment (((after-load-file (success? boolean?)) void?))]{
Updates a private instance variable with the modification
time of the file, for using in implementing
@method[editor:basic<%> save-file-out-of-date?]
}
@defmethod*[#:mode override (((on-focus (on? boolean?)) void?))]{
Manages the state to implement
@method[editor:basic<%> has-focus?]
}
@defmethod*[#:mode augment (((on-edit-sequence) boolean?))]{
Always returns @racket[#t]. Updates a flag for
@method[editor:basic<%> local-edit-sequence?]
}
@defmethod*[#:mode augment (((after-edit-sequence) void?))]{
Helps to implement
@method[editor:basic<%> run-after-edit-sequence].
}
@defmethod*[#:mode override (((on-new-box (type (or/c (quote pasteboard) (quote text)))) (is-a?/c editor-snip%)))]{
Creates instances of
@racket[pasteboard:basic%]
or
@racket[text:basic%]
instead of the built in
@racket[pasteboard%]
and
@racket[text%]
classes.
}
@defmethod[#:mode override (on-new-image-snip [filename (or/c path? false/c)]
[kind (one-of/c 'unknown 'gif 'jpeg 'xbm 'xpm 'bmp 'pict)]
[relative-path? any/c]
[inline? any/c])
(is-a?/c image-snip%)]{
@racketblock[
(super on-new-image-snip
(if (eq? kind 'unknown) 'unknown/mask kind)
relative-path?
inline?)]
}
@defmethod*[#:mode override (((get-file (directory (or/c path-string? false/c))) string))]{
Uses
@racket[finder:get-file]
to find a filename. Also, sets the parameter
@racket[finder:dialog-parent-parameter]
to the result of
@method[editor:basic<%> get-top-level-window].
}
@defmethod*[#:mode override (((put-file (directory (or/c path? false/c)) (default-name (or/c path? false/c))) string))]{
Uses
@racket[finder:put-file]
to find a filename. Also, sets the parameter
@racket[finder:dialog-parent-parameter]
to the result of
@method[editor:basic<%> get-top-level-window].
}
}
@definterface[editor:standard-style-list<%> (editor<%>)]{
This interface is implemented by the results of
@racket[editor:standard-style-list-mixin].
}
@defmixin[editor:standard-style-list-mixin (editor<%>) (editor:standard-style-list<%>)]{
The mixin adds code to the initialization
of the class that sets the editor's style
list (via
@method[editor<%> set-style-list])
to the result of
@racket[editor:get-standard-style-list].
In addition, it calls
@method[editor<%> set-load-overwrites-styles]
with @racket[#f].
This ensures that saved files with different
settings for the style list do not clobber
the shared style list.
}
@definterface[editor:keymap<%> (editor:basic<%>)]{
Classes matching this interface add support for mixing in multiple
keymaps. They provides an extensible interface to chained keymaps,
through the
@method[editor:keymap<%> get-keymaps]
method.
This editor is initialized by calling
@racket[add-editor-keymap-functions],
@racket[add-text-keymap-functions], and
@racket[add-pasteboard-keymap-functions].
@defmethod*[(((get-keymaps) (list-of (is-a?/c keymap%))))]{
The keymaps returned from this method are chained to this
@racket[editor<%>]'s keymap.
The result of this method should not change -- that is, it
should return the same list of keymaps each time it is
called.
See also @racket[editor:add-after-user-keymap].
Returns @racket[(list (keymap:get-user) (keymap:get-global))] by default.
}
}
@defmixin[editor:keymap-mixin (editor:basic<%>) (editor:keymap<%>)]{
This provides a mixin that implements the
@racket[editor:keymap<%>]
interface.
}
@definterface[editor:autowrap<%> (editor:basic<%>)]{
Classes implementing this interface keep the
@method[editor<%> auto-wrap]
state set based on the
@racket['framework:auto-set-wrap?] preference
(see @racket[preferences:get] for more information about preferences).
They install a preferences callback with
@racket[preferences:add-callback]
that sets the state when the preference changes and
initialize the value of
@method[editor<%> auto-wrap]
to the current value of @racket['framework:auto-set-wrap?]
via
@racket[preferences:get].
}
@defmixin[editor:autowrap-mixin (editor:basic<%>) (editor:autowrap<%>)]{
See
@racket[editor:autowrap<%>]
}
@definterface[editor:file<%> (editor:keymap<%>)]{
Objects supporting this interface are expected to support files.
@defmethod*[(((get-can-close-parent) (or/c false (is-a?/c frame%) (is-a?/c dialog%))))]{
The result of this method is used as the parent for the
dialog that asks about closing.
Returns @racket[#f] by default.
}
@defmethod*[(((update-frame-filename) void?))]{
Attempts to find a frame that displays this editor. If it
does, it updates the frame's title based on a new filename
in the editor.
}
@defmethod*[(((allow-close-with-no-filename?) boolean?))]{
This method indicates if closing the file when it hasn't
been saved is a reason to alert the user. See also
@method[editor:file-mixin can-close?].
Returns @racket[#f] by default.
}
@defmethod[(user-saves-or-not-modified? [allow-cancel? #t]) boolean?]{
If the file has not been saved, this prompts the user about saving and,
if the user says to save, then it saves the file.
The result is @racket[#t] if the save file is up to date, or if
the user says it is okay to continue without saving. Generally used
when closing the file or quiting the app.
}
}
@defmixin[editor:file-mixin (editor:keymap<%>) (editor:file<%>)]{
This editor locks itself when the file that is opened is read-only in
the filesystem.
The class that this mixin produces uses the same initialization
arguments as its input.
@defmethod*[#:mode override (((set-filename (name string?) (temp? boolean? #f)) void?))]{
Updates the filename on each frame displaying this editor, for each
frame that matches
@racket[frame:editor<%>].
}
@defmethod*[#:mode augment (((can-close?) boolean?))]{
If the
@method[editor:file<%> allow-close-with-no-filename?]
method returns @racket[#f], this method checks to see if the file
has been saved at all yet. If not, it asks the user
about saving (and saves if they ask).
If the
@method[editor:file<%> allow-close-with-no-filename?]
method returns @racket[#t], this method does as before,
except only asks if the editor's
@method[editor<%> get-filename]method returns a path.
Also calls inner.
}
@defmethod*[#:mode override (((get-keymaps) (list-of (is-a?/c keymap%))))]{
This returns a list containing the super-class's keymaps, plus the
result of
@racket[keymap:get-file]
}
}
@definterface[editor:backup-autosave<%> (editor:basic<%>)]{
Classes matching this interface support backup files and autosaving.
@defmethod*[(((backup?) boolean?))]{
Indicates whether this
@racket[editor<%>]
should be backed up.
Returns the value of the @racket[preferences:get] applied to
@racket['framework:backup-files?]. @index{'framework:backup-files?}
}
@defmethod*[(((autosave?) boolean?))]{
Indicates whether this
@racket[editor<%>]
should be autosaved.
Returns @racket[#t].
}
@defmethod*[(((do-autosave) (or/c #f path?)))]{
This method is called to perform the autosaving. See also
@racket[autosave:register]
When the file has been modified since it was last saved and autosaving it
turned on (via the @method[editor:backup-autosave<%> autosave?] method) an
autosave file is created for this @racket[editor<%>].
Returns the filename where the autosave took place, or @racket[#f] if none
did.
}
@defmethod*[(((remove-autosave) void?))]{
This method removes the autosave file associated with this
@racket[editor<%>].
}
}
@defmixin[editor:backup-autosave-mixin (editor:basic<%>) (editor:backup-autosave<%> autosave:autosavable<%>)]{
This mixin adds backup and autosave functionality to an editor.
During initialization, this object is registered with
@racket[autosave:register].
The result of this mixin uses the same initialization arguments as the
mixin's argument.
@defmethod*[#:mode augment (((on-save-file (filename path?) (format (one-of/c (quote guess) (quote standard) (quote text) (quote text-force-cr) (quote same) (quote copy)))) bool))]{
If a backup file has not been created this session for this file,
deletes any existing backup file and copies the old save file into the
backup file. For the backup file's name, see
@racket[path-utils:generate-backup-name]
}
@defmethod*[#:mode augment (((on-close) void?))]{
Deletes the autosave file and turns off autosaving.
}
@defmethod*[#:mode augment (((on-change) void?))]{
Sets a flag indicating that this @racket[editor<%>] needs to be autosaved.
}
@defmethod*[#:mode override (((set-modified (modified? any/c)) void?))]{
If the file is no longer modified, this method deletes the autosave
file. If it is, it updates a flag to indicate that the autosave file
is out of date.
}
}
@definterface[editor:info<%> (editor:basic<%>)]{
An @racket[editor<%>] matching this interface provides information about its
lock state to its @racket[top-level-window<%>].
}
@defmixin[editor:info-mixin (editor:basic<%>) (editor:info<%>)]{
This editor tells the frame when it is locked and unlocked.
See also @racket[frame:text-info<%>].
@defmethod*[#:mode override (((lock (lock? boolean?)) void?))]{
Uses @method[editor:basic<%> run-after-edit-sequence]
to call @method[frame:info<%> lock-status-changed].
}
}
@defclass[editor:font-size-message% canvas% ()]{
@defconstructor[([message (or/c string? (listof string?))]
[stretchable-height any/c #f])]{
The @racket[message] field controls the initial contents. If there
is a list of strings, then each string is put on a separate line.
If there is just a single string, it is split on newlines and then
treated as if it were a list.
The @racket[stretchable-height] has the opposite default from the
@racket[canvas%] superclass.
}
@defmethod[(set-message [message (or/c string? (listof string?))]) void?]{
Changes the message.
If @racket[message] is a list of strings, then each
string is put on a separate line. If there is just a
single string, it is split on newlines and then treated as
if it were a list argument.
}
}
@(include-previously-extracted "main-extracts.rkt" #rx"^editor:")