##############################################################################
# VoiceCode, a programming-by-voice environment
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# (C)2000, David C. Fox
#
##############################################################################
"""interface and partial implementation of a new generic GUI editor which
can function as an interface to the mediator simulation, or as a test client"""
import debug
import sys, os
import TextBuffer
import Object
def silent_load_file(buffer, file_name):
"""load a new file into an existing buffer, but avoid sending the
change_callback to the managing SourceBuffTB, because we are about to
rename the buffer.
**INPUTS**
*TextBufferChangeSpec buffer* -- the buffer
*STR file_name* -- full path of the file to load
**OUTPUTS**
*BOOL* -- true if the file was loaded successfully
"""
callback = buffer.get_change_callback()
buffer.set_change_callback(None)
success = buffer.load_file(file_name)
buffer.set_change_callback(callback)
return success
def clear_buffer(buffer):
"""clear the contents of a TextBufferChangeSpec to make room for it
to contain a new, empty, file, but avoid sending the change_callback
to the managing SourceBuffTB.
**INPUTS**
*TextBufferChangeSpec buffer* -- the buffer
**OUTPUTS**
*none*
"""
callback = buffer.get_change_callback()
buffer.set_change_callback(None)
buffer.set_text("")
buffer.set_change_callback(callback)
class GenEdit(Object.OwnerObject):
"""base class for new generic GUI editor which can function as an
interface to the mediator simulation, or as a test client
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*AppState app_control* -- reference to the parent
AppState object, which requires certain callbacks
"""
def __init__(self, **args):
"""**Note:** GenEdit requires the app_control attribute to
perform the appropriate callbacks to AppState. However, since
AppStateGenEdit can't be constructed without a reference to an
existing GenEdit, AppStateGenEdit.__init__ will call
set_app_control to set its value."""
self.deep_construct(GenEdit,
{'app_control': None},
args)
self.add_grandparent('app_control')
# note: app_control is a reference to a (grand)parent object, which must
# be deleted so that that object can be deleted. However, since
# AppState is usually considered an interface to an editor, it does not
# "own" GenEdit and its cleanup method won't call GenEdit's.
# Therefore, unless a subclass of GenEdit is also an owner object and
# has an owner, GenEdit must call cleanup itself when the user
# tells it to exit
def file_name(self, buff_name):
"""returns the current filename associated with a given buffer
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*STR* -- the filename
"""
debug.virtual('GenEdit.file_name')
def set_app_control(self, app_control):
"""method called by app_control's (AppStateGenEdit.) __init__
to supply reference to itself, so that GenEdit can perform
callbacks.
**INPUTS**
*AppStateGenEdit app_control* -- the AppState interface
**OUTPUTS**
*none*
"""
self.app_control = app_control
def mic_change(self, state):
"""function to receive microphone state change callbacks
**INPUTS**
*STR* state -- new state ('on', 'off', 'sleeping', 'disabled')
**OUTPUTS**
*none*
"""
pass
# no-op by default, can be overridden
def is_active(self):
"""indicates whether an editor frame is active
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if frame window is active
"""
debug.virtual('GenEdit.is_active')
def editor_has_focus(self):
"""indicates whether the editor window has the focus
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor window has the focus
"""
debug.virtual('GenEdit.editor_has_focus')
def app_active_buffer_name(self):
"""Returns the name of the buffer currently active in the
GenEdit editor.
**INPUTS**
*none*
**OUTPUTS**
*STR* -- buffer name of current buffer, or None if there is none
"""
debug.virtual('GenEdit.app_active_buffer_name')
def app_change_buffer(self, buff_name):
"""Changes the external application's active buffer.
**INPUTS**
STR *buff_name* -- Name of the buffer to switch to.
**OUTPUTS**
*BOOL* -- true if buff_name exists and the application
successfully switches to it
"""
debug.virtual('GenEdit.app_change_buffer')
def open_buffers(self):
"""retrieve a list of the names of open buffers from the
application.
**INPUTS**
*none*
**OUTPUTS**
*[STR]* -- list of the names of open buffers
"""
debug.virtual('GenEdit.open_buffers')
def editor_buffer(self, buff_name):
"""returns a reference to the TextBufferChangeSpec embedded
in the GUI which corresponds to buffer buff_name
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*TextBufferChangeSpec* -- the TextBufferChangeSpec
"""
debug.virtual('GenEdit.editor_buffer')
def active_frame(self):
"""returns the currently active frame, if any
**INPUTS**
*none*
**OUTPUTS**
*GenEditFrame* -- the currently active frame, or None if
no frame of the editor is active.
"""
debug.virtual('GenEdit.active_frame')
def multiple_windows(self):
"""does editor support multiple windows per instance?
Note: the purpose of this function is to allow the RecogStartMgr
to determine whether a previously unknown window could belong to
this known instance. Therefore, Emacs running in text mode
should return false, even though it can have (sub-)windows in
a single frame.
Note: multiple windows of remote editors running in a remote display
which appears as a single window to be local operating system
(X servers in single window mode, VNC) will not appear to the mediator
as having separate windows. However, the mediator will perform a
separate check to detect this, so remote editors which support
multiple windows should return true, regardless of the remote
display method.
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor supports opening multiple editor windows.
"""
debug.virtual('GenEdit.multiple_windows')
def multiple_buffers(self):
"""does editor support multiple open buffers?
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor supports having multiple buffers open
at the same time"""
debug.virtual('GenEdit.multiple_windows')
def new_buffer(self, buff_name, buffer, perform_callback = 1):
"""adds a new buffer, optionally performing a callback to the
AppState interface
**INPUTS**
*STR buff_name* -- the name of the new buffer
*TextBufferChangeSpec buffer* -- the TextBufferChangeSpec
interface to the new buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's open buffer callback
**OUTPUTS**
*BOOL* -- true if the new buffer was added successfully
"""
debug.virtual('GenEdit.new_buffer')
def rename_buffer(self, buff_name, new_buff_name, perform_callback = 1):
"""renames a buffer, optionally performing a callback to the
AppState interface
**INPUTS**
*STR buff_name* -- the old name of the new buffer
*STR new_buff_name* -- the new name of the new buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's rename buffer callback
**OUTPUTS**
*BOOL* -- true if the new buffer was renamed successfully
"""
debug.virtual('GenEdit.rename_buffer')
def open_file(self, file_name, perform_callback = 0):
"""opens a new file
**INPUTS**
*STR file_name* -- the full path of the file to open
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will not invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
**OUTPUTS**
STR *buff_name* -- Unique name of the buffer in which the file
was opened. Returns *None* if the editor was not able to open
the file.
"""
debug.virtual('GenEdit.open_file')
def prompt_to_save(self, buff_name):
"""prompts the user to save the current buffer before closing it,
or cancel. Note: prompt_to_save should save if the user so
indicates, and update the entry in self.filenames corresponding
to the buffer, but should not close the buffer, because
open_file could still fail.
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*BOOL* -- true if the user saved or told GenEdit to proceed
without saving, false if the user asked for the action causing
the buffer closing to be cancelled.
"""
debug.virtual('GenEdit.prompt_to_save')
def overwrite_prompt(self, buff_name, full_path):
"""prompts to see if the user is sure that he/she wants to
overwrite an existing file
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
*STR full_path* -- path name of file to save
**OUTPUTS**
*BOOL* -- true if the user approves of overwriting the file
"""
debug.virtual('GenEdit.overwrite_prompt')
def open_file_dialog(self):
"""prompts for a file to open
**INPUTS**
*none*
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
debug.virtual('GenEdit.open_file_dialog')
def save_as_dialog(self, buff_name):
"""prompts for a filename under which to save the file, and
confirms overwriting
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
debug.virtual('GenEdit.save_as_dialog')
def save_file(self, buff_name, full_path = None, no_prompt = 0,
rename_buff = 1, ask_for_new_name = 0, perform_callback = 0):
"""Saves the buffer buff_name to a file
**INPUTS**
*STR buff_name* -- the name of the buffer
*STR full_path* -- path name of file to save, or None to use the
current file name, or prompt
*BOOL no_prompt* -- if true, don't prompt before overwriting
an existing file.
*BOOL rename_buff* -- if false, do not rename the buffer
*BOOL ask_for_new_name* -- if true, prompt for a new name, even if the
buffer already has a corresponding filename. Ignored unless
full_path == None.
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's rename buffer callback. AppState
should use the default value of false, because
AppState.save_file invokes the callback itself, but
save_file initiated from the frame by the user must
use perform_callback = 1
**OUTPUTS**
*STR* -- new buffer name if successful, or None if the save
failed
"""
debug.virtual('GenEdit.save_file')
def app_close_buffer(self, buff_name, save=0):
"""Close a buffer.
**INPUTS**
STR *buff_name* -- name of buffer to close
INT *save* -- *-1* -> don't save the buffer
*0* -> query user if buffer needs saving
*1* -> save without querying user
**OUTPUTS**
*BOOL* -- true if the editor does close the buffer
"""
debug.virtual('GenEdit.app_close_buffer')
def set_instance_string(self, instance_string):
"""sets the title string which is included in the full title
displayed in the title bar
**INPUTS**
*STR* instance_string -- string to include as part of the title
**OUTPUTS**
*BOOL* -- true if the editor can and will include the
instance string in its window title for all windows
containing editor buffers.
"""
debug.virtual('GenEdit.set_instance_string')
def on_exit(self, ID = None):
"""method by which a frame can notify GenEdit that the user has
selected the Exit item from the File menu. The user may have an
opportunity to cancel this command (e.g. through the cancel button
in a dialog prompting to save modified files)
**NOTE:** GenEdit is responsible for telling all frames to
cleanup and close, so the calling should not assume that
it is in a sane state when this method returns.
*BOOL* -- true if the editor is exiting in response to this
event (unless, e.g., the user has hit cancel in response to a
save modified files dialog)
**INPUTS**
*INT ID* -- ID of the frame sending the event, or None if the
event doesn't originate from a frame.
**OUTPUTS**
*none*
"""
debug.virtual('GenEdit.on_exit')
class GenEditBuffers(GenEdit):
"""partial implementation of GenEdit
**NOTE:** implicit in the use of corresponding_frame is the assumption
that no buffer ever appears in more than one frame
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*{STR: TextBufferChangeSpec} buffers* -- map from buffer names to
TextBufferChangeSpec references (in fact these buffers must
support the VisibleBuffer, StoreableTextBuffer, and NumberedLines
interfaces as well)
*{STR: BOOL} scratch_buffers* -- set of buffers designated as
scratch buffers, not needing to be saved
*{STR: STR} filenames* -- map from buffer names to filenames
*STR curr_dir* -- the current directory
*BOOL multiple* -- does this GenEdit implementation support multiple
buffers?
"""
def __init__(self, multiple = 0, curr_dir = None, **args):
self.deep_construct(GenEditBuffers,
{'multiple': multiple,
'buffers': {},
'scratch_buffers': {},
'filenames': {},
'curr_dir': curr_dir
}, args)
def mark_as_scratch(self, buff_name, as_scratch = 1):
"""mark a buffer as a scratch buffer which never needs to be
saved.
**NOTE:** Use this method with caution, as it can cause the user
to lose work when exiting the editor. This method is used to
avoid having to save buffers created/modified by regression
testing.
**INPUTS**
*STR buff_name* -- name of the buffer
*BOOL as_scratch* -- whether to mark the buffer as a scratch
buffer or unmark it
**OUTPUTS**
*none*
"""
if as_scratch:
self.scratch_buffers[buff_name] = 1
else:
try:
del self.scratch_buffers[buff_name]
except KeyError:
pass
def remove_other_references(self):
"""additional cleanup to ensure that this object's references to
its owned objects are the last remaining references
**INPUTS**
*none*
**OUTPUTS**
*none*
"""
self.buffers = None
self.filenames = None
# subclasses must call their parent class's remove_other_references
# function, after performing their own duties
GenEdit.remove_other_references(self)
def file_name(self, buff_name):
"""returns the current filename associated with a given buffer
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*STR* -- the filename
"""
try:
return self.filenames[buff_name]
except KeyError:
return None
def open_buffers(self):
"""retrieve a list of the names of open buffers from the
application.
**INPUTS**
*none*
**OUTPUTS**
*[STR]* -- list of the names of open buffers
"""
# print 'open buffers: ', self.buffers.keys()
return self.buffers.keys()
def modified_buffer(self, buff_name):
"""returns a list of names buffers which have been modified
since the last time they were saved
**INPUTS**
*none*
**OUTPUTS**
*[STR]* -- the list of buffer names
"""
buffer = self.buffers[buff_name]
if buffer.modified():
try:
self.scratch_buffers[buff_name]
except KeyError:
pass
else:
# if buffer is a scratch buffer, pretend it is unmodified
return 0
if buffer.len() != 0 or self.filenames[buff_name] != None:
# our method of creating a new buffer when one is closed inadvertently
# makes the new, empty buffer appear to have been modified
return 1
return 0
def modified_buffers(self):
"""returns a list of names buffers which have been modified
since the last time they were saved
**INPUTS**
*none*
**OUTPUTS**
*[STR]* -- the list of buffer names
"""
modified = []
# print 'checking modified buffers:'
# print 'all buffers:', self.buffers.keys()
for buff_name in self.buffers.keys():
# print 'buff_name = "%s"' % buff_name
if self.modified_buffer(buff_name):
modified.append(buff_name)
return modified
def editor_buffer(self, buff_name):
"""returns a reference to the TextBufferChangeSpec embedded in
the GUI which corresponds to buffer buff_name
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*TextBufferChangeSpec* -- the TextBufferChangeSpec
"""
if buff_name in self.buffers.keys():
return self.buffers[buff_name]
return None
def multiple_buffers(self):
"""does editor support multiple open buffers?
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor supports having multiple buffers open
at the same time"""
return self.multiple
def new_buffer(self, buff_name, buffer, perform_callback = 1):
"""adds a new buffer, optionally performing a callback to the
AppState interface
**INPUTS**
*STR buff_name* -- the name of the new buffer
*TextBufferChangeSpec buffer* -- the TextBufferChangeSpec interface
to the new buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's open buffer callback
**OUTPUTS**
*BOOL* -- true if the new buffer was added successfully
"""
if buff_name in self.open_buffers():
return 0
self.buffers[buff_name] = buffer
self.filenames[buff_name] = None
# this is incorrect -- do this in GenEdit filenames and in SourceBuffTB
# but not TextBufferChangeSpec
# buffer.name_file("")
if perform_callback and self.app_control:
self.app_control.open_buffer_cbk(buff_name)
return 1
def rename_buffer(self, buff_name, new_buff_name, perform_callback = 1):
"""renames a buffer, optionally performing a callback to the
AppState interface
**INPUTS**
*STR buff_name* -- the old name of the new buffer
*STR new_buff_name* -- the new name of the new buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's rename buffer callback
**OUTPUTS**
*BOOL* -- true if the new buffer was renamed successfully
"""
# print 'rename "%s" to "%s"' % (buff_name, new_buff_name)
# print 'buffers currently = ', self.open_buffers()
if buff_name not in self.open_buffers():
return 0
if new_buff_name in self.open_buffers():
return 0
if buff_name == new_buff_name:
return 1
self.buffers[new_buff_name] = self.buffers[buff_name]
self.filenames[new_buff_name] = self.filenames[buff_name]
del self.buffers[buff_name]
del self.filenames[buff_name]
# print 'after rename:'
# print 'buffers currently = ', self.open_buffers()
if perform_callback and self.app_control:
self.app_control.rename_buffer_cbk(buff_name, new_buff_name)
return 1
def show_buffer(self, buff_name, perform_callback = 1):
"""makes a buffer the foreground one.
Depending on the subclass of GenEditBuffers, this may activate a
different frame, or reveal a previously hidden buffer and update
the window title appropriately)
**INPUTS**
*STR buff_name* -- the name of the buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the curr_buffer_name_cbk
**OUTPUTS**
*none*
"""
success = self.app_change_buffer(buff_name)
self.update_title(buff_name)
if success and perform_callback:
if self.app_control:
self.app_control.curr_buffer_name_cbk(buff_name)
def open_file_new_buffer(self, file_name, new_buff_name,
user_initiated = 0, file_exists = 1):
"""opens a new file. Depending on the subclass of
GenEditBuffers, this may open a new frame, or hide the
previously visible buffer in the same frame.
**NOTE:** despite the name of this method, the
TextBufferChangeSpec returned can be an existing buffer,
if, e.g., the editor only has one window and one buffer.
**INPUTS**
*STR file_name* -- the full path of the file to open, or None
to create a new, empty buffer with no filename
*STR new_buff_name* -- the name which will be given to the new
buffer
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will not invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
*BOOL file_exists* -- indicates whether the specified file
exists (this allows us to specify a filename for an initially
empty buffer)
**OUTPUTS**
*BOOL* -- true if the file was opened successfully.
Note: if no file by the name file_name exists,
open_file_new_buffer should create a new, empty buffer with
that file name.
"""
debug.virtual('GenEditBuffers.open_file_new_buffer')
def corresponding_frame(self, buff_name):
"""returns a reference to the GenEditFrame containing the given
buffer
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*GenEditFrame* -- the corresponding frame
"""
debug.virtual('GenEditBuffers.corresponding_frame')
def generate_buffer_name(self, file_name):
"""find a new, unique buffer name for a buffer, given a file
name
**INPUTS**
*STR file_name* -- the short file name of the file (without the
path)
**OUTPUTS**
STR *buff_name* -- Unique name for the buffer
"""
new_buff_name = file_name
if new_buff_name in self.buffers.keys():
i = 2
alt_name = "%s (%d)" % (new_buff_name, i)
while alt_name in self.buffers.keys():
i = i +1
alt_name = "%s (%d)" % (new_buff_name, i)
new_buff_name = alt_name
return new_buff_name
def open_file(self, file_name = None, user_initiated = 0):
"""opens a new file
**INPUTS**
*STR file_name* -- the full path of the file to open, or None
to ask the user
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will not invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
**OUTPUTS**
STR *buff_name* -- Unique name of the buffer in which the file
was opened. Returns *None* if the editor was not able to open
the file. Note: if no file by the name file_name exists, the
regression tests expect the editor to open an empty buffer with
that name. Therefore, open_file should only fail
if the user cancels the open file command (e.g. if there is an
unsaved buffer) or if file_name was omitted and the user cancelled
the Open File dialog box
"""
if not self.multiple_buffers():
buff_name = self.app_active_buffer_name()
if buff_name != None:
if self.modified_buffer(buff_name):
# our method of creating a new buffer when one is closed inadvertently
# makes the new, empty buffer appear to have been modified
proceed = self.prompt_to_save(buff_name)
if not proceed:
return None
if file_name == None:
file_name = self.open_file_dialog()
if file_name == None:
return None
path, short = os.path.split(file_name)
new_buff_name = self.generate_buffer_name(short)
# print 'new buffer name is "%s"' % new_buff_name
file_or_none = file_name
file_exists = 1
if not os.path.exists(file_name):
# don't throw away the filename, just warn open_file_new_buffer that it
# the file doesn't exist
# file_or_none = None
file_exists = 0
# print 'before ofnb: buffers = ', self.buffers.keys()
# print 'file_or_none is ', file_or_none
success = self.open_file_new_buffer(file_or_none, new_buff_name,
user_initiated, file_exists = file_exists)
# print 'after ofnb: buffers = ', self.buffers.keys()
if not success:
return None
if path:
self.curr_dir = path
# moved to open_file_new_buffer to be before the open_buffer_cbk
# self.filenames[new_buff_name] = file_name
# this is incorrect -- do this in GenEdit filenames and in SourceBuffTB
# but not TextBufferChangeSpec
# self.buffers[new_buff_name].name_file(file_name)
if user_initiated and self.app_control:
if not self.multiple_buffers():
# AppStateGenEditor.tell_editor_to_open_file does this, if necessary,
# but if we are user-initiated we need to do it ourselves
self.app_control.close_buffer_cbk(buff_name)
# open_file_new_buffer will do this (because if it creates a new frame,
# it needs to do the open_buffer_cbk before the new_window_cbk)
self.show_buffer(new_buff_name, perform_callback =
user_initiated)
# print 'after show buffer: buffers = ', self.buffers.keys()
# single buffer editors just rename the buffer instead of creating a new
# one, so we don't want to try to remove the old one
# if not self.multiple:
# self.remove_buffer(buff_name, perform_callback = 1)
return new_buff_name
def save_specified(self, buff_name, full_path, no_prompt = 0):
"""Saves the buffer buff_name to a file. This version assumes
buff_name exists and full_path has actually been specified, but
will still prompt if asked to overwrite an existing file, unless
no_prompt is true.
**INPUTS**
*STR buff_name* -- the name of the buffer
*STR full_path* -- path name of file to save
*BOOL no_prompt* -- if true, don't prompt before overwriting
an existing file.
**OUTPUTS**
*BOOL* -- true if file was saved successfully
"""
buffer = self.editor_buffer(buff_name)
if buffer == None:
return 0
if os.path.exists(full_path) and not no_prompt:
frame = self.corresponding_frame(buff_name)
okay = frame.overwrite_prompt(buff_name, full_path)
if not okay:
return 0
return buffer.save_file(full_path)
def save_file(self, buff_name, full_path = None, no_prompt = 0,
rename_buff = 1, ask_for_new_name = 0, user_initiated = 0):
"""Saves the buffer buff_name to a file
**INPUTS**
*STR buff_name* -- the name of the buffer
*STR full_path* -- path name of file to save, or None to use the
current file name, or prompt
*BOOL no_prompt* -- if true, don't prompt before overwriting
an existing file.
*BOOL rename_buff* -- if false, do not rename the buffer
*BOOL ask_for_new_name* -- if true, prompt for a new name, even if the
buffer already has a corresponding filename. Ignored unless
full_path == None.
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will invoke the parent AppState's
rename buffer callback. If this method is called by AppState,
this would be redundant, because AppState.save_file invokes
the callback itself.
**OUTPUTS**
*STR* -- new buffer name if successful, or None if the save
failed
"""
buffer = self.editor_buffer(buff_name)
if buffer == None:
return None
try:
old_name = self.filenames[buff_name]
except KeyError:
old_name = None
f_path = full_path
quiet = no_prompt
if not f_path:
if old_name and not ask_for_new_name:
f_path = old_name
# if saving under same file name, never prompt
quiet = 1
else:
f_path = self.save_as_dialog(buff_name)
# save_as_dialog will already have prompted about overwriting existing
# file
quiet = 1
if f_path == None:
return None
success = self.save_specified(buff_name, f_path, quiet)
if not success:
return None
path, short = os.path.split(f_path)
if path:
self.curr_dir = path
new_buff_name = buff_name
self.filenames[buff_name] = f_path
if rename_buff:
# if the file acquired a new name, rename the buffer accordingly
if not old_name:
new_buff_name = self.generate_buffer_name(short)
else:
old_path, old_short = os.path.split(old_name)
if short != old_short:
new_buff_name = self.generate_buffer_name(short)
if new_buff_name != buff_name:
# print 'rename on save "%s" to "%s"' % (buff_name, new_buff_name)
self.rename_buffer(buff_name, new_buff_name,
perform_callback = user_initiated)
# buffer.name_file(f_path)
self.update_title(new_buff_name)
return new_buff_name
def update_title(self, buff_name):
"""update the window title of the frame containing buff_name to
reflect a new buffer name (unless that buffer is not the current
buffer displayed in that frame)
**INPUTS**
*STR buff_name* -- name of the buffer
**OUTPUTS**
*none*
"""
debug.virtual('GenEditBuffers.update_title')
def buffer_title(self, buff_name):
"""the buffer or file name to be used when a particular buffer is
active
**INPUTS**
*STR buff_name* -- name of the buffer
**OUTPUTS**
*STR* -- the name to combine with the application name and
instance string
"""
return buff_name
def remove_buffer(self, buff_name, perform_callback = 1):
"""remove a buffer from the list of buffers.
**Note:** this method only removes the buffer from GenEdit's
records, it does not destroy the underlying GUI buffer or the
window containing it. For that, use delete_buffer (which will
also call this method)
**INPUTS**
STR *buff_name* -- name of buffer to remove
*BOOL perform_callback* -- indicates whether this method should
invoke the curr_buffer_name_cbk
**OUTPUTS**
*none*
"""
# print 'removing buffer "%s"' %buff_name
if buff_name in self.open_buffers():
del self.buffers[buff_name]
del self.filenames[buff_name]
def delete_buffer(self, buff_name, perform_callback = 1):
"""delete a buffer
**INPUTS**
STR *buff_name* -- name of buffer to remove
*BOOL perform_callback* -- indicates whether this method should
invoke the curr_buffer_name_cbk
**OUTPUTS**
*none*
"""
debug.virtual('GenEditBuffers.delete_buffer')
def app_close_buffer(self, buff_name, save=0):
"""Close a buffer.
**INPUTS**
STR *buff_name* -- name of buffer to close
INT *save* -- *-1* -> don't save the buffer
*0* -> query user if buffer needs saving
*1* -> save without querying user
**OUTPUTS**
*BOOL* -- true if the editor does close the buffer
"""
# print 'closing buffer "%s"' % buff_name
# print 'currently buffers = ', self.buffers.keys()
# print 'GenEdit.app_close_buffer, save = ', save
buffer = self.buffers[buff_name]
if buffer == None:
return 0
if self.modified_buffer(buff_name):
if save == 1:
# print 'saving'
self.save_file(buff_name)
elif save == 0:
# print 'prompting'
proceed = self.prompt_to_save(buff_name)
if not proceed:
return 0
# else:
# print 'neither'
# print 'about to delete buffer "%s"' % buff_name
self.delete_buffer(buff_name)
# print 'just deleted buffer "%s"' % buff_name
return 1
class GenEditFrame(Object.OwnerObject):
"""abstract base class for frames for GenEdit
Note: to destroy a GenEditFrame, its owner must call the
cleanup method (inherited from OwnerObject), followed by close_window.
The reason for this is that some signals to close the frame are
sent as callbacks from the frame to the GenEditFrames object
which owns it. Separating the call to clean up the generic frame
from the call to destroy the GUI frame allows us to let the function
making the callback close the frame when the callback returns, while
still allowing an exit callback to close all frames.
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*INT frame_ID* -- unique ID for the frame (assigned by
GenEditFrames.add_frame, not by the GUI)
"""
def __init__(self, **args):
self.deep_construct(GenEditFrame,
{'frame_ID': None}, args)
def set_frame_ID(self, ID):
"""set the frame ID, which the frame will use to identify itself
to its owner in callbacks
**INPUTS**
*INT ID* -- unique ID for the frame (assigned by
GenEditFrames.add_frame, not by the GUI)
**OUTPUTS**
*none*
"""
self.frame_ID = ID
def close_window(self):
"""close the window corresponding to this frame
**NOTE:** The owner must call the frame's cleanup method before
calling this method to close the actual GUI frame
**INPUTS**
*none*
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.close_window')
def show(self, initial = 0):
"""show the window corresponding to this frame
**INPUTS**
*BOOL* initial -- is this the initial time the frame is shown?
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.show')
def open_buffers(self):
"""retrieve a list of the names of open buffers associated with
contained by this frame.
**INPUTS**
*none*
**OUTPUTS**
*[STR]* -- list of the names of open buffers
"""
debug.virtual('GenEditFrame.open_buffers')
def is_active(self):
"""indicates whether this frame is active
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if frame window is active
"""
debug.virtual('GenEditFrame.is_active')
def editor_has_focus(self):
"""indicates whether the editor control has the focus in this
frame
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor window has the focus
"""
debug.virtual('GenEditFrame.editor_has_focus')
def frame_active_buffer_name(self):
"""Returns the name of the buffer currently active in this
frame.
**INPUTS**
*none*
**OUTPUTS**
*STR* -- buffer name of current buffer, or None if there is none
"""
debug.virtual('GenEditFrame.frame_active_buffer_name')
def switch_to_buffer(self, buff_name):
"""Puts this frame in the foreground (if it isn't already), and
changes the active buffer to buff_name
**INPUTS**
STR *buff_name* -- Name of the buffer to switch to.
**OUTPUTS**
*BOOL* -- true if buff_name exists and the external application
successfully switches to it
"""
debug.virtual('GenEditFrame.switch_to_buffer')
def rename_buffer(self, buff_name, new_buff_name):
"""notifies the frame that one of its buffers has been renamed
**INPUTS**
*STR buff_name* -- the old name of the new buffer
*STR new_buff_name* -- the new name of the new buffer
**OUTPUTS**
*BOOL* -- false if the old buff_name was unknown
"""
debug.virtual('GenEditFrame.rename_buffer')
def remove_buffer(self, buff_name):
"""remove a buffer from the list of belong to this frame.
**Note:** this method only removes the buffer from GenEditFrame's
records, it does not destroy the underlying GUI buffer or the
window containing it.
**INPUTS**
STR *buff_name* -- name of buffer to remove
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.remove_buffer')
def delete_buffer(self, buff_name):
"""delete a buffer
**INPUTS**
STR *buff_name* -- name of buffer to remove
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.delete_buffer')
def set_instance_string(self, instance_string):
"""update the title to reflect the new instance string
**INPUTS**
*STR* instance_string -- string to include as part of the title
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.set_instance_string')
def update_title(self):
"""update the window title of the frame reflect a new buffer name
**INPUTS**
*none*
**OUTPUTS**
*none*
"""
debug.virtual('GenEditFrame.update_title')
def open_file_dialog(self, init_dir):
"""prompts for a file to open
**INPUTS**
*STR init_dir* -- the path of the initial directory for the Open
File dialog
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
debug.virtual('GenEditFrame.open_file_dialog')
def save_as_dialog(self, buff_name, init_dir):
"""prompts for a filename under which to save the file, and
confirms overwriting
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
*STR init_dir* -- the path of the initial directory for the Save
As dialog
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
debug.virtual('GenEditFrame.save_as_dialog')
def prompt_to_save(self, buff_name):
"""prompts the user to save the current buffer before closing it,
or cancel. Note: prompt_to_save should save if the user so
indicates, and update the entry in self.filenames corresponding
to the buffer, but should not close the buffer, because
open_file could still fail.
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*BOOL* -- true if the user saved or told GenEdit to proceed
without saving, false if the user asked for the action causing
the buffer closing to be cancelled.
"""
debug.virtual('GenEditFrame.prompt_to_save')
def overwrite_prompt(self, buff_name, full_path):
"""prompts to see if the user is sure that he/she wants to
overwrite an existing file
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
*STR full_path* -- path name of file to save
**OUTPUTS**
*BOOL* -- true if the user approves of overwriting the file
"""
debug.virtual('GenEditFrame.overwrite_prompt')
def editor_buffer(self, buff_name):
"""returns a reference to the TextBufferChangeSpec embedded
in the GUI which corresponds to buffer buff_name
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*TextBufferChangeSpec* -- the TextBufferChangeSpec
"""
debug.virtual('GenEditFrame.editor_buffer')
class GenEditFrameWithBuffers(GenEditFrame):
"""partial implementation of GenEditFrame which keeps track of
buffers belonging to this frame
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*GenEditFrames owner* -- parent GenEditFrames object
*STR app_name* -- portion of the title string indicating the name of this
particular GenEdit editor.
*STR instance_string* -- portion of the title string indicating the name of this particular instance.
"""
def __init__(self, owner, app_name, instance_string = "", **args):
self.deep_construct(GenEditFrameWithBuffers,
{'owner': owner,
'app_name': app_name,
'instance_string': instance_string
}, args
)
self.name_parent('owner')
def set_instance_string(self, instance_string):
"""update the title to reflect the new instance string
**INPUTS**
*STR* instance_string -- string to include as part of the title
**OUTPUTS**
*none*
"""
self.instance_string = instance_string
self.update_title()
def on_activate(self, activated = 1):
"""event handler for activation and deactivation events
A concrete subclass of GenEditFrameActivateEvent must call this
handler when this frame is activated or deactivated, so that it can
inform its owner. For subclasses which query for the active
frame instead of storing the state, calling this method is
optional
**INPUTS**
*BOOL active* -- whether this is an activate or deactivate event.
**OUTPUTS**
*none*
"""
if activated:
# window is being activated
self.owner.frame_activated( self.frame_ID, activated = 1)
else:
self.owner.frame_activated( self.frame_ID, activated = 0)
class GenEditFrameActivateEvent(GenEditFrameWithBuffers):
"""partial implementation of GenEditFrameWithBuffers with an event
sent on activation/deactivation (sent to foreground/background),
allowing us to keep track of whether the frame is active with a flag
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*INT ID* -- unique ID of this frame (usually the GUI ID), used for
communicating with the owner.
*BOOL active* -- flag indicating whether the frame is active (in the
foreground)
"""
def __init__(self, ID, **args):
self.deep_construct(GenEditFrameActivateEvent,
{'active':0,
'ID': ID
}, args)
def is_active(self):
"""indicates whether this frame is active
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if frame window is active
"""
return self.active
def on_activate(self, activated = 1):
"""event handler for activation and deactivation events
A concrete subclass of GenEditFrameActivateEvent must call this
handler when this frame is activated or deactivated, so that it can
inform its owner.
**INPUTS**
*BOOL active* -- whether this is an activate or deactivate event.
**OUTPUTS**
*none*
"""
GenEditFrameWithBuffers.on_activate(self, activated)
if activated:
# window is being activated
self.active = 1
else:
self.active = 0
class GenEditFrames(GenEditBuffers):
"""partial implementation of GenEditBuffers
**NOTE:** GenEditFrames assumes that no buffer ever appears in
more than one frame
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*{INT: GenEditFrame} frames* -- map from IDs to frames
*{STR: INT} corresponding_frames* -- map from buffer name to ID of
the frame containing that buffer
*INT next_ID* -- next available unique frame ID
*STR app_name* -- name of the application
(used as part of the title string)
*STR instance_string* -- portion of the title string indicating the name of this particular instance.
"""
def __init__(self, app_name, **args):
self.deep_construct(GenEditFrames,
{'frames': {},
'app_name': app_name,
'corresponding_frames': {},
'instance_string': "",
'next_ID': 0
}, args)
self.add_owned('frames')
def active_frame_ID(self):
"""returns the ID of the currently active frame, if any
**INPUTS**
*none*
**OUTPUTS**
*INT* -- the unique ID of the currently active frame, or None if
no frame of the editor is active.
"""
debug.virtual('GenEditFrames.active_frame_ID')
def active_frame(self):
"""returns the currently active frame, if any
**INPUTS**
*none*
**OUTPUTS**
*GenEditFrame* -- the currently active frame, or None if
no frame of the editor is active.
"""
ID = self.active_frame_ID()
if ID == None:
return None
return self.frames[ID]
def frame_activated(self, frame_ID, activated = 1):
"""callback used by GenEditFrame to notify GenEditFrames that
it has been activated or deactivated.
**INPUTS**
*STR frame_ID* -- the ID of the frame
*BOOL activated* -- true if the frame has just been activated,
otherwise false
**OUTPUTS**
*none*
"""
pass
def corresponding_frame(self, buff_name):
"""returns a reference to the GenEditFrame containing the given
buffer
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*GenEditFrame* -- the corresponding frame
"""
if buff_name in self.corresponding_frames.keys():
return self.frames[self.corresponding_frames[buff_name]]
return None
def new_frame(self, buff_name, instance_string = None):
"""creates a new frame of the appropriate concrete class
open buffer and new window callbacks to the AppState interface
**NOTE:** when adding a new frame with a buffer, you should call
new_buffer first, followed by add_frame
**INPUTS**
*STR buff_name* -- the name of the initial buffer for the frame
*STR instance_string* -- portion of the title string indicating
the name of this particular instance.
**OUTPUTS**
*GenEditFrame frame* -- the new frame
"""
debug.virtual('GenEditFrames.new_frame')
def add_frame(self, frame, buff_name, user_initiated = 1):
"""adds a new buffer to self.buffers and new frame window
containing that buffer to self.frames, optionally performing
open buffer and new window callbacks to the AppState interface
**NOTE:** when adding a new frame with a buffer, you should call
new_buffer first, followed by add_frame
**INPUTS**
*GenEditFrame frame* -- the new frame
*STR buff_name* -- the name of the initial buffer for the frame
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
**OUTPUTS**
*INT* -- ID of the new frame, or None if the frame was not
added successfully
"""
ID = self.next_ID
self.frames[ID] = frame
frame.set_frame_ID(ID)
self.next_ID = ID + 1
buffer = frame.editor_buffer(buff_name)
self.new_buffer(buff_name, buffer, perform_callback =
user_initiated)
self.corresponding_frames[buff_name] = ID
if user_initiated and self.app_control:
self.app_control.new_window_cbk()
return ID
def remove_buffer(self, buff_name, perform_callback = 1):
"""remove a buffer from the list of buffers.
**Note:** this method only removes the buffer from GenEdit's
records, it does not destroy the underlying GUI buffer or the
window containing it.
**INPUTS**
STR *buff_name* -- name of buffer to remove
*BOOL perform_callback* -- indicates whether this method should
invoke the curr_buffer_name_cbk
**OUTPUTS**
*none*
"""
if buff_name in self.open_buffers():
frame = self.corresponding_frame(buff_name)
frame.remove_buffer(buff_name)
del self.corresponding_frames[buff_name]
GenEditBuffers.remove_buffer(self, buff_name, perform_callback =
perform_callback)
def delete_buffer(self, buff_name, perform_callback = 1):
"""delete a buffer
**INPUTS**
STR *buff_name* -- name of buffer to remove
*BOOL perform_callback* -- indicates whether this method should
invoke the curr_buffer_name_cbk
**OUTPUTS**
*none*
"""
ID = self.corresponding_frames[buff_name]
frame = self.corresponding_frame(buff_name)
buffers = frame.open_buffers()
if len(buffers) > 1:
# not last buffer in the window
frame.delete_buffer(buff_name)
if perform_callback and self.app_control:
new_buff_name = frame.frame_active_buffer_name()
self.app_control.curr_buffer_name_cbk(new_buff_name)
self.remove_buffer(buff_name, 0)
return
# last buffer in window
if len(self.frames.keys()) == 1:
# and last window, so create a new, empty buffer in that window
new_buff_name = self.generate_buffer_name("")
buffer = self.editor_buffer(buff_name)
clear_buffer(buffer)
# print 'rename buffer on delete_buffer on last buffer in last window'
# print 'from "%s" to "%s"' % (buff_name, new_buff_name)
self.filenames[buff_name] = None
self.rename_buffer(buff_name, new_buff_name,
perform_callback = 0)
self.update_title(new_buff_name)
if perform_callback and self.app_control:
new_buff_name = frame.frame_active_buffer_name()
self.app_control.curr_buffer_name_cbk(new_buff_name)
else:
self.remove_buffer(buff_name, 0)
# in case the frame doesn't get a chance to let us know it was
# deactivated
frame.on_activate(0)
# cleanup the frame
frame.cleanup()
frame.close_window()
del self.frames[ID]
# and return true so that the frame will close itself
def prompt_to_save(self, buff_name):
"""prompts the user to save the current buffer before closing it,
or cancel. Note: prompt_to_save should save if the user so
indicates, and update the entry in self.filenames corresponding
to the buffer, but should not close the buffer, because
open_file could still fail.
**INPUTS**
*STR buff_name* -- the name of the buffer
**OUTPUTS**
*BOOL* -- true if the user saved or told GenEdit to proceed
without saving, false if the user asked for the action causing
the buffer closing to be cancelled.
"""
frame = self.corresponding_frame(buff_name)
debug.trace_call_stack('GenEditFrames.prompt_to_save')
return frame.prompt_to_save(buff_name)
def overwrite_prompt(self, buff_name, full_path):
"""prompts to see if the user is sure that he/she wants to
overwrite an existing file
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
*STR full_path* -- path name of file to save
**OUTPUTS**
*BOOL* -- true if the user approves of overwriting the file
"""
frame = self.corresponding_frame(buff_name)
return frame.overwrite_prompt(buff_name)
def open_file_dialog(self):
"""prompts for a file to open
**INPUTS**
*none*
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
# print 'in open_file_dialog'
# print self.active_frame
frame = self.active_frame()
init_dir = self.curr_dir
return frame.open_file_dialog(init_dir)
def save_as_dialog(self, buff_name):
"""prompts for a filename under which to save the file, and
confirms overwriting
**INPUTS**
*STR buff_name* -- the name of the buffer (used to find the
corresponding frame over which to pop up the Save As dialog)
**OUTPUTS**
*STR* -- the specified path, or None if the user cancelled
"""
frame = self.corresponding_frame(buff_name)
init_dir = self.curr_dir
try:
old_file_name = self.filenames[buff_name]
if not (old_file_name is None):
path, short = os.path.split(old_file_name)
if path:
init_dir = path
except KeyError:
pass
return frame.save_as_dialog(buff_name, init_dir)
def on_frame_close(self, ID):
"""method by which a frame can notify GenEdit that the user has
requested that it be closed (either through the close button
or a menu item)
The user may have an opportunity to cancel this command
(e.g. through the cancel button in a dialog prompting to save
modified files)
**NOTE:** Unless the user cancels, this method will
tell the frame to cleanup and close, so the caller should
not assume that it is in a sane state when this method returns.
**INPUTS**
*INT ID* -- ID of the frame sending the event
**OUTPUTS**
*BOOL* -- true if the frame should be closed in response to this
event (unless, e.g., the user has hit cancel in response to a
save modified files dialog)
"""
closing = 1
frame = self.frames[ID]
if len(self.frames.keys()) == 1:
return self.on_exit(ID)
buffers = frame.open_buffers()
for buff_name in buffers:
if self.modified_buffer(buff_name):
if not self.prompt_to_save(buff_name):
closing = 0
if not closing:
return 0
# remove buffers from our records, and notify the owner that
# the buffers are closing
for buff_name in buffers:
self.remove_buffer(buff_name, perform_callback = 1)
# self.app_control.close_buffer_cbk(buff_name)
# in case the frame doesn't get a chance to let us know it was
# deactivated
frame.on_activate(0)
# cleanup the frame
frame.cleanup()
del self.frames[ID]
# and return true so that the frame will close itself
return 1
def confirm_exit(self, ID = None):
"""method called by on_exit which allows the user to save files,
etc. or cancel the exit process
**INPUTS**
*INT ID* -- ID of the frame sending the event, or None if the
event doesn't originate from a frame. (Currently, this
parameter is ignored).
**OUTPUTS**
*BOOL* -- true if the editor is exiting in response to this
event (unless, e.g., the user has hit cancel in response to a
save modified files dialog)
"""
exiting = 1
# print 'here I am'
# sys.stdout.flush()
# print 'modified: ', repr(self.modified_buffers())
# sys.stdout.flush()
for buff_name in self.modified_buffers():
# print 'buff name is "%s"' % buff_name
# sys.stdout.flush()
if not self.prompt_to_save(buff_name):
exiting = 0
if not exiting:
return 0
return 1
def on_exit(self, ID = None):
"""method by which a frame can notify GenEdit that the user has
selected the Exit item from the File menu. The user may have an
opportunity to cancel this command (e.g. through the cancel button
in a dialog prompting to save modified files)
Unless the user cancels, on_exit will close all frames.
Depending on the particular GUI, this may cause the
GUI event loop to exit. If not, on_exit in the GenEditFrames
subclass for that GUI will have to call this method, and then
perform some additional processing if it returns true.
**NOTE:** GenEdit is responsible for telling all frames to
cleanup and close, so the caller should not assume that
it is in a sane state when this method returns.
on_exit
**INPUTS**
*INT ID* -- ID of the frame sending the event, or None if the
event doesn't originate from a frame. (Currently, this
parameter is ignored).
**OUTPUTS**
*BOOL* -- true if the editor is exiting in response to this
event (unless, e.g., the user has hit cancel in response to a
save modified files dialog)
"""
if not self.confirm_exit(ID = ID):
return 0
# notify the owner
# print 'notifying owner'
# sys.stdout.flush()
if self.app_control:
self.app_control.close_app_cbk()
# print 'done notifying owner'
# sys.stdout.flush()
# cleanup and close all frames
# print self.frames.keys()
# sys.stdout.flush()
for ID in self.frames.keys():
# print ID
# sys.stdout.flush()
frame = self.frames[ID]
# print 'cleaning'
# sys.stdout.flush()
frame.cleanup()
# print 'closing'
# sys.stdout.flush()
frame.close_window()
# print 'deleting reference'
# sys.stdout.flush()
del self.frames[ID]
# cleanup self, unless we have an owner which will do so for us
if self.owned_by() == None:
self.cleanup()
return 1
def rename_buffer(self, buff_name, new_buff_name, perform_callback = 1):
"""renames a buffer, optionally performing a callback to the
AppState interface
**INPUTS**
*STR buff_name* -- the old name of the new buffer
*STR new_buff_name* -- the new name of the new buffer
*BOOL perform_callback* -- indicates whether this method should
invoke the parent AppState's rename buffer callback
**OUTPUTS**
*BOOL* -- true if the new buffer was renamed successfully
"""
ID = self.corresponding_frames[buff_name]
frame = self.corresponding_frame(buff_name)
# print 'Frames.rename buffer "%s" to "%s"' % (buff_name, new_buff_name)
success = GenEditBuffers.rename_buffer(self, buff_name,
new_buff_name, perform_callback = perform_callback)
if success:
del self.corresponding_frames[buff_name]
self.corresponding_frames[new_buff_name] = ID
frame.rename_buffer(buff_name, new_buff_name)
def update_title(self, buff_name):
"""update the window title of the frame containing buff_name to
reflect a new buffer name (unless that buffer is not the current
buffer displayed in that frame)
**INPUTS**
*STR buff_name* -- name of the buffer
**OUTPUTS**
*none*
"""
frame = self.corresponding_frame(buff_name)
if frame:
frame.update_title()
def editor_has_focus(self):
"""indicates whether the editor window has the focus
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor window has the focus
"""
frame = self.active_frame()
if frame == None:
return 0
return frame.editor_has_focus()
def app_active_buffer_name(self):
"""Returns the name of the buffer currently active in the
GenEdit editor.
**INPUTS**
*none*
**OUTPUTS**
*STR* -- buffer name of current buffer, or None if there is none
"""
# print self, self.__class__, self.__class__.__bases__
# print self.active_frame
# print self.active_frame_ID
frame = self.active_frame()
if not frame:
return None
return frame.frame_active_buffer_name()
def app_change_buffer(self, buff_name):
"""Changes the external application's active buffer.
**INPUTS**
STR *buff_name* -- Name of the buffer to switch to.
**OUTPUTS**
*BOOL* -- true if buff_name exists and the application
successfully switches to it
"""
frame = self.corresponding_frame(buff_name)
if not frame:
return 0
frame.switch_to_buffer(buff_name)
return 1
def set_instance_string(self, instance_string):
"""sets the title string which is included in the full title
displayed in the title bar
**INPUTS**
*STR* instance_string -- string to include as part of the title
**OUTPUTS**
*BOOL* -- true if the editor, given the title escape sequence,
can and will include the instance string in its window title
for all windows containing editor buffers.
"""
self.instance_string = instance_string
for frame in self.frames.values():
frame.set_instance_string(instance_string)
return 1
class ActivateEventMixIn(Object.Object):
"""mix-in which implements GenEditFrames.is_active and active_frame_ID
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*INT current_frame* -- ID of the active frame
"""
def __init__(self, **args):
self.deep_construct(ActivateEventMixIn,
{'current_frame': None
}, args)
def frame_activated(self, frame_ID, activated = 1):
"""callback used by GenEditFrame to notify GenEditFrames that
it has been activated or deactivated.
**INPUTS**
*STR frame_ID* -- the ID of the frame
*BOOL activated* -- true if the frame has just been activated,
otherwise false
**OUTPUTS**
*none*
"""
# print 'activated = %d, %d' % (activated, frame_ID)
if activated:
self.current_frame = frame_ID
elif self.current_frame == frame_ID:
self.current_frame = None
def is_active(self):
"""indicates whether an editor frame is active
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if frame window is active
"""
if self.current_frame == None:
return 0
return 1
def active_frame_ID(self):
"""returns the ID of the currently active frame, if any
**INPUTS**
*none*
**OUTPUTS**
*INT* -- the unique ID of the currently active frame, or None if
no frame of the editor is active.
"""
return self.current_frame
class GenEditSingle(GenEditFrames):
"""partially concrete subclass of GenEditFrames with only a single
frame and a single buffer
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*INT only_ID* -- ID of the only frame
"""
def __init__(self, init_buff_name = "", show = 1, **args):
self.deep_construct(GenEditSingle,
{'only_ID': None}, args,
enforce_value = {'multiple': 0})
frame = self.new_frame(buff_name = init_buff_name)
# at this stage, GenEdit hasn't been added to AppStateGenEdit yet, so we
# use user_initiated = 1 so we don't do any AppState callbacks
ID = self.add_frame(frame, init_buff_name, user_initiated = 1)
if show:
frame.show(initial = 1)
self.only_ID = ID
def open_file_new_buffer(self, file_name, new_buff_name,
user_initiated = 0, file_exists = 1):
"""opens a new file. Depending on the subclass of
GenEditBuffers, this may open a new frame, or hide the
previously visible buffer in the same frame.
**NOTE:** despite the name of this method, the
TextBufferChangeSpec returned can be an existing buffer,
if, e.g., the editor only has one window and one buffer.
**INPUTS**
*STR file_name* -- the full path of the file to open, or None
to create a new, empty buffer
*STR new_buff_name* -- the name which will be given to the new
buffer
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
*BOOL file_exists* -- indicates whether the specified file
exists (this allows us to specify a filename for an initially
empty buffer)
**OUTPUTS**
*BOOL* -- true if the file was opened successfully.
Note: if no file by the name file_name exists,
open_file_new_buffer should create a new, empty buffer with
that file name.
"""
frame = self.frames[self.only_ID]
old_buff_name = frame.frame_active_buffer_name()
# print 'opening in old buffer "%s"' % old_buff_name
buffer = frame.editor_buffer(old_buff_name)
# we use clear_buffer and silent_load_file, because the old_buff_name
# refers to a scratch buffer which will be eliminated if we are
# successful. Therefore, we don't want any change events associated
# with that old buffer name.
if not file_exists or file_name == None:
clear_buffer(buffer)
success = 1
else:
success = silent_load_file(buffer, file_name)
if success:
self.rename_buffer(old_buff_name, new_buff_name,
perform_callback = 0)
self.filenames[new_buff_name] = file_name
if user_initiated and self.app_control:
self.app_control.open_buffer_cbk(new_buff_name)
return success
def multiple_windows(self):
"""does editor support multiple windows per instance?
Note: the purpose of this function is to allow the RecogStartMgr
to determine whether a previously unknown window could belong to
this known instance. Therefore, Emacs running in text mode
should return false, even though it can have (sub-)windows in
a single frame.
Note: multiple windows of remote editors running in a remote display
which appears as a single window to be local operating system
(X servers in single window mode, VNC) will not appear to the mediator
as having separate windows. However, the mediator will perform a
separate check to detect this, so remote editors which support
multiple windows should return true, regardless of the remote
display method.
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor supports opening multiple editor windows.
"""
return 0
class GenEditSimple(GenEditFrames):
"""partially concrete subclass of GenEdit with multiple frame windows, but
only one buffer per window.
**CLASS ATTRIBUTES**
*none*
**INSTANCE ATTRIBUTES**
*none*
"""
def __init__(self, init_buff_name = "", show = 1, **args):
self.deep_construct(GenEditSimple,
{}, args,
enforce_value = {'multiple': 1})
frame = self.new_frame(buff_name = init_buff_name)
# at this stage, GenEdit hasn't been added to AppStateGenEdit yet, so we
# use user_initiated = 1 so we don't do any AppState callbacks
ID = self.add_frame(frame, init_buff_name, user_initiated = 1)
if show:
frame.show(initial = 1)
def open_file_new_buffer(self, file_name, new_buff_name,
user_initiated = 0, file_exists = 1):
"""opens a new file and returns the corresponding
TextBufferChangeSpec. Depending on the subclass of
GenEditBuffers, this may open a new frame, or hide the
previously visible buffer in the same frame.
**NOTE:** despite the name of this method, the
TextBufferChangeSpec returned can be an existing buffer,
if, e.g., the editor only has one window and one buffer.
**INPUTS**
*STR file_name* -- the full path of the file to open, or None
to create a new, empty buffer
*STR new_buff_name* -- the name which will be given to the new
buffer
*BOOL user_initiated* -- indicates whether this method was
user-initiated or whether it was called by AppState.
In the latter case, it will invoke the parent AppState's
open buffer callback, because AppState.open_file invokes
the callback itself.
*BOOL file_exists* -- indicates whether the specified file
exists (this allows us to specify a filename for an initially
empty buffer)
**OUTPUTS**
*BOOL* -- true if the file was opened successfully.
Note: if no file by the name file_name exists,
open_file_new_buffer should create a new, empty buffer with
that file name.
"""
frame = self.new_frame(buff_name = new_buff_name,
instance_string = self.instance_string)
buffer = frame.editor_buffer(new_buff_name)
if not file_exists or file_name == None:
# unnecessary, since it is a new frame with a new buffer
# buffer.set_text("")
success = 1
else:
# unlike the single-buffer case above, we are working with a new buffer,
# so it is okay to send the change event (though that might result in
# the server being notified of the new file early, so maybe we shouldn't)
success = buffer.load_file(file_name)
if not success:
frame.cleanup()
frame.close_window()
return 0
self.filenames[new_buff_name] = file_name
ID = self.add_frame(frame, new_buff_name, user_initiated =
user_initiated)
if ID != None:
frame.show(initial = 1)
return 1
else:
frame.cleanup()
frame.close_window()
return 0
def multiple_windows(self):
"""does editor support multiple windows per instance?
Note: the purpose of this function is to allow the RecogStartMgr
to determine whether a previously unknown window could belong to
this known instance. Therefore, Emacs running in text mode
should return false, even though it can have (sub-)windows in
a single frame.
Note: multiple windows of remote editors running in a remote display
which appears as a single window to be local operating system
(X servers in single window mode, VNC) will not appear to the mediator
as having separate windows. However, the mediator will perform a
separate check to detect this, so remote editors which support
multiple windows should return true, regardless of the remote
display method.
**INPUTS**
*none*
**OUTPUTS**
*BOOL* -- true if editor supports opening multiple editor windows.
"""
return 1
# defaults for vim - otherwise ignore
# vim:sw=4