Files
wxWidgets/src/common/docview.cpp
Vadim Zeitlin 407020995e 1. undid the "simplification" of patch 1096066 which resulted in a crash
(see this patch discussion)
2. check that all views agree to close before starting to delete them
3. commented the code so that the next poor soul looking at it will know
   that deleting a view can delete the document which deletes -- surely
   wasn't obvious to me...


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@32172 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2005-02-19 13:45:31 +00:00

2428 lines
63 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/docview.cpp
// Purpose: Document/view classes
// Author: Julian Smart
// Modified by:
// Created: 01/02/97
// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "docview.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_DOC_VIEW_ARCHITECTURE
#ifndef WX_PRECOMP
#include "wx/string.h"
#include "wx/utils.h"
#include "wx/app.h"
#include "wx/dc.h"
#include "wx/dialog.h"
#include "wx/menu.h"
#include "wx/list.h"
#include "wx/filedlg.h"
#include "wx/intl.h"
#include "wx/log.h"
#endif
#include "wx/ffile.h"
#ifdef __WXMAC__
#include "wx/filename.h"
#endif
#ifdef __WXGTK__
#include "wx/mdi.h"
#endif
#if wxUSE_PRINTING_ARCHITECTURE
#include "wx/prntbase.h"
#include "wx/printdlg.h"
#endif
#include "wx/msgdlg.h"
#include "wx/choicdlg.h"
#include "wx/docview.h"
#include "wx/confbase.h"
#include "wx/file.h"
#include "wx/cmdproc.h"
#include <stdio.h>
#include <string.h>
#if wxUSE_STD_IOSTREAM
#include "wx/ioswrap.h"
#if wxUSE_IOSTREAMH
#include <fstream.h>
#else
#include <fstream>
#endif
#else
#include "wx/wfstream.h"
#endif
// ----------------------------------------------------------------------------
// wxWidgets macros
// ----------------------------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(wxDocument, wxEvtHandler)
IMPLEMENT_ABSTRACT_CLASS(wxView, wxEvtHandler)
IMPLEMENT_ABSTRACT_CLASS(wxDocTemplate, wxObject)
IMPLEMENT_DYNAMIC_CLASS(wxDocManager, wxEvtHandler)
IMPLEMENT_CLASS(wxDocChildFrame, wxFrame)
IMPLEMENT_CLASS(wxDocParentFrame, wxFrame)
#if wxUSE_PRINTING_ARCHITECTURE
IMPLEMENT_DYNAMIC_CLASS(wxDocPrintout, wxPrintout)
#endif
IMPLEMENT_DYNAMIC_CLASS(wxFileHistory, wxObject)
// ----------------------------------------------------------------------------
// function prototypes
// ----------------------------------------------------------------------------
static inline wxString FindExtension(const wxChar *path);
static wxWindow* wxFindSuitableParent(void);
// ----------------------------------------------------------------------------
// local constants
// ----------------------------------------------------------------------------
static const wxChar *s_MRUEntryFormat = wxT("&%d %s");
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// local functions
// ----------------------------------------------------------------------------
static wxString FindExtension(const wxChar *path)
{
wxString ext;
wxSplitPath(path, NULL, NULL, &ext);
// VZ: extensions are considered not case sensitive - is this really a good
// idea?
return ext.MakeLower();
}
// ----------------------------------------------------------------------------
// Definition of wxDocument
// ----------------------------------------------------------------------------
wxDocument::wxDocument(wxDocument *parent)
{
m_documentModified = false;
m_documentParent = parent;
m_documentTemplate = (wxDocTemplate *) NULL;
m_commandProcessor = (wxCommandProcessor*) NULL;
m_savedYet = false;
}
bool wxDocument::DeleteContents()
{
return true;
}
wxDocument::~wxDocument()
{
DeleteContents();
if (m_commandProcessor)
delete m_commandProcessor;
if (GetDocumentManager())
GetDocumentManager()->RemoveDocument(this);
// Not safe to do here, since it'll invoke virtual view functions
// expecting to see valid derived objects: and by the time we get here,
// we've called destructors higher up.
//DeleteAllViews();
}
bool wxDocument::Close()
{
if (OnSaveModified())
return OnCloseDocument();
else
return false;
}
bool wxDocument::OnCloseDocument()
{
// Tell all views that we're about to close
NotifyClosing();
DeleteContents();
Modify(false);
return true;
}
// Note that this implicitly deletes the document when the last view is
// deleted.
bool wxDocument::DeleteAllViews()
{
wxDocManager* manager = GetDocumentManager();
// first check if all views agree to be closed
const wxList::iterator end = m_documentViews.end();
for ( wxList::iterator i = m_documentViews.begin(); i != end; ++i )
{
wxView *view = (wxView *)*i;
if ( !view->Close() )
return false;
}
// all views agreed to close, now do close them
if ( m_documentViews.empty() )
{
// normally the document would be implicitly deleted when the last view
// is, but if don't have any views, do it here instead
if ( manager && manager->GetDocuments().Member(this) )
delete this;
}
else // have views
{
// as we delete elements we iterate over, don't use the usual "from
// begin to end" loop
for ( ;; )
{
wxView *view = (wxView *)*m_documentViews.begin();
bool isLastOne = m_documentViews.size() == 1;
// this always deletes the node implicitly and if this is the last
// view also deletes this object itself (also implicitly, great),
// so we can't test for m_documentViews.empty() after calling this!
delete view;
if ( isLastOne )
break;
}
}
return true;
}
wxView *wxDocument::GetFirstView() const
{
if (m_documentViews.GetCount() == 0)
return (wxView *) NULL;
return (wxView *)m_documentViews.GetFirst()->GetData();
}
wxDocManager *wxDocument::GetDocumentManager() const
{
return (m_documentTemplate ? m_documentTemplate->GetDocumentManager() : (wxDocManager*) NULL);
}
bool wxDocument::OnNewDocument()
{
if (!OnSaveModified())
return false;
if (OnCloseDocument()==false) return false;
DeleteContents();
Modify(false);
SetDocumentSaved(false);
wxString name;
GetDocumentManager()->MakeDefaultName(name);
SetTitle(name);
SetFilename(name, true);
return true;
}
bool wxDocument::Save()
{
if (!IsModified() && m_savedYet)
return true;
if ( m_documentFile.empty() || !m_savedYet )
return SaveAs();
return OnSaveDocument(m_documentFile);
}
bool wxDocument::SaveAs()
{
wxDocTemplate *docTemplate = GetDocumentTemplate();
if (!docTemplate)
return false;
#if defined(__WXMSW__) || defined(__WXGTK__) || defined(__WXMAC__)
wxString filter = docTemplate->GetDescription() + wxT(" (") + docTemplate->GetFileFilter() + wxT(")|") + docTemplate->GetFileFilter();
// Now see if there are some other template with identical view and document
// classes, whose filters may also be used.
if (docTemplate->GetViewClassInfo() && docTemplate->GetDocClassInfo())
{
wxList::compatibility_iterator node = wxDocManager::GetDocumentManager()->GetTemplates().GetFirst();
while (node)
{
wxDocTemplate *t = (wxDocTemplate*) node->GetData();
if (t->IsVisible() && t != docTemplate &&
t->GetViewClassInfo() == docTemplate->GetViewClassInfo() &&
t->GetDocClassInfo() == docTemplate->GetDocClassInfo())
{
// add a '|' to separate this filter from the previous one
if ( !filter.empty() )
filter << wxT('|');
filter << t->GetDescription() << wxT(" (") << t->GetFileFilter() << wxT(") |")
<< t->GetFileFilter();
}
node = node->GetNext();
}
}
#else
wxString filter = docTemplate->GetFileFilter() ;
#endif
wxString tmp = wxFileSelector(_("Save as"),
docTemplate->GetDirectory(),
wxFileNameFromPath(GetFilename()),
docTemplate->GetDefaultExtension(),
filter,
wxSAVE | wxOVERWRITE_PROMPT,
GetDocumentWindow());
if (tmp.empty())
return false;
wxString fileName(tmp);
wxString path, name, ext;
wxSplitPath(fileName, & path, & name, & ext);
if (ext.empty())
{
fileName += wxT(".");
fileName += docTemplate->GetDefaultExtension();
}
SetFilename(fileName);
SetTitle(wxFileNameFromPath(fileName));
// Notify the views that the filename has changed
wxList::compatibility_iterator node = m_documentViews.GetFirst();
while (node)
{
wxView *view = (wxView *)node->GetData();
view->OnChangeFilename();
node = node->GetNext();
}
// Files that were not saved correctly are not added to the FileHistory.
if (!OnSaveDocument(m_documentFile))
return false;
// A file that doesn't use the default extension of its document template cannot be opened
// via the FileHistory, so we do not add it.
if (docTemplate->FileMatchesTemplate(fileName))
{
GetDocumentManager()->AddFileToHistory(fileName);
}
else
{
// The user will probably not be able to open the file again, so
// we could warn about the wrong file-extension here.
}
return true;
}
bool wxDocument::OnSaveDocument(const wxString& file)
{
if ( !file )
return false;
if ( !DoSaveDocument(file) )
return false;
Modify(false);
SetFilename(file);
SetDocumentSaved(true);
#ifdef __WXMAC__
wxFileName fn(file) ;
fn.MacSetDefaultTypeAndCreator() ;
#endif
return true;
}
bool wxDocument::OnOpenDocument(const wxString& file)
{
if (!OnSaveModified())
return false;
if ( !DoOpenDocument(file) )
return false;
SetFilename(file, true);
Modify(false);
m_savedYet = true;
UpdateAllViews();
return true;
}
#if wxUSE_STD_IOSTREAM
wxSTD istream& wxDocument::LoadObject(wxSTD istream& stream)
#else
wxInputStream& wxDocument::LoadObject(wxInputStream& stream)
#endif
{
return stream;
}
#if wxUSE_STD_IOSTREAM
wxSTD ostream& wxDocument::SaveObject(wxSTD ostream& stream)
#else
wxOutputStream& wxDocument::SaveObject(wxOutputStream& stream)
#endif
{
return stream;
}
bool wxDocument::Revert()
{
return false;
}
// Get title, or filename if no title, else unnamed
bool wxDocument::GetPrintableName(wxString& buf) const
{
if (!m_documentTitle.empty())
{
buf = m_documentTitle;
return true;
}
else if (!m_documentFile.empty())
{
buf = wxFileNameFromPath(m_documentFile);
return true;
}
else
{
buf = _("unnamed");
return true;
}
}
wxWindow *wxDocument::GetDocumentWindow() const
{
wxView *view = GetFirstView();
if (view)
return view->GetFrame();
else
return wxTheApp->GetTopWindow();
}
wxCommandProcessor *wxDocument::OnCreateCommandProcessor()
{
return new wxCommandProcessor;
}
// true if safe to close
bool wxDocument::OnSaveModified()
{
if (IsModified())
{
wxString title;
GetPrintableName(title);
wxString msgTitle;
if (!wxTheApp->GetAppName().empty())
msgTitle = wxTheApp->GetAppName();
else
msgTitle = wxString(_("Warning"));
wxString prompt;
prompt.Printf(_("Do you want to save changes to document %s?"),
(const wxChar *)title);
int res = wxMessageBox(prompt, msgTitle,
wxYES_NO|wxCANCEL|wxICON_QUESTION,
GetDocumentWindow());
if (res == wxNO)
{
Modify(false);
return true;
}
else if (res == wxYES)
return Save();
else if (res == wxCANCEL)
return false;
}
return true;
}
bool wxDocument::Draw(wxDC& WXUNUSED(context))
{
return true;
}
bool wxDocument::AddView(wxView *view)
{
if (!m_documentViews.Member(view))
{
m_documentViews.Append(view);
OnChangedViewList();
}
return true;
}
bool wxDocument::RemoveView(wxView *view)
{
(void)m_documentViews.DeleteObject(view);
OnChangedViewList();
return true;
}
bool wxDocument::OnCreate(const wxString& WXUNUSED(path), long flags)
{
if (GetDocumentTemplate()->CreateView(this, flags))
return true;
else
return false;
}
// Called after a view is added or removed.
// The default implementation deletes the document if
// there are no more views.
void wxDocument::OnChangedViewList()
{
if (m_documentViews.GetCount() == 0)
{
if (OnSaveModified())
{
delete this;
}
}
}
void wxDocument::UpdateAllViews(wxView *sender, wxObject *hint)
{
wxList::compatibility_iterator node = m_documentViews.GetFirst();
while (node)
{
wxView *view = (wxView *)node->GetData();
if (view != sender)
view->OnUpdate(sender, hint);
node = node->GetNext();
}
}
void wxDocument::NotifyClosing()
{
wxList::compatibility_iterator node = m_documentViews.GetFirst();
while (node)
{
wxView *view = (wxView *)node->GetData();
view->OnClosingDocument();
node = node->GetNext();
}
}
void wxDocument::SetFilename(const wxString& filename, bool notifyViews)
{
m_documentFile = filename;
if ( notifyViews )
{
// Notify the views that the filename has changed
wxList::compatibility_iterator node = m_documentViews.GetFirst();
while (node)
{
wxView *view = (wxView *)node->GetData();
view->OnChangeFilename();
node = node->GetNext();
}
}
}
bool wxDocument::DoSaveDocument(const wxString& file)
{
wxString msgTitle;
if (!wxTheApp->GetAppName().empty())
msgTitle = wxTheApp->GetAppName();
else
msgTitle = wxString(_("File error"));
#if wxUSE_STD_IOSTREAM
wxSTD ofstream store(file.mb_str());
if (store.fail() || store.bad())
#else
wxFileOutputStream store(file);
if (store.GetLastError() != wxSTREAM_NO_ERROR)
#endif
{
(void)wxMessageBox(_("Sorry, could not open this file for saving."), msgTitle, wxOK | wxICON_EXCLAMATION,
GetDocumentWindow());
// Saving error
return false;
}
if (!SaveObject(store))
{
(void)wxMessageBox(_("Sorry, could not save this file."), msgTitle, wxOK | wxICON_EXCLAMATION,
GetDocumentWindow());
// Saving error
return false;
}
return true;
}
bool wxDocument::DoOpenDocument(const wxString& file)
{
wxString msgTitle;
if (!wxTheApp->GetAppName().empty())
msgTitle = wxTheApp->GetAppName();
else
msgTitle = wxString(_("File error"));
#if wxUSE_STD_IOSTREAM
wxSTD ifstream store(file.mb_str());
if (store.fail() || store.bad())
#else
wxFileInputStream store(file);
if (store.GetLastError() != wxSTREAM_NO_ERROR)
#endif
{
(void)wxMessageBox(_("Sorry, could not open this file."), msgTitle, wxOK|wxICON_EXCLAMATION,
GetDocumentWindow());
return false;
}
#if wxUSE_STD_IOSTREAM
LoadObject(store);
if ( !store && !store.eof() )
#else
int res = LoadObject(store).GetLastError();
if ((res != wxSTREAM_NO_ERROR) &&
(res != wxSTREAM_EOF))
#endif
{
(void)wxMessageBox(_("Sorry, could not open this file."), msgTitle, wxOK|wxICON_EXCLAMATION,
GetDocumentWindow());
return false;
}
return true;
}
// ----------------------------------------------------------------------------
// Document view
// ----------------------------------------------------------------------------
wxView::wxView()
{
m_viewDocument = (wxDocument*) NULL;
m_viewFrame = (wxFrame *) NULL;
}
wxView::~wxView()
{
GetDocumentManager()->ActivateView(this, false);
m_viewDocument->RemoveView(this);
}
// Extend event processing to search the document's event table
bool wxView::ProcessEvent(wxEvent& event)
{
if ( !GetDocument() || !GetDocument()->ProcessEvent(event) )
return wxEvtHandler::ProcessEvent(event);
return true;
}
void wxView::OnActivateView(bool WXUNUSED(activate), wxView *WXUNUSED(activeView), wxView *WXUNUSED(deactiveView))
{
}
void wxView::OnPrint(wxDC *dc, wxObject *WXUNUSED(info))
{
OnDraw(dc);
}
void wxView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint))
{
}
void wxView::OnChangeFilename()
{
if (GetFrame() && GetDocument())
{
wxString title;
GetDocument()->GetPrintableName(title);
GetFrame()->SetTitle(title);
}
}
void wxView::SetDocument(wxDocument *doc)
{
m_viewDocument = doc;
if (doc)
doc->AddView(this);
}
bool wxView::Close(bool deleteWindow)
{
if (OnClose(deleteWindow))
return true;
else
return false;
}
void wxView::Activate(bool activate)
{
if (GetDocument() && GetDocumentManager())
{
OnActivateView(activate, this, GetDocumentManager()->GetCurrentView());
GetDocumentManager()->ActivateView(this, activate);
}
}
bool wxView::OnClose(bool WXUNUSED(deleteWindow))
{
return GetDocument() ? GetDocument()->Close() : true;
}
#if wxUSE_PRINTING_ARCHITECTURE
wxPrintout *wxView::OnCreatePrintout()
{
return new wxDocPrintout(this);
}
#endif // wxUSE_PRINTING_ARCHITECTURE
// ----------------------------------------------------------------------------
// wxDocTemplate
// ----------------------------------------------------------------------------
wxDocTemplate::wxDocTemplate(wxDocManager *manager,
const wxString& descr,
const wxString& filter,
const wxString& dir,
const wxString& ext,
const wxString& docTypeName,
const wxString& viewTypeName,
wxClassInfo *docClassInfo,
wxClassInfo *viewClassInfo,
long flags)
{
m_documentManager = manager;
m_description = descr;
m_directory = dir;
m_defaultExt = ext;
m_fileFilter = filter;
m_flags = flags;
m_docTypeName = docTypeName;
m_viewTypeName = viewTypeName;
m_documentManager->AssociateTemplate(this);
m_docClassInfo = docClassInfo;
m_viewClassInfo = viewClassInfo;
}
wxDocTemplate::~wxDocTemplate()
{
m_documentManager->DisassociateTemplate(this);
}
// Tries to dynamically construct an object of the right class.
wxDocument *wxDocTemplate::CreateDocument(const wxString& path, long flags)
{
wxDocument *doc = DoCreateDocument();
if ( doc == NULL )
return (wxDocument *) NULL;
if (InitDocument(doc, path, flags))
{
return doc;
}
else
{
return (wxDocument *) NULL;
}
}
bool wxDocTemplate::InitDocument(wxDocument* doc, const wxString& path, long flags)
{
doc->SetFilename(path);
doc->SetDocumentTemplate(this);
GetDocumentManager()->AddDocument(doc);
doc->SetCommandProcessor(doc->OnCreateCommandProcessor());
if (doc->OnCreate(path, flags))
return true;
else
{
if (GetDocumentManager()->GetDocuments().Member(doc))
doc->DeleteAllViews();
return false;
}
}
wxView *wxDocTemplate::CreateView(wxDocument *doc, long flags)
{
wxView *view = DoCreateView();
if ( view == NULL )
return (wxView *) NULL;
view->SetDocument(doc);
if (view->OnCreate(doc, flags))
{
return view;
}
else
{
delete view;
return (wxView *) NULL;
}
}
// The default (very primitive) format detection: check is the extension is
// that of the template
bool wxDocTemplate::FileMatchesTemplate(const wxString& path)
{
return GetDefaultExtension().IsSameAs(FindExtension(path));
}
wxDocument *wxDocTemplate::DoCreateDocument()
{
if (!m_docClassInfo)
return (wxDocument *) NULL;
return (wxDocument *)m_docClassInfo->CreateObject();
}
wxView *wxDocTemplate::DoCreateView()
{
if (!m_viewClassInfo)
return (wxView *) NULL;
return (wxView *)m_viewClassInfo->CreateObject();
}
// ----------------------------------------------------------------------------
// wxDocManager
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxDocManager, wxEvtHandler)
EVT_MENU(wxID_OPEN, wxDocManager::OnFileOpen)
EVT_MENU(wxID_CLOSE, wxDocManager::OnFileClose)
EVT_MENU(wxID_CLOSE_ALL, wxDocManager::OnFileCloseAll)
EVT_MENU(wxID_REVERT, wxDocManager::OnFileRevert)
EVT_MENU(wxID_NEW, wxDocManager::OnFileNew)
EVT_MENU(wxID_SAVE, wxDocManager::OnFileSave)
EVT_MENU(wxID_SAVEAS, wxDocManager::OnFileSaveAs)
EVT_MENU(wxID_UNDO, wxDocManager::OnUndo)
EVT_MENU(wxID_REDO, wxDocManager::OnRedo)
EVT_UPDATE_UI(wxID_OPEN, wxDocManager::OnUpdateFileOpen)
EVT_UPDATE_UI(wxID_CLOSE, wxDocManager::OnUpdateFileClose)
EVT_UPDATE_UI(wxID_CLOSE_ALL, wxDocManager::OnUpdateFileClose)
EVT_UPDATE_UI(wxID_REVERT, wxDocManager::OnUpdateFileRevert)
EVT_UPDATE_UI(wxID_NEW, wxDocManager::OnUpdateFileNew)
EVT_UPDATE_UI(wxID_SAVE, wxDocManager::OnUpdateFileSave)
EVT_UPDATE_UI(wxID_SAVEAS, wxDocManager::OnUpdateFileSaveAs)
EVT_UPDATE_UI(wxID_UNDO, wxDocManager::OnUpdateUndo)
EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo)
#if wxUSE_PRINTING_ARCHITECTURE
EVT_MENU(wxID_PRINT, wxDocManager::OnPrint)
EVT_MENU(wxID_PREVIEW, wxDocManager::OnPreview)
EVT_UPDATE_UI(wxID_PRINT, wxDocManager::OnUpdatePrint)
EVT_UPDATE_UI(wxID_PREVIEW, wxDocManager::OnUpdatePreview)
#endif
END_EVENT_TABLE()
wxDocManager* wxDocManager::sm_docManager = (wxDocManager*) NULL;
wxDocManager::wxDocManager(long flags, bool initialize)
{
m_defaultDocumentNameCounter = 1;
m_flags = flags;
m_currentView = (wxView *) NULL;
m_maxDocsOpen = 10000;
m_fileHistory = (wxFileHistory *) NULL;
if (initialize)
Initialize();
sm_docManager = this;
}
wxDocManager::~wxDocManager()
{
Clear();
if (m_fileHistory)
delete m_fileHistory;
sm_docManager = (wxDocManager*) NULL;
}
// closes the specified document
bool wxDocManager::CloseDocument(wxDocument* doc, bool force)
{
if (doc->Close() || force)
{
// Implicitly deletes the document when
// the last view is deleted
doc->DeleteAllViews();
// Check we're really deleted
if (m_docs.Member(doc))
delete doc;
return true;
}
return false;
}
bool wxDocManager::CloseDocuments(bool force)
{
wxList::compatibility_iterator node = m_docs.GetFirst();
while (node)
{
wxDocument *doc = (wxDocument *)node->GetData();
wxList::compatibility_iterator next = node->GetNext();
if (!CloseDocument(doc, force))
return false;
// This assumes that documents are not connected in
// any way, i.e. deleting one document does NOT
// delete another.
node = next;
}
return true;
}
bool wxDocManager::Clear(bool force)
{
if (!CloseDocuments(force))
return false;
m_currentView = NULL;
wxList::compatibility_iterator node = m_templates.GetFirst();
while (node)
{
wxDocTemplate *templ = (wxDocTemplate*) node->GetData();
wxList::compatibility_iterator next = node->GetNext();
delete templ;
node = next;
}
return true;
}
bool wxDocManager::Initialize()
{
m_fileHistory = OnCreateFileHistory();
return true;
}
wxFileHistory *wxDocManager::OnCreateFileHistory()
{
return new wxFileHistory;
}
void wxDocManager::OnFileClose(wxCommandEvent& WXUNUSED(event))
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
if (doc->Close())
{
doc->DeleteAllViews();
if (m_docs.Member(doc))
delete doc;
}
}
void wxDocManager::OnFileCloseAll(wxCommandEvent& WXUNUSED(event))
{
CloseDocuments(false);
}
void wxDocManager::OnFileNew(wxCommandEvent& WXUNUSED(event))
{
CreateDocument( wxEmptyString, wxDOC_NEW );
}
void wxDocManager::OnFileOpen(wxCommandEvent& WXUNUSED(event))
{
if ( !CreateDocument( wxEmptyString, 0) )
{
OnOpenFileFailure();
}
}
void wxDocManager::OnFileRevert(wxCommandEvent& WXUNUSED(event))
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
doc->Revert();
}
void wxDocManager::OnFileSave(wxCommandEvent& WXUNUSED(event))
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
doc->Save();
}
void wxDocManager::OnFileSaveAs(wxCommandEvent& WXUNUSED(event))
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
doc->SaveAs();
}
void wxDocManager::OnPrint(wxCommandEvent& WXUNUSED(event))
{
#if wxUSE_PRINTING_ARCHITECTURE
wxView *view = GetCurrentView();
if (!view)
return;
wxPrintout *printout = view->OnCreatePrintout();
if (printout)
{
wxPrinter printer;
printer.Print(view->GetFrame(), printout, true);
delete printout;
}
#endif // wxUSE_PRINTING_ARCHITECTURE
}
void wxDocManager::OnPreview(wxCommandEvent& WXUNUSED(event))
{
#if wxUSE_PRINTING_ARCHITECTURE
wxView *view = GetCurrentView();
if (!view)
return;
wxPrintout *printout = view->OnCreatePrintout();
if (printout)
{
// Pass two printout objects: for preview, and possible printing.
wxPrintPreviewBase *preview = new wxPrintPreview(printout, view->OnCreatePrintout());
if ( !preview->Ok() )
{
delete preview;
wxMessageBox( _("Sorry, print preview needs a printer to be installed.") );
return;
}
wxPreviewFrame *frame = new wxPreviewFrame(preview, (wxFrame *)wxTheApp->GetTopWindow(), _("Print Preview"),
wxPoint(100, 100), wxSize(600, 650));
frame->Centre(wxBOTH);
frame->Initialize();
frame->Show(true);
}
#endif // wxUSE_PRINTING_ARCHITECTURE
}
void wxDocManager::OnUndo(wxCommandEvent& event)
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
if (doc->GetCommandProcessor())
doc->GetCommandProcessor()->Undo();
else
event.Skip();
}
void wxDocManager::OnRedo(wxCommandEvent& event)
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
return;
if (doc->GetCommandProcessor())
doc->GetCommandProcessor()->Redo();
else
event.Skip();
}
// Handlers for UI update commands
void wxDocManager::OnUpdateFileOpen(wxUpdateUIEvent& event)
{
event.Enable( true );
}
void wxDocManager::OnUpdateFileClose(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( (doc != (wxDocument*) NULL) );
}
void wxDocManager::OnUpdateFileRevert(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( (doc != (wxDocument*) NULL) );
}
void wxDocManager::OnUpdateFileNew(wxUpdateUIEvent& event)
{
event.Enable( true );
}
void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( doc && doc->IsModified() );
}
void wxDocManager::OnUpdateFileSaveAs(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( (doc != (wxDocument*) NULL) );
}
void wxDocManager::OnUpdateUndo(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
event.Enable(false);
else if (!doc->GetCommandProcessor())
event.Skip();
else
{
event.Enable( doc->GetCommandProcessor()->CanUndo() );
doc->GetCommandProcessor()->SetMenuStrings();
}
}
void wxDocManager::OnUpdateRedo(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
if (!doc)
event.Enable(false);
else if (!doc->GetCommandProcessor())
event.Skip();
else
{
event.Enable( doc->GetCommandProcessor()->CanRedo() );
doc->GetCommandProcessor()->SetMenuStrings();
}
}
void wxDocManager::OnUpdatePrint(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( (doc != (wxDocument*) NULL) );
}
void wxDocManager::OnUpdatePreview(wxUpdateUIEvent& event)
{
wxDocument *doc = GetCurrentDocument();
event.Enable( (doc != (wxDocument*) NULL) );
}
wxView *wxDocManager::GetCurrentView() const
{
if (m_currentView)
return m_currentView;
if (m_docs.GetCount() == 1)
{
wxDocument* doc = (wxDocument*) m_docs.GetFirst()->GetData();
return doc->GetFirstView();
}
return (wxView *) NULL;
}
// Extend event processing to search the view's event table
bool wxDocManager::ProcessEvent(wxEvent& event)
{
wxView* view = GetCurrentView();
if (view)
{
if (view->ProcessEvent(event))
return true;
}
return wxEvtHandler::ProcessEvent(event);
}
wxDocument *wxDocManager::CreateDocument(const wxString& path, long flags)
{
wxDocTemplate **templates = new wxDocTemplate *[m_templates.GetCount()];
int n = 0;
for (size_t i = 0; i < m_templates.GetCount(); i++)
{
wxDocTemplate *temp = (wxDocTemplate *)(m_templates.Item(i)->GetData());
if (temp->IsVisible())
{
templates[n] = temp;
n ++;
}
}
if (n == 0)
{
delete[] templates;
return (wxDocument *) NULL;
}
wxDocument* docToClose = NULL;
// If we've reached the max number of docs, close the
// first one.
if ( (int)GetDocuments().GetCount() >= m_maxDocsOpen )
{
wxDocument *doc = (wxDocument *)GetDocuments().GetFirst()->GetData();
docToClose = doc;
}
// New document: user chooses a template, unless there's only one.
if (flags & wxDOC_NEW)
{
if (n == 1)
{
if (docToClose)
{
if (!CloseDocument(docToClose, false))
{
delete[] templates;
return NULL;
}
}
wxDocTemplate *temp = templates[0];
delete[] templates;
wxDocument *newDoc = temp->CreateDocument(path, flags);
if (newDoc)
{
newDoc->SetDocumentName(temp->GetDocumentName());
newDoc->SetDocumentTemplate(temp);
newDoc->OnNewDocument();
}
return newDoc;
}
wxDocTemplate *temp = SelectDocumentType(templates, n);
delete[] templates;
if (temp)
{
if (docToClose)
{
if (!CloseDocument(docToClose, false))
{
return NULL;
}
}
wxDocument *newDoc = temp->CreateDocument(path, flags);
if (newDoc)
{
newDoc->SetDocumentName(temp->GetDocumentName());
newDoc->SetDocumentTemplate(temp);
newDoc->OnNewDocument();
}
return newDoc;
}
else
return (wxDocument *) NULL;
}
// Existing document
wxDocTemplate *temp;
wxString path2 = path;
if (flags & wxDOC_SILENT)
{
temp = FindTemplateForPath(path2);
if (!temp)
{
// Since we do not add files with non-default extensions to the FileHistory this
// can only happen if the application changes the allowed templates in runtime.
(void)wxMessageBox(_("Sorry, the format for this file is unknown."),
_("Open File"),
wxOK | wxICON_EXCLAMATION, wxFindSuitableParent());
}
}
else
temp = SelectDocumentPath(templates, n, path2, flags);
delete[] templates;
if (temp)
{
if (docToClose)
{
if (!CloseDocument(docToClose, false))
{
return NULL;
}
}
wxDocument *newDoc = temp->CreateDocument(path2, flags);
if (newDoc)
{
newDoc->SetDocumentName(temp->GetDocumentName());
newDoc->SetDocumentTemplate(temp);
if (!newDoc->OnOpenDocument(path2))
{
newDoc->DeleteAllViews();
// delete newDoc; // Implicitly deleted by DeleteAllViews
return (wxDocument *) NULL;
}
// A file that doesn't use the default extension of its document
// template cannot be opened via the FileHistory, so we do not
// add it.
if (temp->FileMatchesTemplate(path2))
AddFileToHistory(path2);
}
return newDoc;
}
return (wxDocument *) NULL;
}
wxView *wxDocManager::CreateView(wxDocument *doc, long flags)
{
wxDocTemplate **templates = new wxDocTemplate *[m_templates.GetCount()];
int n =0;
for (size_t i = 0; i < m_templates.GetCount(); i++)
{
wxDocTemplate *temp = (wxDocTemplate *)(m_templates.Item(i)->GetData());
if (temp->IsVisible())
{
if (temp->GetDocumentName() == doc->GetDocumentName())
{
templates[n] = temp;
n ++;
}
}
}
if (n == 0)
{
delete[] templates;
return (wxView *) NULL;
}
if (n == 1)
{
wxDocTemplate *temp = templates[0];
delete[] templates;
wxView *view = temp->CreateView(doc, flags);
if (view)
view->SetViewName(temp->GetViewName());
return view;
}
wxDocTemplate *temp = SelectViewType(templates, n);
delete[] templates;
if (temp)
{
wxView *view = temp->CreateView(doc, flags);
if (view)
view->SetViewName(temp->GetViewName());
return view;
}
else
return (wxView *) NULL;
}
// Not yet implemented
void wxDocManager::DeleteTemplate(wxDocTemplate *WXUNUSED(temp), long WXUNUSED(flags))
{
}
// Not yet implemented
bool wxDocManager::FlushDoc(wxDocument *WXUNUSED(doc))
{
return false;
}
wxDocument *wxDocManager::GetCurrentDocument() const
{
wxView *view = GetCurrentView();
if (view)
return view->GetDocument();
else
return (wxDocument *) NULL;
}
// Make a default document name
bool wxDocManager::MakeDefaultName(wxString& name)
{
name.Printf(_("unnamed%d"), m_defaultDocumentNameCounter);
m_defaultDocumentNameCounter++;
return true;
}
// Make a frame title (override this to do something different)
// If docName is empty, a document is not currently active.
wxString wxDocManager::MakeFrameTitle(wxDocument* doc)
{
wxString appName = wxTheApp->GetAppName();
wxString title;
if (!doc)
title = appName;
else
{
wxString docName;
doc->GetPrintableName(docName);
title = docName + wxString(_(" - ")) + appName;
}
return title;
}
// Not yet implemented
wxDocTemplate *wxDocManager::MatchTemplate(const wxString& WXUNUSED(path))
{
return (wxDocTemplate *) NULL;
}
// File history management
void wxDocManager::AddFileToHistory(const wxString& file)
{
if (m_fileHistory)
m_fileHistory->AddFileToHistory(file);
}
void wxDocManager::RemoveFileFromHistory(size_t i)
{
if (m_fileHistory)
m_fileHistory->RemoveFileFromHistory(i);
}
wxString wxDocManager::GetHistoryFile(size_t i) const
{
wxString histFile;
if (m_fileHistory)
histFile = m_fileHistory->GetHistoryFile(i);
return histFile;
}
void wxDocManager::FileHistoryUseMenu(wxMenu *menu)
{
if (m_fileHistory)
m_fileHistory->UseMenu(menu);
}
void wxDocManager::FileHistoryRemoveMenu(wxMenu *menu)
{
if (m_fileHistory)
m_fileHistory->RemoveMenu(menu);
}
#if wxUSE_CONFIG
void wxDocManager::FileHistoryLoad(wxConfigBase& config)
{
if (m_fileHistory)
m_fileHistory->Load(config);
}
void wxDocManager::FileHistorySave(wxConfigBase& config)
{
if (m_fileHistory)
m_fileHistory->Save(config);
}
#endif
void wxDocManager::FileHistoryAddFilesToMenu(wxMenu* menu)
{
if (m_fileHistory)
m_fileHistory->AddFilesToMenu(menu);
}
void wxDocManager::FileHistoryAddFilesToMenu()
{
if (m_fileHistory)
m_fileHistory->AddFilesToMenu();
}
size_t wxDocManager::GetHistoryFilesCount() const
{
return m_fileHistory ? m_fileHistory->GetCount() : 0;
}
// Find out the document template via matching in the document file format
// against that of the template
wxDocTemplate *wxDocManager::FindTemplateForPath(const wxString& path)
{
wxDocTemplate *theTemplate = (wxDocTemplate *) NULL;
// Find the template which this extension corresponds to
for (size_t i = 0; i < m_templates.GetCount(); i++)
{
wxDocTemplate *temp = (wxDocTemplate *)m_templates.Item(i)->GetData();
if ( temp->FileMatchesTemplate(path) )
{
theTemplate = temp;
break;
}
}
return theTemplate;
}
// Try to get a more suitable parent frame than the top window,
// for selection dialogs. Otherwise you may get an unexpected
// window being activated when a dialog is shown.
static wxWindow* wxFindSuitableParent()
{
wxWindow* parent = wxTheApp->GetTopWindow();
wxWindow* focusWindow = wxWindow::FindFocus();
if (focusWindow)
{
while (focusWindow &&
!focusWindow->IsKindOf(CLASSINFO(wxDialog)) &&
!focusWindow->IsKindOf(CLASSINFO(wxFrame)))
focusWindow = focusWindow->GetParent();
if (focusWindow)
parent = focusWindow;
}
return parent;
}
// Prompts user to open a file, using file specs in templates.
// Must extend the file selector dialog or implement own; OR
// match the extension to the template extension.
wxDocTemplate *wxDocManager::SelectDocumentPath(wxDocTemplate **templates,
#if defined(__WXMSW__) || defined(__WXGTK__) || defined(__WXMAC__)
int noTemplates,
#else
int WXUNUSED(noTemplates),
#endif
wxString& path,
long WXUNUSED(flags),
bool WXUNUSED(save))
{
// We can only have multiple filters in Windows and GTK
#if defined(__WXMSW__) || defined(__WXGTK__) || defined(__WXMAC__)
wxString descrBuf;
int i;
for (i = 0; i < noTemplates; i++)
{
if (templates[i]->IsVisible())
{
// add a '|' to separate this filter from the previous one
if ( !descrBuf.empty() )
descrBuf << wxT('|');
descrBuf << templates[i]->GetDescription()
<< wxT(" (") << templates[i]->GetFileFilter() << wxT(") |")
<< templates[i]->GetFileFilter();
}
}
#else
wxString descrBuf = wxT("*.*");
#endif
int FilterIndex = -1;
wxWindow* parent = wxFindSuitableParent();
wxString pathTmp = wxFileSelectorEx(_("Select a file"),
m_lastDirectory,
wxEmptyString,
&FilterIndex,
descrBuf,
0,
parent);
wxDocTemplate *theTemplate = (wxDocTemplate *)NULL;
if (!pathTmp.empty())
{
if (!wxFileExists(pathTmp))
{
wxString msgTitle;
if (!wxTheApp->GetAppName().empty())
msgTitle = wxTheApp->GetAppName();
else
msgTitle = wxString(_("File error"));
(void)wxMessageBox(_("Sorry, could not open this file."), msgTitle, wxOK | wxICON_EXCLAMATION,
parent);
path = wxEmptyString;
return (wxDocTemplate *) NULL;
}
m_lastDirectory = wxPathOnly(pathTmp);
path = pathTmp;
// first choose the template using the extension, if this fails (i.e.
// wxFileSelectorEx() didn't fill it), then use the path
if ( FilterIndex != -1 )
theTemplate = templates[FilterIndex];
if ( !theTemplate )
theTemplate = FindTemplateForPath(path);
if ( !theTemplate )
{
// Since we do not add files with non-default extensions to the FileHistory this
// can only happen if the application changes the allowed templates in runtime.
(void)wxMessageBox(_("Sorry, the format for this file is unknown."),
_("Open File"),
wxOK | wxICON_EXCLAMATION, wxFindSuitableParent());
}
}
else
{
path = wxEmptyString;
}
return theTemplate;
}
wxDocTemplate *wxDocManager::SelectDocumentType(wxDocTemplate **templates,
int noTemplates, bool sort)
{
wxArrayString strings;
wxDocTemplate **data = new wxDocTemplate *[noTemplates];
int i;
int n = 0;
for (i = 0; i < noTemplates; i++)
{
if (templates[i]->IsVisible())
{
int j;
bool want = true;
for (j = 0; j < n; j++)
{
//filter out NOT unique documents + view combinations
if ( templates[i]->m_docTypeName == data[j]->m_docTypeName &&
templates[i]->m_viewTypeName == data[j]->m_viewTypeName
)
want = false;
}
if ( want )
{
strings.Add(templates[i]->m_description);
data[n] = templates[i];
n ++;
}
}
} // for
if (sort)
{
strings.Sort(); // ascending sort
// Yes, this will be slow, but template lists
// are typically short.
int j;
n = strings.Count();
for (i = 0; i < n; i++)
{
for (j = 0; j < noTemplates; j++)
{
if (strings[i] == templates[j]->m_description)
data[i] = templates[j];
}
}
}
wxDocTemplate *theTemplate;
switch ( n )
{
case 0:
// no visible templates, hence nothing to choose from
theTemplate = NULL;
break;
case 1:
// don't propose the user to choose if he heas no choice
theTemplate = data[0];
break;
default:
// propose the user to choose one of several
theTemplate = (wxDocTemplate *)wxGetSingleChoiceData
(
_("Select a document template"),
_("Templates"),
strings,
(void **)data,
wxFindSuitableParent()
);
}
delete[] data;
return theTemplate;
}
wxDocTemplate *wxDocManager::SelectViewType(wxDocTemplate **templates,
int noTemplates, bool sort)
{
wxArrayString strings;
wxDocTemplate **data = new wxDocTemplate *[noTemplates];
int i;
int n = 0;
for (i = 0; i < noTemplates; i++)
{
wxDocTemplate *templ = templates[i];
if ( templ->IsVisible() && !templ->GetViewName().empty() )
{
int j;
bool want = true;
for (j = 0; j < n; j++)
{
//filter out NOT unique views
if ( templates[i]->m_viewTypeName == data[j]->m_viewTypeName )
want = false;
}
if ( want )
{
strings.Add(templ->m_viewTypeName);
data[n] = templ;
n ++;
}
}
}
if (sort)
{
strings.Sort(); // ascending sort
// Yes, this will be slow, but template lists
// are typically short.
int j;
n = strings.Count();
for (i = 0; i < n; i++)
{
for (j = 0; j < noTemplates; j++)
{
if (strings[i] == templates[j]->m_viewTypeName)
data[i] = templates[j];
}
}
}
wxDocTemplate *theTemplate;
// the same logic as above
switch ( n )
{
case 0:
theTemplate = (wxDocTemplate *)NULL;
break;
case 1:
theTemplate = data[0];
break;
default:
theTemplate = (wxDocTemplate *)wxGetSingleChoiceData
(
_("Select a document view"),
_("Views"),
strings,
(void **)data,
wxFindSuitableParent()
);
}
delete[] data;
return theTemplate;
}
void wxDocManager::AssociateTemplate(wxDocTemplate *temp)
{
if (!m_templates.Member(temp))
m_templates.Append(temp);
}
void wxDocManager::DisassociateTemplate(wxDocTemplate *temp)
{
m_templates.DeleteObject(temp);
}
// Add and remove a document from the manager's list
void wxDocManager::AddDocument(wxDocument *doc)
{
if (!m_docs.Member(doc))
m_docs.Append(doc);
}
void wxDocManager::RemoveDocument(wxDocument *doc)
{
m_docs.DeleteObject(doc);
}
// Views or windows should inform the document manager
// when a view is going in or out of focus
void wxDocManager::ActivateView(wxView *view, bool activate)
{
if ( activate )
{
m_currentView = view;
}
else // deactivate
{
if ( m_currentView == view )
{
// don't keep stale pointer
m_currentView = (wxView *) NULL;
}
}
}
// ----------------------------------------------------------------------------
// Default document child frame
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxDocChildFrame, wxFrame)
EVT_ACTIVATE(wxDocChildFrame::OnActivate)
EVT_CLOSE(wxDocChildFrame::OnCloseWindow)
END_EVENT_TABLE()
wxDocChildFrame::wxDocChildFrame(wxDocument *doc,
wxView *view,
wxFrame *frame,
wxWindowID id,
const wxString& title,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
: wxFrame(frame, id, title, pos, size, style, name)
{
m_childDocument = doc;
m_childView = view;
if (view)
view->SetFrame(this);
}
// Extend event processing to search the view's event table
bool wxDocChildFrame::ProcessEvent(wxEvent& event)
{
if (m_childView)
m_childView->Activate(true);
if ( !m_childView || ! m_childView->ProcessEvent(event) )
{
// Only hand up to the parent if it's a menu command
if (!event.IsKindOf(CLASSINFO(wxCommandEvent)) || !GetParent() || !GetParent()->ProcessEvent(event))
return wxEvtHandler::ProcessEvent(event);
else
return true;
}
else
return true;
}
void wxDocChildFrame::OnActivate(wxActivateEvent& event)
{
wxFrame::OnActivate(event);
if (m_childView)
m_childView->Activate(event.GetActive());
}
void wxDocChildFrame::OnCloseWindow(wxCloseEvent& event)
{
if (m_childView)
{
bool ans = event.CanVeto()
? m_childView->Close(false) // false means don't delete associated window
: true; // Must delete.
if (ans)
{
m_childView->Activate(false);
delete m_childView;
m_childView = (wxView *) NULL;
m_childDocument = (wxDocument *) NULL;
this->Destroy();
}
else
event.Veto();
}
else
event.Veto();
}
// ----------------------------------------------------------------------------
// Default parent frame
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxDocParentFrame, wxFrame)
EVT_MENU(wxID_EXIT, wxDocParentFrame::OnExit)
EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, wxDocParentFrame::OnMRUFile)
EVT_CLOSE(wxDocParentFrame::OnCloseWindow)
END_EVENT_TABLE()
wxDocParentFrame::wxDocParentFrame(wxDocManager *manager,
wxFrame *frame,
wxWindowID id,
const wxString& title,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
: wxFrame(frame, id, title, pos, size, style, name)
{
m_docManager = manager;
}
void wxDocParentFrame::OnExit(wxCommandEvent& WXUNUSED(event))
{
Close();
}
void wxDocParentFrame::OnMRUFile(wxCommandEvent& event)
{
int n = event.GetId() - wxID_FILE1; // the index in MRU list
wxString filename(m_docManager->GetHistoryFile(n));
if ( !filename.empty() )
{
// verify that the file exists before doing anything else
if ( wxFile::Exists(filename) )
{
// try to open it
if (!m_docManager->CreateDocument(filename, wxDOC_SILENT))
{
// remove the file from the MRU list. The user should already be notified.
m_docManager->RemoveFileFromHistory(n);
wxLogError(_("The file '%s' couldn't be opened.\nIt has been removed from the most recently used files list."),
filename.c_str());
}
}
else
{
// remove the bogus filename from the MRU list and notify the user
// about it
m_docManager->RemoveFileFromHistory(n);
wxLogError(_("The file '%s' doesn't exist and couldn't be opened.\nIt has been removed from the most recently used files list."),
filename.c_str());
}
}
}
// Extend event processing to search the view's event table
bool wxDocParentFrame::ProcessEvent(wxEvent& event)
{
// Try the document manager, then do default processing
if (!m_docManager || !m_docManager->ProcessEvent(event))
return wxEvtHandler::ProcessEvent(event);
else
return true;
}
// Define the behaviour for the frame closing
// - must delete all frames except for the main one.
void wxDocParentFrame::OnCloseWindow(wxCloseEvent& event)
{
if (m_docManager->Clear(!event.CanVeto()))
{
this->Destroy();
}
else
event.Veto();
}
#if wxUSE_PRINTING_ARCHITECTURE
wxDocPrintout::wxDocPrintout(wxView *view, const wxString& title)
: wxPrintout(title)
{
m_printoutView = view;
}
bool wxDocPrintout::OnPrintPage(int WXUNUSED(page))
{
wxDC *dc = GetDC();
// Get the logical pixels per inch of screen and printer
int ppiScreenX, ppiScreenY;
GetPPIScreen(&ppiScreenX, &ppiScreenY);
wxUnusedVar(ppiScreenY);
int ppiPrinterX, ppiPrinterY;
GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
wxUnusedVar(ppiPrinterY);
// This scales the DC so that the printout roughly represents the
// the screen scaling. The text point size _should_ be the right size
// but in fact is too small for some reason. This is a detail that will
// need to be addressed at some point but can be fudged for the
// moment.
float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
// Now we have to check in case our real page size is reduced
// (e.g. because we're drawing to a print preview memory DC)
int pageWidth, pageHeight;
int w, h;
dc->GetSize(&w, &h);
GetPageSizePixels(&pageWidth, &pageHeight);
wxUnusedVar(pageHeight);
// If printer pageWidth == current DC width, then this doesn't
// change. But w might be the preview bitmap width, so scale down.
float overallScale = scale * (float)(w/(float)pageWidth);
dc->SetUserScale(overallScale, overallScale);
if (m_printoutView)
{
m_printoutView->OnDraw(dc);
}
return true;
}
bool wxDocPrintout::HasPage(int pageNum)
{
return (pageNum == 1);
}
bool wxDocPrintout::OnBeginDocument(int startPage, int endPage)
{
if (!wxPrintout::OnBeginDocument(startPage, endPage))
return false;
return true;
}
void wxDocPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
{
*minPage = 1;
*maxPage = 1;
*selPageFrom = 1;
*selPageTo = 1;
}
#endif // wxUSE_PRINTING_ARCHITECTURE
// ----------------------------------------------------------------------------
// File history processor
// ----------------------------------------------------------------------------
static inline wxChar* MYcopystring(const wxString& s)
{
wxChar* copy = new wxChar[s.length() + 1];
return wxStrcpy(copy, s.c_str());
}
static inline wxChar* MYcopystring(const wxChar* s)
{
wxChar* copy = new wxChar[wxStrlen(s) + 1];
return wxStrcpy(copy, s);
}
wxFileHistory::wxFileHistory(size_t maxFiles, wxWindowID idBase)
{
m_fileMaxFiles = maxFiles;
m_idBase = idBase;
m_fileHistoryN = 0;
m_fileHistory = new wxChar *[m_fileMaxFiles];
}
wxFileHistory::~wxFileHistory()
{
size_t i;
for (i = 0; i < m_fileHistoryN; i++)
delete[] m_fileHistory[i];
delete[] m_fileHistory;
}
// File history management
void wxFileHistory::AddFileToHistory(const wxString& file)
{
size_t i;
// Check we don't already have this file
for (i = 0; i < m_fileHistoryN; i++)
{
#if defined( __WXMSW__ ) // Add any other OSes with case insensitive file names
wxString testString;
if ( m_fileHistory[i] )
testString = m_fileHistory[i];
if ( m_fileHistory[i] && ( file.Lower() == testString.Lower() ) )
#else
if ( m_fileHistory[i] && ( file == m_fileHistory[i] ) )
#endif
{
// we do have it, move it to the top of the history
RemoveFileFromHistory (i);
AddFileToHistory (file);
return;
}
}
// if we already have a full history, delete the one at the end
if ( m_fileMaxFiles == m_fileHistoryN )
{
RemoveFileFromHistory (m_fileHistoryN - 1);
AddFileToHistory (file);
return;
}
// Add to the project file history:
// Move existing files (if any) down so we can insert file at beginning.
if (m_fileHistoryN < m_fileMaxFiles)
{
wxList::compatibility_iterator node = m_fileMenus.GetFirst();
while (node)
{
wxMenu* menu = (wxMenu*) node->GetData();
if ( m_fileHistoryN == 0 && menu->GetMenuItemCount() )
{
menu->AppendSeparator();
}
menu->Append(m_idBase+m_fileHistoryN, _("[EMPTY]"));
node = node->GetNext();
}
m_fileHistoryN ++;
}
// Shuffle filenames down
for (i = (m_fileHistoryN-1); i > 0; i--)
{
m_fileHistory[i] = m_fileHistory[i-1];
}
m_fileHistory[0] = MYcopystring(file);
// this is the directory of the last opened file
wxString pathCurrent;
wxSplitPath( m_fileHistory[0], &pathCurrent, NULL, NULL );
for (i = 0; i < m_fileHistoryN; i++)
{
if ( m_fileHistory[i] )
{
// if in same directory just show the filename; otherwise the full
// path
wxString pathInMenu, path, filename, ext;
wxSplitPath( m_fileHistory[i], &path, &filename, &ext );
if ( path == pathCurrent )
{
pathInMenu = filename;
if ( !ext.empty() )
pathInMenu = pathInMenu + wxFILE_SEP_EXT + ext;
}
else
{
// absolute path; could also set relative path
pathInMenu = m_fileHistory[i];
}
wxString buf;
buf.Printf(s_MRUEntryFormat, i + 1, pathInMenu.c_str());
wxList::compatibility_iterator node = m_fileMenus.GetFirst();
while (node)
{
wxMenu* menu = (wxMenu*) node->GetData();
menu->SetLabel(m_idBase + i, buf);
node = node->GetNext();
}
}
}
}
void wxFileHistory::RemoveFileFromHistory(size_t i)
{
wxCHECK_RET( i < m_fileHistoryN,
wxT("invalid index in wxFileHistory::RemoveFileFromHistory") );
// delete the element from the array (could use memmove() too...)
delete [] m_fileHistory[i];
size_t j;
for ( j = i; j < m_fileHistoryN - 1; j++ )
{
m_fileHistory[j] = m_fileHistory[j + 1];
}
wxList::compatibility_iterator node = m_fileMenus.GetFirst();
while ( node )
{
wxMenu* menu = (wxMenu*) node->GetData();
// shuffle filenames up
wxString buf;
for ( j = i; j < m_fileHistoryN - 1; j++ )
{
buf.Printf(s_MRUEntryFormat, j + 1, m_fileHistory[j]);
menu->SetLabel(m_idBase + j, buf);
}
node = node->GetNext();
// delete the last menu item which is unused now
wxWindowID lastItemId = m_idBase + m_fileHistoryN - 1;
if (menu->FindItem(lastItemId))
{
menu->Delete(lastItemId);
}
// delete the last separator too if no more files are left
if ( m_fileHistoryN == 1 )
{
wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetLast();
if ( node )
{
wxMenuItem *menuItem = node->GetData();
if ( menuItem->IsSeparator() )
{
menu->Delete(menuItem);
}
//else: should we search backwards for the last separator?
}
//else: menu is empty somehow
}
}
m_fileHistoryN--;
}
wxString wxFileHistory::GetHistoryFile(size_t i) const
{
wxString s;
if ( i < m_fileHistoryN )
{
s = m_fileHistory[i];
}
else
{
wxFAIL_MSG( wxT("bad index in wxFileHistory::GetHistoryFile") );
}
return s;
}
void wxFileHistory::UseMenu(wxMenu *menu)
{
if (!m_fileMenus.Member(menu))
m_fileMenus.Append(menu);
}
void wxFileHistory::RemoveMenu(wxMenu *menu)
{
m_fileMenus.DeleteObject(menu);
}
#if wxUSE_CONFIG
void wxFileHistory::Load(wxConfigBase& config)
{
m_fileHistoryN = 0;
wxString buf;
buf.Printf(wxT("file%d"), (int)m_fileHistoryN+1);
wxString historyFile;
while ((m_fileHistoryN < m_fileMaxFiles) && config.Read(buf, &historyFile) && (!historyFile.empty()))
{
m_fileHistory[m_fileHistoryN] = MYcopystring((const wxChar*) historyFile);
m_fileHistoryN ++;
buf.Printf(wxT("file%d"), (int)m_fileHistoryN+1);
historyFile = wxEmptyString;
}
AddFilesToMenu();
}
void wxFileHistory::Save(wxConfigBase& config)
{
size_t i;
for (i = 0; i < m_fileMaxFiles; i++)
{
wxString buf;
buf.Printf(wxT("file%d"), (int)i+1);
if (i < m_fileHistoryN)
config.Write(buf, wxString(m_fileHistory[i]));
else
config.Write(buf, wxEmptyString);
}
}
#endif // wxUSE_CONFIG
void wxFileHistory::AddFilesToMenu()
{
if (m_fileHistoryN > 0)
{
wxList::compatibility_iterator node = m_fileMenus.GetFirst();
while (node)
{
wxMenu* menu = (wxMenu*) node->GetData();
if (menu->GetMenuItemCount())
{
menu->AppendSeparator();
}
size_t i;
for (i = 0; i < m_fileHistoryN; i++)
{
if (m_fileHistory[i])
{
wxString buf;
buf.Printf(s_MRUEntryFormat, i+1, m_fileHistory[i]);
menu->Append(m_idBase+i, buf);
}
}
node = node->GetNext();
}
}
}
void wxFileHistory::AddFilesToMenu(wxMenu* menu)
{
if (m_fileHistoryN > 0)
{
if (menu->GetMenuItemCount())
{
menu->AppendSeparator();
}
size_t i;
for (i = 0; i < m_fileHistoryN; i++)
{
if (m_fileHistory[i])
{
wxString buf;
buf.Printf(s_MRUEntryFormat, i+1, m_fileHistory[i]);
menu->Append(m_idBase+i, buf);
}
}
}
}
// ----------------------------------------------------------------------------
// Permits compatibility with existing file formats and functions that
// manipulate files directly
// ----------------------------------------------------------------------------
#if wxUSE_STD_IOSTREAM
bool wxTransferFileToStream(const wxString& filename, wxSTD ostream& stream)
{
wxFFile file(filename, _T("rb"));
if ( !file.IsOpened() )
return false;
char buf[4096];
size_t nRead;
do
{
nRead = file.Read(buf, WXSIZEOF(buf));
if ( file.Error() )
return false;
stream.write(buf, nRead);
if ( !stream )
return false;
}
while ( !file.Eof() );
return true;
}
bool wxTransferStreamToFile(wxSTD istream& stream, const wxString& filename)
{
wxFFile file(filename, _T("wb"));
if ( !file.IsOpened() )
return false;
char buf[4096];
do
{
stream.read(buf, WXSIZEOF(buf));
if ( !stream.bad() ) // fail may be set on EOF, don't use operator!()
{
if ( !file.Write(buf, stream.gcount()) )
return false;
}
}
while ( !stream.eof() );
return true;
}
#else // !wxUSE_STD_IOSTREAM
bool wxTransferFileToStream(const wxString& filename, wxOutputStream& stream)
{
wxFFile file(filename, _T("rb"));
if ( !file.IsOpened() )
return false;
char buf[4096];
size_t nRead;
do
{
nRead = file.Read(buf, WXSIZEOF(buf));
if ( file.Error() )
return false;
stream.Write(buf, nRead);
if ( !stream )
return false;
}
while ( !file.Eof() );
return true;
}
bool wxTransferStreamToFile(wxInputStream& stream, const wxString& filename)
{
wxFFile file(filename, _T("wb"));
if ( !file.IsOpened() )
return false;
char buf[4096];
do
{
stream.Read(buf, WXSIZEOF(buf));
const size_t nRead = stream.LastRead();
if ( !nRead || !file.Write(buf, nRead) )
return false;
}
while ( !stream.Eof() );
return true;
}
#endif // wxUSE_STD_IOSTREAM/!wxUSE_STD_IOSTREAM
#endif // wxUSE_DOC_VIEW_ARCHITECTURE