+CPythonDocTemp::CPythonDocTemp()
+{
+}
+
+template
+CPythonDocTemp::~CPythonDocTemp()
+{
+ Python_delete_assoc( this );
+}
+
+template
+BOOL CPythonDocTemp::OnCmdMsg (UINT nID, int nCode,
+ void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo)
+{
+ // yield to Python first
+ if (Python_OnCmdMsg (this, nID, nCode, pExtra, pHandlerInfo))
+ return TRUE;
+ else
+ return P::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+}
+
+template
+BOOL CPythonDocTemp::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
+{
+ // @pyvirtual int|PyCDocument|DoSave|Called by the MFC architecture to save a document.
+ // @pyparm string|fileName||The name of the file being saved.
+ // @pyparm int|bReplace||TRUE if the file should be replaced.
+ // @xref
+ // @comm If a handler is defined for this function, it must call the
+ // base class method.
+ CVirtualHelper helper( "DoSave", this );
+ if (helper.HaveHandler()) {
+ if (!helper.call(lpszPathName, bReplace))
+ return FALSE;
+ int ret;
+ // @rdesc TRUE if the document could be saved, else FALSE.
+ if (helper.retval(ret))
+ return ret;
+ return FALSE;
+ }
+ return P::DoSave(lpszPathName, bReplace);
+}
+
+template
+BOOL CPythonDocTemp::DoFileSave()
+{
+ CVirtualHelper helper( "DoFileSave", this );
+ // @pyvirtual int|PyCDocument|DoFileSave|Called by the MFC architecture.
+ // @comm If a handler is defined for this function, it must call the
+ // base class method.
+ // @xref
+ if (helper.HaveHandler()) {
+ if (!helper.call())
+ return FALSE;
+ int ret;
+ // @rdesc TRUE if the document could be saved, else FALSE.
+ if (helper.retval(ret))
+ return ret;
+ return FALSE;
+ }
+ return P::DoFileSave();
+}
+
+template
+BOOL CPythonDocTemp::OnSaveDocument(const char *fileName)
+{
+ // @pyvirtual int|PyCDocument|OnSaveDocument|Called by the MFC architecture.
+ // @pyparm string|fileName||The name of the file being saved.
+ // @xref
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+ CVirtualHelper helper( "OnSaveDocument", this );
+ if (helper.call((char *)fileName)) {
+ int ret;
+ // @rdesc TRUE if the document could be saved, else FALSE.
+ if (helper.retval(ret))
+ return ret;
+ return FALSE;
+ }
+ return FALSE;
+// return CDocument::OnSaveDocument(fileName);
+}
+
+template
+BOOL CPythonDocTemp::OnOpenDocument(const char *fileName)
+{
+ // @pyvirtual int|PyCDocument|OnOpenDocument|Called by the MFC architecture.
+ // @xref
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+ CVirtualHelper helper( "OnOpenDocument", this );
+ if (!helper.HaveHandler()) {
+ PyErr_SetString(ui_module_error,"PyCDocument::OnOpenDocument handler does not exist.");
+ gui_print_error();
+ return FALSE;
+ }
+ // @pyparm string|fileName||The name of the file being opened.
+ if (helper.call((char *)fileName)) {
+ int ret;
+ // @rdesc TRUE if the document could be opened, else FALSE.
+ if (helper.retval(ret))
+ return ret;
+ return FALSE;
+ }
+ return FALSE; // failed!
+}
+
+template
+BOOL CPythonDocTemp::OnNewDocument()
+{
+ // @pyvirtual int|PyCDocument|OnNewDocument|Called by the MFC architecture.
+ // @xref
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+ CVirtualHelper helper( "OnNewDocument", this );
+ if (!helper.HaveHandler()) {
+ return P::OnNewDocument();
+ }
+ // from here, it means a Python exception occurred, and this has been reported.
+ if (helper.call()) {
+ int ret;
+ // @rdesc TRUE if a new document could be created, else FALSE.
+ if (helper.retval(ret))
+ return ret;
+ else {
+ PyErr_SetString(PyExc_TypeError, "PyCDocument.OnNewDocument - bad return type.");
+ gui_print_error();
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+template
+void CPythonDocTemp::OnCloseDocument()
+{
+ // @pyvirtual |PyCDocument|OnCloseDocument|Called by the MFC architecture.
+ // @xref
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+ CVirtualHelper helper( "OnCloseDocument", this );
+ if (helper.HaveHandler()) {
+ helper.call();
+ } else
+ P::OnCloseDocument();
+}
+
+template
+void CPythonDocTemp::PreCloseFrame( CFrameWnd *pWnd )
+{
+ // @pyvirtual |PyCDocument|PreCloseFrame|Called before the frame window is closed.
+ CVirtualHelper helper( "PreCloseFrame", this );
+ helper.call(pWnd);
+ P::PreCloseFrame(pWnd);
+ // @comm The MFC base implementation is always called after the Python handler returns.
+}
+
+template
+void CPythonDocTemp::DeleteContents()
+{
+ // @pyvirtual |PyCDocument|DeleteContents|Called by the MFC architecture when a document is newly created or closed.
+ // @xref
+ CVirtualHelper helper( "DeleteContents", this );
+ if (!helper.call())
+ P::DeleteContents();
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+}
+template
+BOOL CPythonDocTemp::SaveModified()
+{
+ // @pyvirtual int|PyCDocument|SaveModified|Called by the MFC architecture when a document is closed.
+ // @xref
+ // @comm If a handler is defined for this function, the base (MFC) function will not
+ // be called. If necessary, the handler must call this function explicitely.
+ CVirtualHelper helper( "SaveModified", this );
+ if (!helper.HaveHandler())
+ return P::SaveModified();
+ if (helper.call()) {
+ int ret;
+ // @rdesc The handler should return TRUE if it is safe to continue and close
+ // the document; 0 if the document should not be closed.
+ if (helper.retval(ret))
+ return ret;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonDocTemp serialization
+
+template
+void CPythonDocTemp::Serialize(CArchive& ar)
+{
+ if (ar.IsStoring())
+ {
+ // TODO: add storing code here
+ }
+ else
+ {
+ // TODO: add loading code here
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonDocTemp diagnostics
+
+#ifdef _DEBUG
+template
+void CPythonDocTemp::AssertValid() const
+{
+ P::AssertValid();
+}
+
+template
+void CPythonDocTemp::Dump(CDumpContext& dc) const
+{
+ P::Dump(dc);
+}
+
+#endif //_DEBUG
+
+class CPythonDoc : public CPythonDocTemp
+{
+ DECLARE_DYNCREATE(CPythonDoc);
+protected:
+ //{{AFX_MSG(CPythonDoc)
+ afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // __filename_h__
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Pythonwin/pythonframe.h b/Pythonwin/pythonframe.h
new file mode 100644
index 0000000000..ca41c40603
--- /dev/null
+++ b/Pythonwin/pythonframe.h
@@ -0,0 +1,22 @@
+// pythonframe.h : header file
+//
+#ifndef __PYTHONFRAME_H__
+#define __PYTHONFRAME_H__
+
+// With the new template mechanism, the Python frame classes
+// become (nearly) 1 liners :-)
+
+class CPythonFrameWnd : public CPythonWndFramework {
+ DECLARE_DYNAMIC(CPythonFrameWnd);
+};
+
+class CPythonMDIChildWnd : public CPythonFrameFramework {
+ DECLARE_DYNAMIC(CPythonMDIChildWnd);
+};
+
+class CPythonMDIFrameWnd : public CPythonFrameFramework {
+ DECLARE_DYNAMIC(CPythonMDIFrameWnd);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // __filename_h__
diff --git a/Pythonwin/pythonppage.cpp b/Pythonwin/pythonppage.cpp
new file mode 100644
index 0000000000..4b1489d81c
--- /dev/null
+++ b/Pythonwin/pythonppage.cpp
@@ -0,0 +1,96 @@
+// pythonppage.cpp : implementation file
+//
+// Note that this source file contains embedded documentation.
+// This documentation consists of marked up text inside the
+// C comments, and is prefixed with an '@' symbol. The source
+// files are processed by a tool called "autoduck" which
+// generates Windows .hlp files.
+// @doc
+
+#include "stdafx.h"
+#include "pythonwin.h"
+#include "win32ui.h"
+
+#include "pythonppage.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonPropertyPage property page
+
+IMPLEMENT_DYNAMIC(CPythonPropertyPage, CPropertyPage)
+
+
+CPythonPropertyPage::CPythonPropertyPage(UINT id, UINT caption) :
+ CPythonPropertyPageFramework(id, caption)
+{
+ CommonConstruct();
+}
+
+CPythonPropertyPage::CPythonPropertyPage(LPCTSTR id, UINT caption) :
+ CPythonPropertyPageFramework(id, caption)
+{
+ CommonConstruct();
+}
+
+void CPythonPropertyPage::CommonConstruct()
+{
+// hTemplate = 0;
+ hSaved = 0;
+ //{{AFX_DATA_INIT(CPythonPropertyPage)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+}
+
+CPythonPropertyPage::~CPythonPropertyPage()
+{
+ if (m_psp.pResource)
+ {
+ GlobalUnlock(hSaved);
+ GlobalFree(hSaved);
+ }
+}
+
+void CPythonPropertyPage::PostNcDestroy()
+{
+}
+
+BOOL CPythonPropertyPage::SetTemplate(HGLOBAL tpl)
+{
+ hSaved = tpl;
+// if (m_psp.pResource!=NULL) {
+// PyErr_SetString(ui_module_error, "The template can only be assigned once");
+// return FALSE;
+// }
+ m_psp.dwFlags |= PSP_DLGINDIRECT;
+
+ m_psp.pResource = (const DLGTEMPLATE *)GlobalLock(tpl);
+
+ // Set the caption if not already set
+ if (m_strCaption.GetLength() == 0)
+ {
+ // use a LPWSTR because all resource are UNICODE
+ LPCWSTR p = (LPCWSTR)((BYTE*)m_psp.pResource + sizeof(DLGTEMPLATE));
+ // skip menu stuff
+ p+= (*p == 0xffff) ? 2 : wcslen(p)+1;
+ // skip window class stuff
+ p+= (*p == 0xffff) ? 2 : wcslen(p)+1;
+ // we're now at the caption
+ m_strCaption = p;
+ }
+ return TRUE; //CreatePage();
+}
+
+#ifdef _DEBUG
+void CPythonPropertyPage::Dump( CDumpContext &dc ) const
+{
+ CPropertyPage::Dump(dc);
+ DumpAssocPyObject(dc, (void *)this);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonPropertyPage message handlers
diff --git a/Pythonwin/pythonppage.h b/Pythonwin/pythonppage.h
new file mode 100644
index 0000000000..c3a352b270
--- /dev/null
+++ b/Pythonwin/pythonppage.h
@@ -0,0 +1,37 @@
+// pythonppage.h : header file
+//
+#ifndef __PYTHONPPAGE_H__
+#define __PYTHONPPAGE_H__
+
+// bit of a hack
+#ifdef _DEBUG
+ BOOL AFXAPI _AfxCheckDialogTemplate(LPCTSTR lpszResource,
+ BOOL bInvisibleChild);
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonPropertyPage dialog
+
+class CPythonPropertyPage : public CPythonPropertyPageFramework
+{
+ DECLARE_DYNAMIC(CPythonPropertyPage)
+
+protected:
+ // Support for indirect creation
+ HGLOBAL hSaved;
+
+// Construction
+public:
+ CPythonPropertyPage(UINT id, UINT caption = 0);
+ CPythonPropertyPage(LPCTSTR id, UINT caption = 0);
+ ~CPythonPropertyPage();
+ virtual void PostNcDestroy();
+
+ BOOL SetTemplate(HGLOBAL tpl);
+private:
+ void CommonConstruct();
+#ifdef _DEBUG
+ virtual void Dump( CDumpContext &dc ) const;
+#endif
+};
+#endif // __filename_h__
diff --git a/Pythonwin/pythonpsheet.cpp b/Pythonwin/pythonpsheet.cpp
new file mode 100644
index 0000000000..f383aab3df
--- /dev/null
+++ b/Pythonwin/pythonpsheet.cpp
@@ -0,0 +1,80 @@
+// pythonpsheet.cpp : implementation file
+//
+// Note that this source file contains embedded documentation.
+// This documentation consists of marked up text inside the
+// C comments, and is prefixed with an '@' symbol. The source
+// files are processed by a tool called "autoduck" which
+// generates Windows .hlp files.
+// @doc
+
+#include "stdafx.h"
+#include "pythonpsheet.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+#if _MFC_VER < 0x0600
+// MSVC V 5.1 and certain version of the IE4 SDK cant agree on object sizes!
+
+// God damn - inlines and DLL dont agree on object sizes!!!
+# if defined(PROPSHEETHEADERA_V1_SIZE)
+# if !defined(_WIN32_IE)
+# error "Please update the IE4 SDK to a newer version"
+# endif
+# if _WIN32_IE > 0x0300
+# error "Please recompile with _WIN32_IE set to 0x0300"
+# endif
+# endif // PROPSHEETHEADERA_V1_SIZE
+
+#endif // _MFC_VER
+/////////////////////////////////////////////////////////////////////////////
+// CPythonPropertySheet
+
+IMPLEMENT_DYNAMIC(CPythonPropertySheet, CPropertySheet)
+
+/*CPythonPropertySheet::CPythonPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
+ :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
+{
+}
+
+CPythonPropertySheet::CPythonPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
+ :CPropertySheet(pszCaption, pParentWnd, iSelectPage)
+{
+}
+*/
+CPythonPropertySheet::~CPythonPropertySheet()
+{
+}
+
+void CPythonPropertySheet::PostNcDestroy(void)
+{
+ // Loop over all pages, ensuring no Python association exists.
+ // This is because some pages may never have had windows, and
+ // therefore will not have the WM_DESTROY handling done.
+ int numPages = GetPageCount();
+ for (int i = 0; i < numPages; i++)
+ {
+ CPropertyPage* pPage = GetPage(i);
+ delete pPage;
+ }
+
+ Python_delete_assoc(this);
+ delete this;
+}
+
+BEGIN_MESSAGE_MAP(CPythonPropertySheet, CPropertySheet)
+ //{{AFX_MSG_MAP(CPythonPropertySheet)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+#ifdef _DEBUG
+void CPythonPropertySheet::Dump( CDumpContext &dc ) const
+{
+ CPropertySheet::Dump(dc);
+ DumpAssocPyObject(dc, (void *)this);
+}
+#endif
+
diff --git a/Pythonwin/pythonpsheet.h b/Pythonwin/pythonpsheet.h
new file mode 100644
index 0000000000..b531d89049
--- /dev/null
+++ b/Pythonwin/pythonpsheet.h
@@ -0,0 +1,34 @@
+// pythonpsheet.h : header file
+//
+#ifndef __PYTHONPSHEET_H__
+#define __PYTHONPSHEET_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonPropertySheet
+
+class CPythonPropertySheet : public CPythonPropertySheetFramework
+{
+ DECLARE_DYNAMIC(CPythonPropertySheet)
+
+// Construction
+public:
+ CPythonPropertySheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0) :
+ CPythonPropertySheetFramework(nIDCaption, pParentWnd, iSelectPage) {;}
+ CPythonPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0) :
+ CPythonPropertySheetFramework(pszCaption, pParentWnd, iSelectPage) {;}
+
+ virtual ~CPythonPropertySheet();
+ virtual void PostNcDestroy(void);
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CPythonPropertySheet)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+#ifdef _DEBUG
+ virtual void Dump( CDumpContext &dc ) const;
+#endif
+};
+
+#endif // __filename_h__
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Pythonwin/pythonview.cpp b/Pythonwin/pythonview.cpp
new file mode 100644
index 0000000000..5608c027fe
--- /dev/null
+++ b/Pythonwin/pythonview.cpp
@@ -0,0 +1,174 @@
+// Pythonview.cpp - implementation of a CEditView, especially set up for
+// python support.
+//
+// Note that this source file contains embedded documentation.
+// This documentation consists of marked up text inside the
+// C comments, and is prefixed with an '@' symbol. The source
+// files are processed by a tool called "autoduck" which
+// generates Windows .hlp files.
+// @doc
+
+#include "stdafx.h"
+#include "pythonwin.h"
+#include "pythonview.h"
+#include "win32ui.h"
+#include "win32dc.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNAMIC(CPythonListView, CListView)
+IMPLEMENT_DYNAMIC(CPythonTreeView, CTreeView)
+IMPLEMENT_DYNAMIC(CPythonView, CScrollView);
+IMPLEMENT_DYNAMIC(CPythonEditView, CEditView);
+IMPLEMENT_DYNAMIC(CPythonFormView, CFormView);
+IMPLEMENT_DYNAMIC(CPythonCtrlView, CCtrlView);
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonView
+
+void CPythonViewImpl::OnPrepareDC (CDC *pDC, CPrintInfo *pInfo)
+{
+ // @pyvirtual |PyCScrollView|OnPrepareDC|Called to prepare the device context for a view.
+ // @xref
+ if (m_nMapMode == 0) {
+ // base class will ASSERT
+ CEnterLeavePython _celp;
+ PyErr_SetString(ui_module_error, "Must call SetScrollSizes() or SetScaleToFitSize() before painting scroll view.");
+ gui_print_error();
+ return;
+ }
+
+ CVirtualHelper helper ("OnPrepareDC", this);
+ helper.call (pDC, pInfo);
+ CScrollView::OnPrepareDC (pDC, pInfo);
+ // @pyparm |dc||The DC object.
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonView message handlers
+/*
+BOOL
+CPythonView::SetDynamicScrollBars (BOOL dynamic)
+{
+ BOOL old = m_bInsideUpdate;
+
+ // Prevent MFC from hiding/showing scrollbars by setting recursive
+ // protection variable.
+ if (dynamic)
+ m_bInsideUpdate = FALSE;
+ else
+ m_bInsideUpdate = TRUE;
+
+ return (old);
+}
+*/
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+// CPythonListView
+//
+CPythonListViewImpl::CPythonListViewImpl()
+{
+}
+CPythonListViewImpl::~CPythonListViewImpl()
+{
+}
+
+void CPythonListViewImpl::DrawItem( LPDRAWITEMSTRUCT lpDIS )
+{
+ CVirtualHelper helper("DrawItem", this);
+ PyObject *obData = PyWin_GetPythonObjectFromLong(lpDIS->itemData);
+ if (obData==NULL) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert the Python object");
+ gui_print_error();
+ obData = Py_None;
+ }
+
+ // Get the MFC device context
+ CDC *pDC = CDC::FromHandle(lpDIS->hDC);
+ PyObject *obDC = ui_dc_object::make(ui_dc_object::type, pDC);
+ if (obDC==NULL) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert the DC object");
+ gui_print_error();
+ obDC = Py_None;
+ }
+
+ PyObject *args = Py_BuildValue("iiiiiiO(iiii)O",
+ lpDIS->CtlType, lpDIS->CtlID, lpDIS->itemID,
+ lpDIS->itemAction, lpDIS->itemState, lpDIS->hwndItem,
+ obDC,
+ lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right, lpDIS->rcItem.bottom,
+ obData);
+ ASSERT(args);
+ if (!args) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert args - handler not called.");
+ return; // not too much we can do
+ }
+ // make the call.
+ helper.call_args(args);
+ // Cleanup.
+ Py_DECREF(args);
+ // The DC is no longer valid.
+ Python_delete_assoc(pDC);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+// CPythonTreeView
+//
+
+CPythonTreeViewImpl::CPythonTreeViewImpl()
+{
+}
+CPythonTreeViewImpl::~CPythonTreeViewImpl()
+{
+}
+
+void CPythonTreeViewImpl::DrawItem( LPDRAWITEMSTRUCT lpDIS )
+{
+ CVirtualHelper helper("DrawItem", this);
+ PyObject *obData = PyWin_GetPythonObjectFromLong(lpDIS->itemData);
+ if (obData==NULL) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert the Python object");
+ gui_print_error();
+ obData = Py_None;
+ }
+
+ // Get the MFC device context
+ CDC *pDC = CDC::FromHandle(lpDIS->hDC);
+ PyObject *obDC = ui_dc_object::make(ui_dc_object::type, pDC);
+ if (obDC==NULL) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert the DC object");
+ gui_print_error();
+ obDC = Py_None;
+ }
+
+ PyObject *args = Py_BuildValue("iiiiiiO(iiii)O",
+ lpDIS->CtlType, lpDIS->CtlID, lpDIS->itemID,
+ lpDIS->itemAction, lpDIS->itemState, lpDIS->hwndItem,
+ obDC,
+ lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right, lpDIS->rcItem.bottom,
+ obData);
+ ASSERT(args);
+ if (!args) {
+ gui_print_error();
+ PyErr_SetString(ui_module_error, "DrawItem could not convert args - handler not called.");
+ return; // not too much we can do
+ }
+ // make the call.
+ helper.call_args(args);
+ // Cleanup.
+ Py_DECREF(args);
+ // The DC is no longer valid.
+ Python_delete_assoc(pDC);
+}
+
diff --git a/Pythonwin/pythonview.h b/Pythonwin/pythonview.h
new file mode 100644
index 0000000000..ea97a4e20e
--- /dev/null
+++ b/Pythonwin/pythonview.h
@@ -0,0 +1,77 @@
+// Pythonview.h : header file
+//
+#ifndef __PYTHONVIEW_H__
+#define __PYTHONVIEW_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonViewTemp
+class CPythonViewImpl : public CScrollView
+{
+public:
+ virtual void OnPrepareDC (CDC *pDC, CPrintInfo *pInfo);
+
+};
+
+class CPythonListViewImpl : public CListView
+{
+// DECLARE_DYNCREATE(CPythonListViewImpl)
+public:
+ CPythonListViewImpl();
+ ~CPythonListViewImpl();
+// Operations
+ virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
+};
+
+class CPythonTreeViewImpl : public CTreeView
+{
+// DECLARE_DYNCREATE(CPythonTreeViewImpl)
+public:
+ CPythonTreeViewImpl();
+ ~CPythonTreeViewImpl();
+// Operations
+ virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
+};
+
+class CPythonEditView : public CPythonViewFramework< CEditView >
+{
+ DECLARE_DYNAMIC(CPythonEditView);
+};
+
+class CPythonView : public CPythonViewFramework
+{
+ DECLARE_DYNAMIC(CPythonView);
+};
+
+class CPythonListView : public CPythonViewFramework
+{
+ DECLARE_DYNAMIC(CPythonListView);
+};
+
+class CPythonTreeView : public CPythonViewFramework
+{
+ DECLARE_DYNAMIC(CPythonTreeView);
+};
+
+class CPythonFormView : public CPythonViewFramework
+{
+public:
+ CPythonFormView(UINT id) :
+ CPythonViewFramework(id) {;}
+ CPythonFormView(LPCTSTR id) :
+ CPythonViewFramework(id) {;}
+ DECLARE_DYNAMIC(CPythonFormView);
+};
+
+class CPythonCtrlView : public CPythonViewFramework
+{
+ DECLARE_DYNAMIC(CPythonCtrlView);
+public:
+ CPythonCtrlView(LPCTSTR lpszClass, DWORD dwStyle) :
+ CPythonViewFramework(lpszClass, dwStyle)
+ {;}
+};
+
+//typedef CPythonViewFramework CPythonCtrlView;
+
+#endif // __filename_h__
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Pythonwin/pythonwin.cpp b/Pythonwin/pythonwin.cpp
new file mode 100644
index 0000000000..b5bd5eb972
--- /dev/null
+++ b/Pythonwin/pythonwin.cpp
@@ -0,0 +1,137 @@
+// pythonwin.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafxpw.h"
+#include "pythonwin.h"
+#include "win32uiHostGlue.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+// CPythonWinApp
+
+BEGIN_MESSAGE_MAP(CPythonWinApp, CWinApp)
+ //{{AFX_MSG_MAP(CPythonWinApp)
+ //}}AFX_MSG_MAP
+ // Standard file based document commands
+ // Standard print setup command
+ ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonWinApp construction
+
+CPythonWinApp::CPythonWinApp()
+{
+ // Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CPythonWinApp object
+
+CPythonWinApp NEAR theApp;
+
+// The one and only Glue object.
+Win32uiHostGlue NEAR glue;
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonWinApp initialization
+BOOL CPythonWinApp::InitApplication()
+{
+ // create mem mapped file for switching instances.
+ m_pDocManager = new CDocManager();
+ if (!CWinApp::InitApplication())
+ return FALSE;
+ CString startup;
+ startup.LoadString(57346); // Grrr....
+ if (startup.GetLength()==0)
+ startup = "import pywin.framework.startup";
+
+ if (!glue.DynamicApplicationInit(startup))
+ return FALSE;
+ return TRUE;
+}
+
+BOOL CPythonWinApp::InitInstance()
+{
+ if (!glue.InitInstance())
+ return FALSE;
+ // dialog based apps dont have a message pump.
+ return m_pMainWnd && !m_pMainWnd->IsKindOf(RUNTIME_CLASS(CDialog));
+}
+int CPythonWinApp::ExitInstance()
+{
+ int rc = glue.ExitInstance();
+ CWinApp::ExitInstance();
+ return rc;
+}
+
+BOOL
+CPythonWinApp::OnCmdMsg (UINT nID, int nCode,
+ void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo)
+{
+ // yield to Python first
+ if (glue.OnCmdMsg (this, nID, nCode, pExtra, pHandlerInfo))
+ return TRUE;
+ else
+ return CWinApp::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
+}
+
+BOOL CPythonWinApp::PreTranslateMessage(MSG *pMsg)
+{
+ if (glue.PreTranslateMessage(pMsg))
+ return TRUE;
+ else
+ return CWinApp::PreTranslateMessage(pMsg);
+
+/* BOOL ret=CWinApp::PreTranslateMessage(pMsg);
+ BOOL ret2 = glue.PreTranslateMessage(pMsg);
+ return ret||ret2;
+*/
+}
+
+BOOL CPythonWinApp::OnIdle( LONG lCount )
+{
+ // call base class idle first
+ if (CWinApp::OnIdle(lCount))
+ return TRUE;
+ return glue.OnIdle(lCount);
+}
+
+CDocument *CPythonWinApp::OpenDocumentFile(LPCTSTR lpszFileName)
+{
+#if 0 // win32s no longer supported
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ GetVersionEx(&ver);
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ GetVersionEx(&ver);
+ if (ver.dwPlatformId == VER_PLATFORM_WIN32s) {
+ OutputDebugString("Win32s - Searching templates!\n");
+ POSITION posTempl = m_pDocManager->GetFirstDocTemplatePosition();
+ CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(posTempl);
+ if (pTemplate)
+ return pTemplate->OpenDocumentFile(lpszFileName);
+ else {
+ AfxMessageBox("win32s error - There is no template to use");
+ return NULL;
+ }
+ } else
+#endif
+ return CWinApp::OpenDocumentFile(lpszFileName);
+}
+
+int CPythonWinApp::Run()
+{
+ // Allow our Python app to override the run!
+ int rc = glue.Run();
+ glue.ApplicationFinalize();
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonWinApp commands
+
diff --git a/Pythonwin/pythonwin.dsp b/Pythonwin/pythonwin.dsp
new file mode 100644
index 0000000000..950a283ec0
--- /dev/null
+++ b/Pythonwin/pythonwin.dsp
@@ -0,0 +1,154 @@
+# Microsoft Developer Studio Project File - Name="pythonwin" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=pythonwin - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "pythonwin.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "pythonwin.mak" CFG="pythonwin - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "pythonwin - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "pythonwin - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/Python/Pythonwin/pythonwin", FTAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "pythonwin - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\pythonwin\Release"
+# PROP BASE Intermediate_Dir ".\pythonwin\Release"
+# PROP BASE Target_Dir ".\pythonwin"
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Build"
+# PROP Intermediate_Dir "Build\Temp\Pythonwin\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ".\pythonwin"
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafxpw.h" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0x409 /i "c:\src\python1.4\include" /d "NDEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "pythonwin - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\pythonwin\Debug"
+# PROP BASE Intermediate_Dir ".\pythonwin\Debug"
+# PROP BASE Target_Dir ".\pythonwin"
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Build"
+# PROP Intermediate_Dir "Build\Temp\Pythonwin\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ".\pythonwin"
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafxpw.h" /Fd"Build\Temp\Pythonwin\Debug\pythonwin" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0x409 /i "c:\src\python1.4\include" /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /out:"Build/pythonwin_d.exe"
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "pythonwin - Win32 Release"
+# Name "pythonwin - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\pythonwin.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\pythonwin.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\stdafxpw.cpp
+# ADD CPP /Yc"stdafxpw.h"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\pythonwin.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\stdafxpw.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win32uiHostGlue.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\ICO00002.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\IDR_MAIN.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\IDR_PYTH.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\PADDOC.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\pyc.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\pycon.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Pythonwin/pythonwin.h b/Pythonwin/pythonwin.h
new file mode 100644
index 0000000000..d54f5fc0c6
--- /dev/null
+++ b/Pythonwin/pythonwin.h
@@ -0,0 +1,46 @@
+// pythonwin.h : main header file for the PYTHONWIN application
+//
+#ifndef __PYTHONWIN_H__
+#define __PYTHONWIN_H__
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPythonWinApp:
+// See pythonwin.cpp for the implementation of this class
+//
+
+class CPythonWinApp : public CWinApp
+{
+public:
+ CPythonWinApp();
+ void SetStatusText(const char *szStatus, BOOL bForce=FALSE);
+
+protected:
+ BOOL OnCmdMsg (UINT nID, int nCode,
+ void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo);
+
+private:
+// Overrides
+ virtual CDocument *CPythonWinApp::OpenDocumentFile(LPCTSTR lpszFileName);
+ virtual BOOL PreTranslateMessage(MSG *pMsg);
+ virtual BOOL InitInstance();
+ virtual BOOL InitApplication();
+ virtual int ExitInstance();
+ virtual BOOL OnIdle( LONG );
+ virtual int Run(void);
+
+// Implementation
+// CPtrList idleHookList;
+// int myIdleCtr;
+// CString lastFile;
+public: // give access to message map.
+ //{{AFX_MSG(CPythonWinApp)
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // __filename_h__
diff --git a/Pythonwin/pythonwin.rc b/Pythonwin/pythonwin.rc
new file mode 100644
index 0000000000..1594f537a8
--- /dev/null
+++ b/Pythonwin/pythonwin.rc
@@ -0,0 +1,83 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "respw.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "respw.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+128 ICON DISCARDABLE "RES\\IDR_MAIN.ICO"
+130 ICON DISCARDABLE "RES\\PADDOC.ICO"
+129 ICON DISCARDABLE "RES\\IDR_PYTH.ICO"
+132 ICON DISCARDABLE "RES\\pycon.ico"
+134 ICON DISCARDABLE "RES\\ICO00002.ICO"
+135 ICON DISCARDABLE "res\\pyc.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDR_MAINFRAME "PythonWin"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ AFX_IDS_APP_TITLE "Python for Win32"
+ AFX_IDS_IDLEMESSAGE "Ready"
+ IDS_STARTUP_SCRIPT "import pywin.framework.startup"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
diff --git a/Pythonwin/pywin/Demos/app/basictimerapp.py b/Pythonwin/pywin/Demos/app/basictimerapp.py
new file mode 100644
index 0000000000..adf70f062d
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/basictimerapp.py
@@ -0,0 +1,221 @@
+# basictimerapp - a really simple timer application.
+# This should be run using the command line:
+# pythonwin /app demos\basictimerapp.py
+import win32ui
+import win32api
+import win32con
+import sys
+from pywin.framework import app, cmdline, dlgappcore, cmdline
+import timer
+import time
+import string
+import regsub
+
+class TimerAppDialog(dlgappcore.AppDialog):
+ softspace=1
+ def __init__(self, appName = ""):
+ dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
+ self.timerAppName = appName
+ self.argOff = 0
+ if len(self.timerAppName)==0:
+ if len(sys.argv)>1 and sys.argv[1][0]!='/':
+ self.timerAppName = sys.argv[1]
+ self.argOff = 1
+
+ def PreDoModal(self):
+# sys.stderr = sys.stdout
+ pass
+
+ def ProcessArgs(self, args):
+ for arg in args:
+ if arg=="/now":
+ self.OnOK()
+
+ def OnInitDialog(self):
+ win32ui.SetProfileFileName('pytimer.ini')
+ self.title = win32ui.GetProfileVal(self.timerAppName, "Title", "Remote System Timer")
+ self.buildTimer = win32ui.GetProfileVal(self.timerAppName, "Timer", "EachMinuteIntervaler()")
+ self.doWork = win32ui.GetProfileVal(self.timerAppName, "Work", "DoDemoWork()")
+ # replace "\n" with real \n.
+ self.doWork = regsub.gsub('\\\\n','\n', self.doWork)
+ dlgappcore.AppDialog.OnInitDialog(self)
+
+ self.SetWindowText(self.title)
+ self.prompt1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
+ self.prompt2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
+ self.prompt3 = self.GetDlgItem(win32ui.IDC_PROMPT3)
+ self.butOK = self.GetDlgItem(win32con.IDOK)
+ self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
+ self.prompt1.SetWindowText("Python Timer App")
+ self.prompt2.SetWindowText("")
+ self.prompt3.SetWindowText("")
+ self.butOK.SetWindowText("Do it now")
+ self.butCancel.SetWindowText("Close")
+
+ self.timerManager = TimerManager(self)
+ self.ProcessArgs(sys.argv[self.argOff:])
+ self.timerManager.go()
+ return 1
+
+ def OnDestroy(self,msg):
+ dlgappcore.AppDialog.OnDestroy(self, msg)
+ self.timerManager.stop()
+ def OnOK(self):
+ # stop the timer, then restart after setting special boolean
+ self.timerManager.stop()
+ self.timerManager.bConnectNow = 1
+ self.timerManager.go()
+ return
+# def OnCancel(self): default behaviour - cancel == close.
+# return
+
+class TimerManager:
+ def __init__(self, dlg):
+ self.dlg = dlg
+ self.timerId = None
+ self.intervaler = eval(self.dlg.buildTimer)
+ self.bConnectNow = 0
+ self.bHaveSetPrompt1 = 0
+ def CaptureOutput(self):
+ self.oldOut = sys.stdout
+ self.oldErr = sys.stderr
+ sys.stdout = sys.stderr = self
+ self.bHaveSetPrompt1 = 0
+ def ReleaseOutput(self):
+ sys.stdout = self.oldOut
+ sys.stderr = self.oldErr
+ def write(self, str):
+ s = string.strip(str)
+ if len(s):
+ if self.bHaveSetPrompt1:
+ dest = self.dlg.prompt3
+ else:
+ dest = self.dlg.prompt1
+ self.bHaveSetPrompt1 = 1
+ dest.SetWindowText(s)
+ def go(self):
+ self.OnTimer(None,None)
+ def stop(self):
+ if self.timerId: timer.kill_timer (self.timerId)
+ self.timerId = None
+
+ def OnTimer(self, id, timeVal):
+ if id: timer.kill_timer (id)
+ if self.intervaler.IsTime() or self.bConnectNow :
+ # do the work.
+ try:
+ self.dlg.SetWindowText(self.dlg.title + " - Working...")
+ self.dlg.butOK.EnableWindow(0)
+ self.dlg.butCancel.EnableWindow(0)
+ self.CaptureOutput()
+ try:
+ exec(self.dlg.doWork)
+ print "The last operation completed successfully."
+ except:
+ t, v, tb = sys.exc_info()
+ str = "Failed: %s: %s" % (t, `v`)
+ print str
+ self.oldErr.write(str)
+ finally:
+ self.ReleaseOutput()
+ self.dlg.butOK.EnableWindow()
+ self.dlg.butCancel.EnableWindow()
+ self.dlg.SetWindowText(self.dlg.title)
+ else:
+ now = time.time()
+ nextTime = self.intervaler.GetNextTime()
+ if nextTime:
+ timeDiffSeconds = nextTime - now
+ timeDiffMinutes = int(timeDiffSeconds / 60)
+ timeDiffSeconds = timeDiffSeconds % 60
+ timeDiffHours = int(timeDiffMinutes / 60)
+ timeDiffMinutes = timeDiffMinutes % 60
+ self.dlg.prompt1.SetWindowText("Next connection due in %02d:%02d:%02d" % (timeDiffHours,timeDiffMinutes,timeDiffSeconds))
+ self.timerId = timer.set_timer (self.intervaler.GetWakeupInterval(), self.OnTimer)
+ self.bConnectNow = 0
+
+class TimerIntervaler:
+ def __init__(self):
+ self.nextTime = None
+ self.wakeUpInterval = 2000
+ def GetWakeupInterval(self):
+ return self.wakeUpInterval
+ def GetNextTime(self):
+ return self.nextTime
+ def IsTime(self):
+ now = time.time()
+ if self.nextTime is None:
+ self.nextTime = self.SetFirstTime(now)
+ ret = 0
+ if now >= self.nextTime:
+ ret = 1
+ self.nextTime = self.SetNextTime(self.nextTime, now)
+ # do the work.
+ return ret
+
+class EachAnyIntervaler(TimerIntervaler):
+ def __init__(self, timeAt, timePos, timeAdd, wakeUpInterval = None):
+ TimerIntervaler.__init__(self)
+ self.timeAt = timeAt
+ self.timePos = timePos
+ self.timeAdd = timeAdd
+ if wakeUpInterval:
+ self.wakeUpInterval = wakeUpInterval
+ def SetFirstTime(self, now):
+ timeTup = time.localtime(now)
+ lst = []
+ for item in timeTup:
+ lst.append(item)
+ bAdd = timeTup[self.timePos] > self.timeAt
+ lst[self.timePos] = self.timeAt
+ for pos in range(self.timePos+1, 6):
+ lst[pos]=0
+ ret = time.mktime(tuple(lst))
+ if (bAdd):
+ ret = ret + self.timeAdd
+ return ret;
+
+ def SetNextTime(self, lastTime, now):
+ return lastTime + self.timeAdd
+
+class EachMinuteIntervaler(EachAnyIntervaler):
+ def __init__(self, at=0):
+ EachAnyIntervaler.__init__(self, at, 5, 60, 2000)
+
+class EachHourIntervaler(EachAnyIntervaler):
+ def __init__(self, at=0):
+ EachAnyIntervaler.__init__(self, at, 4, 3600, 10000)
+
+class EachDayIntervaler(EachAnyIntervaler):
+ def __init__(self,at=0):
+ EachAnyIntervaler.__init__(self, at, 3, 86400, 10000)
+
+class TimerDialogApp(dlgappcore.DialogApp):
+ def CreateDialog(self):
+ return TimerAppDialog()
+
+def DoDemoWork():
+ print "Doing the work..."
+ print "About to connect"
+ win32api.MessageBeep(win32con.MB_ICONASTERISK)
+ win32api.Sleep(2000)
+ print "Doing something else..."
+ win32api.MessageBeep(win32con.MB_ICONEXCLAMATION)
+ win32api.Sleep(2000)
+ print "More work."
+ win32api.MessageBeep(win32con.MB_ICONHAND)
+ win32api.Sleep(2000)
+ print "The last bit."
+ win32api.MessageBeep(win32con.MB_OK)
+ win32api.Sleep(2000)
+
+app = TimerDialogApp()
+
+def t():
+ t = TimerAppDialog("Test Dialog")
+ t.DoModal()
+ return t
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NeedApp()
diff --git a/Pythonwin/pywin/Demos/app/customprint.py b/Pythonwin/pywin/Demos/app/customprint.py
new file mode 100644
index 0000000000..da7087657c
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/customprint.py
@@ -0,0 +1,198 @@
+# A demo of an Application object that has some custom print functionality.
+
+# If you desire, you can also run this from inside Pythonwin, in which
+# case it will do the demo inside the Pythonwin environment.
+
+# NOTE - The same was contributed by Roger Burnham, and works fine for him.
+# MHammond cant make it work (ie, it wont bring up the Print dialog for me.
+# However I suspect a local problem rather than anything in the code.
+
+from pywin.mfc import docview, dialog, afxres
+from pywin.framework import app
+
+import win32con
+import win32ui
+import win32api
+
+PRINTDLGORD = 1538
+IDC_PRINT_MAG_EDIT = 1010
+
+
+class PrintDemoTemplate(docview.DocTemplate):
+ def _SetupSharedMenu_(self):
+ pass
+
+class PrintDemoView(docview.ScrollView):
+
+ def OnInitialUpdate(self):
+ ret = self._obj_.OnInitialUpdate()
+ self.colors = {'Black' : (0x00<<0) + (0x00<<8) + (0x00<<16),
+ 'Red' : (0xff<<0) + (0x00<<8) + (0x00<<16),
+ 'Green' : (0x00<<0) + (0xff<<8) + (0x00<<16),
+ 'Blue' : (0x00<<0) + (0x00<<8) + (0xff<<16),
+ 'Cyan' : (0x00<<0) + (0xff<<8) + (0xff<<16),
+ 'Magenta': (0xff<<0) + (0x00<<8) + (0xff<<16),
+ 'Yellow' : (0xff<<0) + (0xff<<8) + (0x00<<16),
+ }
+ self.pens = {}
+ for name, color in self.colors.items():
+ self.pens[name] = win32ui.CreatePen(win32con.PS_SOLID,
+ 5, color)
+ self.pen = None
+ self.size = (128,128)
+ self.SetScaleToFitSize(self.size)
+ self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT)
+ self.HookCommand(self.OnFilePrintPreview,
+ win32ui.ID_FILE_PRINT_PREVIEW)
+ return ret
+
+ def OnDraw(self, dc):
+ oldPen = None
+ x,y = self.size
+ delta = 2
+ colors = self.colors.keys()
+ colors.sort()
+ colors = colors*2
+ for color in colors:
+ if oldPen is None:
+ oldPen = dc.SelectObject(self.pens[color])
+ else:
+ dc.SelectObject(self.pens[color])
+ dc.MoveTo(( delta, delta))
+ dc.LineTo((x-delta, delta))
+ dc.LineTo((x-delta, y-delta))
+ dc.LineTo(( delta, y-delta))
+ dc.LineTo(( delta, delta))
+ delta = delta + 4
+ if x-delta <= 0 or y-delta <= 0:
+ break
+ dc.SelectObject(oldPen)
+
+ def OnPrepareDC (self, dc, pInfo):
+ if dc.IsPrinting():
+ mag = self.prtDlg['mag']
+ dc.SetMapMode(win32con.MM_ANISOTROPIC);
+ dc.SetWindowOrg((0, 0))
+ dc.SetWindowExt((1, 1))
+ dc.SetViewportOrg((0, 0))
+ dc.SetViewportExt((mag, mag))
+
+ def OnPreparePrinting(self, pInfo):
+ flags = (win32ui.PD_USEDEVMODECOPIES|
+ win32ui.PD_PAGENUMS|
+ win32ui.PD_NOPAGENUMS|
+ win32ui.PD_NOSELECTION)
+ self.prtDlg = ImagePrintDialog(pInfo, PRINTDLGORD, flags)
+ pInfo.SetPrintDialog(self.prtDlg)
+ pInfo.SetMinPage(1)
+ pInfo.SetMaxPage(1)
+ pInfo.SetFromPage(1)
+ pInfo.SetToPage(1)
+ ret = self.DoPreparePrinting(pInfo)
+ return ret
+
+ def OnBeginPrinting(self, dc, pInfo):
+ return self._obj_.OnBeginPrinting(dc, pInfo)
+
+ def OnEndPrinting(self, dc, pInfo):
+ del self.prtDlg
+ return self._obj_.OnEndPrinting(dc, pInfo)
+
+ def OnFilePrintPreview(self, *arg):
+ self._obj_.OnFilePrintPreview()
+
+ def OnFilePrint(self, *arg):
+ self._obj_.OnFilePrint()
+
+ def OnPrint(self, dc, pInfo):
+ doc = self.GetDocument()
+ metrics = dc.GetTextMetrics()
+ cxChar = metrics['tmAveCharWidth']
+ cyChar = metrics['tmHeight']
+ left, top, right, bottom = pInfo.GetDraw()
+ dc.TextOut(0, 2*cyChar, doc.GetTitle())
+ top = top + (7*cyChar)/2
+ dc.MoveTo(left, top)
+ dc.LineTo(right, top)
+ top = top + cyChar
+ # this seems to have not effect...
+ # get what I want with the dc.SetWindowOrg calls
+ pInfo.SetDraw((left, top, right, bottom))
+ dc.SetWindowOrg((0, -top))
+
+ self.OnDraw(dc)
+ dc.SetTextAlign(win32con.TA_LEFT|win32con.TA_BOTTOM)
+
+ rect = self.GetWindowRect()
+ rect = self.ScreenToClient(rect)
+ height = (rect[3]-rect[1])
+ dc.SetWindowOrg((0, -(top+height+cyChar)))
+ dc.MoveTo(left, 0)
+ dc.LineTo(right, 0)
+
+ x = 0
+ y = (3*cyChar)/2
+
+ dc.TextOut(x, y, doc.GetTitle())
+ y = y + cyChar
+
+
+class PrintDemoApp(app.CApp):
+ def __init__(self):
+ app.CApp.__init__(self)
+
+ def InitInstance(self):
+ template = PrintDemoTemplate(None, None,
+ None, PrintDemoView)
+ self.AddDocTemplate(template)
+ self._obj_.InitMDIInstance()
+ self.LoadMainFrame()
+ doc = template.OpenDocumentFile(None)
+ doc.SetTitle('Custom Print Document')
+
+
+class ImagePrintDialog(dialog.PrintDialog):
+
+ sectionPos = 'Image Print Demo'
+
+ def __init__(self, pInfo, dlgID, flags=win32ui.PD_USEDEVMODECOPIES):
+ dialog.PrintDialog.__init__(self, pInfo, dlgID, flags=flags)
+ mag = win32ui.GetProfileVal(self.sectionPos,
+ 'Document Magnification',
+ 0)
+ if mag <= 0:
+ mag = 2
+ win32ui.WriteProfileVal(self.sectionPos,
+ 'Document Magnification',
+ mag)
+
+ self['mag'] = mag
+
+ def OnInitDialog(self):
+ dialog.PrintDialog.OnInitDialog(self)
+ self.magCtl = self.GetDlgItem(IDC_PRINT_MAG_EDIT)
+ self.magCtl.SetWindowText(`self['mag']`)
+ return 1
+
+ def OnOK(self):
+ dialog.PrintDialog.OnOK(self)
+ strMag = self.magCtl.GetWindowText()
+ try:
+ self['mag'] = string.atoi(strMag)
+ except:
+ pass
+ win32ui.WriteProfileVal(self.sectionPos,
+ 'Document Magnification',
+ self['mag'])
+
+
+if __name__=='__main__':
+ # Running under Pythonwin
+ def test():
+ template = PrintDemoTemplate(None, None,
+ None, PrintDemoView)
+ template.OpenDocumentFile(None)
+ test()
+else:
+ app = PrintDemoApp()
+
diff --git a/Pythonwin/pywin/Demos/app/demoutils.py b/Pythonwin/pywin/Demos/app/demoutils.py
new file mode 100644
index 0000000000..74352011e7
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/demoutils.py
@@ -0,0 +1,52 @@
+# Utilities for the demos
+
+import sys, win32api, win32con, win32ui
+
+NotScriptMsg = """\
+This demo program is not designed to be run as a Script, but is
+probably used by some other test program. Please try another demo.
+"""
+
+NeedGUIMsg = """\
+This demo program can only be run from inside of Pythonwin
+
+You must start Pythonwin, and select 'Run' from the toolbar or File menu
+"""
+
+
+NeedAppMsg = """\
+This demo program is a 'Pythonwin Application'.
+
+It is more demo code than an example of Pythonwin's capabilities.
+
+To run it, you must execute the command:
+pythonwin.exe /app "%s"
+
+Would you like to execute it now?
+"""
+
+def NotAScript():
+ import win32ui
+ win32ui.MessageBox(NotScriptMsg, "Demos")
+
+def NeedGoodGUI():
+ from pywin.framework.app import HaveGoodGUI
+ rc = HaveGoodGUI()
+ if not rc:
+ win32ui.MessageBox(NeedGUIMsg, "Demos")
+ return rc
+
+def NeedApp():
+ import win32ui
+ rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
+ if rc==win32con.IDYES:
+ try:
+ parent = win32ui.GetMainFrame().GetSafeHwnd()
+ win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
+ except win32api.error, details:
+ win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
+
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NotAScript()
diff --git a/Pythonwin/pywin/Demos/app/dlgappdemo.py b/Pythonwin/pywin/Demos/app/dlgappdemo.py
new file mode 100644
index 0000000000..3390c8ce53
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/dlgappdemo.py
@@ -0,0 +1,48 @@
+# dlgappdemo - a demo of a dialog application.
+# This is a demonstration of both a custom "application" module,
+# and a Python program in a dialog box.
+#
+# NOTE: You CAN NOT import this module from either PythonWin or Python.
+# This module must be specified on the commandline to PythonWin only.
+# eg, PythonWin /app dlgappdemo.py
+
+from pywin.framework import dlgappcore, app
+import win32ui
+import sys
+import regsub
+
+class TestDialogApp(dlgappcore.DialogApp):
+ def CreateDialog(self):
+ return TestAppDialog()
+
+
+class TestAppDialog(dlgappcore.AppDialog):
+ def __init__(self):
+ self.edit = None
+ dlgappcore.AppDialog.__init__(self, win32ui.IDD_LARGE_EDIT)
+ def OnInitDialog(self):
+ self.SetWindowText('Test dialog application')
+ self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
+ print "Hello from Python"
+ print "args are:",
+ for arg in sys.argv:
+ print arg
+ return 1
+
+ def PreDoModal(self):
+ sys.stdout = sys.stderr = self
+
+ def write(self, str):
+ if self.edit:
+ self.edit.SetSel(-2)
+ # translate \n to \n\r
+ self.edit.ReplaceSel(regsub.gsub('\n','\r\n',str))
+ else:
+ win32ui.OutputDebug("dlgapp - no edit control! >>\n%s\n<<\n" % str )
+
+app.AppBuilder = TestDialogApp
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NeedApp()
+
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/app/dojobapp.py b/Pythonwin/pywin/Demos/app/dojobapp.py
new file mode 100644
index 0000000000..bdd22cd975
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/dojobapp.py
@@ -0,0 +1,62 @@
+# dojobapp - do a job, show the result in a dialog, and exit.
+#
+# Very simple - faily minimal dialog based app.
+#
+# This should be run using the command line:
+# pythonwin /app demos\dojobapp.py
+
+import win32ui
+import win32api
+import win32con
+import sys
+from pywin.framework import app, dlgappcore
+import string
+
+class DoJobAppDialog(dlgappcore.AppDialog):
+ softspace=1
+ def __init__(self, appName = ""):
+ self.appName = appName
+ dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
+
+ def PreDoModal(self):
+ pass
+
+ def ProcessArgs(self, args):
+ pass
+
+ def OnInitDialog(self):
+ self.SetWindowText(self.appName)
+ butCancel = self.GetDlgItem(win32con.IDCANCEL)
+ butCancel.ShowWindow(win32con.SW_HIDE)
+ p1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
+ p2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
+
+ # Do something here!
+
+ p1.SetWindowText("Hello there")
+ p2.SetWindowText("from the demo")
+ def OnDestroy(self,msg):
+ pass
+# def OnOK(self):
+# pass
+# def OnCancel(self): default behaviour - cancel == close.
+# return
+
+class DoJobDialogApp(dlgappcore.DialogApp):
+ def CreateDialog(self):
+ return DoJobAppDialog("Do Something")
+
+class CopyToDialogApp(DoJobDialogApp):
+ def __init__(self):
+ DoJobDialogApp.__init__(self)
+
+app.AppBuilder = DoJobDialogApp
+
+def t():
+ t = DoJobAppDialog("Copy To")
+ t.DoModal()
+ return t
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NeedApp()
diff --git a/Pythonwin/pywin/Demos/app/helloapp.py b/Pythonwin/pywin/Demos/app/helloapp.py
new file mode 100644
index 0000000000..20ff3706aa
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/helloapp.py
@@ -0,0 +1,44 @@
+##
+## helloapp.py
+##
+##
+## A nice, small 'hello world' Pythonwin application.
+## NOT an MDI application - just a single, normal, top-level window.
+##
+## MUST be run with the command line "pythonwin.exe /app helloapp.py"
+## (or if you are really keen, rename "pythonwin.exe" to something else, then
+## using MSVC or similar, edit the string section in the .EXE to name this file)
+##
+## Originally by Willy Heineman
+
+
+import win32con
+import win32ui
+from pywin.mfc import window, dialog, thread, afxres
+
+# The main frame.
+# Does almost nothing at all - doesnt even create a child window!
+class HelloWindow(window.Wnd):
+ def __init__(self):
+ # The window.Wnd ctor creates a Window object, and places it in
+ # self._obj_. Note the window object exists, but the window itself
+ # does not!
+ window.Wnd.__init__(self, win32ui.CreateWnd())
+
+ # Now we ask the window object to create the window itself.
+ self._obj_.CreateWindowEx(win32con.WS_EX_CLIENTEDGE, \
+ win32ui.RegisterWndClass(0, 0, win32con.COLOR_WINDOW + 1), \
+ 'Hello World!', win32con.WS_OVERLAPPEDWINDOW, \
+ (100, 100, 400, 300), None, 0, None)
+
+# The application object itself.
+class HelloApp(thread.WinApp):
+
+ def InitInstance(self):
+ self.frame = HelloWindow()
+ self.frame.ShowWindow(win32con.SW_SHOWNORMAL)
+ # We need to tell MFC what our main frame is.
+ self.SetMainFrame(self.frame)
+
+# Now create the application object itself!
+app = HelloApp()
diff --git a/Pythonwin/pywin/Demos/app/readme.txt b/Pythonwin/pywin/Demos/app/readme.txt
new file mode 100644
index 0000000000..7d58a4f9a2
--- /dev/null
+++ b/Pythonwin/pywin/Demos/app/readme.txt
@@ -0,0 +1,7 @@
+These files are all demos of a Pythonwin "Application".
+
+To run these demos, you should use the command line:
+
+pythonwin /app filename.py
+
+where filename.py is one of the demos in this directory.
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/cmdserver.py b/Pythonwin/pywin/Demos/cmdserver.py
new file mode 100644
index 0000000000..77498a3bc4
--- /dev/null
+++ b/Pythonwin/pywin/Demos/cmdserver.py
@@ -0,0 +1,106 @@
+# cmdserver.py
+
+# Demo code that is not Pythonwin related, but too good to throw away...
+
+import win32api
+import sys
+from pywin.framework import winout
+
+import thread, sys
+
+import traceback
+
+class ThreadWriter:
+ "Assign an instance to sys.stdout for per-thread printing objects - Courtesy Guido!"
+ def __init__(self):
+ "Constructor -- initialize the table of writers"
+ self.writers = {}
+ self.origStdOut = None
+ def register(self, writer):
+ "Register the writer for the current thread"
+ self.writers[thread.get_ident()] = writer
+ if self.origStdOut is None:
+ self.origStdOut = sys.stdout
+ sys.stdout = self
+
+ def unregister(self):
+ "Remove the writer for the current thread, if any"
+ try:
+ del self.writers[thread.get_ident()]
+ except KeyError:
+ pass
+ if len(self.writers)==0:
+ sys.stdout = self.origStdOut
+ self.origStdOut = None
+
+ def getwriter(self):
+ "Return the current thread's writer, default sys.stdout"
+ try:
+ return self.writers[thread.get_ident()]
+ except KeyError:
+ return self.origStdOut
+
+ def write(self, str):
+ "Write to the current thread's writer, default sys.stdout"
+ self.getwriter().write(str)
+
+def Test():
+ num=1
+ while num<1000:
+ print 'Hello there no ' + str(num)
+ win32api.Sleep(50)
+ num = num + 1
+
+class flags:
+ SERVER_BEST = 0
+ SERVER_IMMEDIATE = 1
+ SERVER_THREAD = 2
+ SERVER_PROCESS = 3
+
+def StartServer( cmd, title=None, bCloseOnEnd=0, serverFlags = flags.SERVER_BEST ):
+ out = winout.WindowOutput( title, None, winout.flags.WQ_IDLE )
+ if not title:
+ title=cmd
+ out.Create(title)
+# ServerThread((out, cmd, title, bCloseOnEnd))
+# out = sys.stdout
+ thread.start_new_thread( ServerThread, (out, cmd, title, bCloseOnEnd) )
+
+def ServerThread(myout, cmd, title, bCloseOnEnd):
+ try:
+ writer.register(myout)
+ print 'Executing "%s"\n' % cmd
+ bOK = 1
+ try:
+ import __main__
+ exec (cmd+'\n', __main__.__dict__)
+ except:
+ bOK = 0
+ if bOK:
+ print "Command terminated without errors."
+ else:
+ t, v, tb = sys.exc_info()
+ print t, ': ', v
+ traceback.print_tb(tb)
+ print "Command terminated with an unhandled exception"
+ writer.unregister()
+ if bOK and bCloseOnEnd:
+ myout.frame.DestroyWindow()
+
+ # Unhandled exception of any kind in a thread kills the gui!
+ except:
+ t, v, tb = sys.exc_info()
+ print t, ': ', v
+ traceback.print_tb(tb)
+ print "Thread failed"
+
+# assist for reloading (when debugging) - use only 1 tracer object,
+# else a large chain of tracer objects will exist.
+#try:
+# writer
+#except NameError:
+# writer=ThreadWriter()
+if __name__=='__main__':
+ import demoutils
+ demoutils.NotAScript()
+
diff --git a/Pythonwin/pywin/Demos/createwin.py b/Pythonwin/pywin/Demos/createwin.py
new file mode 100644
index 0000000000..8f2687025d
--- /dev/null
+++ b/Pythonwin/pywin/Demos/createwin.py
@@ -0,0 +1,99 @@
+#
+# Window creation example
+#
+# This example creates a minimal "control" that just fills in its
+# window with red. To make your own control, subclass Control and
+# write your own OnPaint() method. See PyCWnd.HookMessage for what
+# the parameters to OnPaint are.
+#
+
+from pywin.mfc import dialog, window
+import win32ui
+import win32con
+import win32api
+
+class Control(window.Wnd):
+ """Generic control class"""
+ def __init__ (self):
+ window.Wnd.__init__(self, win32ui.CreateWnd ())
+
+ def OnPaint (self):
+ dc, paintStruct = self.BeginPaint()
+ self.DoPaint(dc)
+ self.EndPaint(paintStruct)
+
+ def DoPaint (self, dc): # Override this!
+ pass
+
+class RedBox (Control):
+ def DoPaint (self, dc):
+ dc.FillSolidRect (self.GetClientRect(), win32api.RGB(255,0,0))
+
+
+class RedBoxWithPie (RedBox):
+ def DoPaint (self, dc):
+ RedBox.DoPaint(self, dc)
+ r = self.GetClientRect()
+ dc.Pie(r[0], r[1], r[2], r[3], 0,0,r[2], r[3]/2)
+
+def MakeDlgTemplate():
+ style = (win32con.DS_MODALFRAME |
+ win32con.WS_POPUP |
+ win32con.WS_VISIBLE |
+ win32con.WS_CAPTION |
+ win32con.WS_SYSMENU |
+ win32con.DS_SETFONT)
+ cs = (win32con.WS_CHILD |
+ win32con.WS_VISIBLE)
+
+ w = 64
+ h = 64
+
+ dlg = [["Red box",
+ (0, 0, w, h),
+ style,
+ None,
+ (8, "MS Sans Serif")],
+ ]
+
+ s = win32con.WS_TABSTOP | cs
+
+ dlg.append([128,
+ "Cancel",
+ win32con.IDCANCEL,
+ (7, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
+
+ return dlg
+
+class TestDialog(dialog.Dialog):
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.redbox = RedBox ()
+ self.redbox.CreateWindow (None, "RedBox",
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE,
+ (5, 5, 90, 68),
+ self, 1003)
+ return rc
+
+class TestPieDialog(dialog.Dialog):
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.control = RedBoxWithPie()
+ self.control.CreateWindow (None, "RedBox with Pie",
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE,
+ (5, 5, 90, 68),
+ self, 1003)
+
+def demo(modal=0):
+ d = TestPieDialog (MakeDlgTemplate())
+ if modal:
+ d.DoModal()
+ else:
+ d.CreateWindow()
+
+if __name__=='__main__':
+ demo(1)
+
+# II Cor. 12:9 $Header$
diff --git a/Pythonwin/pywin/Demos/demoutils.py b/Pythonwin/pywin/Demos/demoutils.py
new file mode 100644
index 0000000000..818313d115
--- /dev/null
+++ b/Pythonwin/pywin/Demos/demoutils.py
@@ -0,0 +1,54 @@
+# Utilities for the demos
+
+import sys, win32api, win32con, win32ui
+
+NotScriptMsg = """\
+This demo program is not designed to be run as a Script, but is
+probably used by some other test program. Please try another demo.
+"""
+
+NeedGUIMsg = """\
+This demo program can only be run from inside of Pythonwin
+
+You must start Pythonwin, and select 'Run' from the toolbar or File menu
+"""
+
+
+NeedAppMsg = """\
+This demo program is a 'Pythonwin Application'.
+
+It is more demo code than an example of Pythonwin's capabilities.
+
+To run it, you must execute the command:
+pythonwin.exe /app "%s"
+
+Would you like to execute it now?
+"""
+
+def NotAScript():
+ import win32ui
+ win32ui.MessageBox(NotScriptMsg, "Demos")
+
+def NeedGoodGUI():
+ from pywin.framework.app import HaveGoodGUI
+ rc = HaveGoodGUI()
+ if not rc:
+ win32ui.MessageBox(NeedGUIMsg, "Demos")
+ return rc
+
+def NeedApp():
+ import win32ui
+ rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
+ if rc==win32con.IDYES:
+ try:
+ parent = win32ui.GetMainFrame().GetSafeHwnd()
+ win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
+ except win32api.error, details:
+ win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
+
+
+from pywin.framework.app import HaveGoodGUI
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NotAScript()
diff --git a/Pythonwin/pywin/Demos/dibdemo.py b/Pythonwin/pywin/Demos/dibdemo.py
new file mode 100644
index 0000000000..10ca222750
--- /dev/null
+++ b/Pythonwin/pywin/Demos/dibdemo.py
@@ -0,0 +1,69 @@
+# A demo which creates a view and a frame which displays a PPM format bitmap
+#
+# This hasnnt been run in a while, as I dont have many of that format around!
+import win32ui
+import win32con
+import win32api
+import string
+
+class DIBView:
+ def __init__(self, doc, dib):
+ self.dib = dib
+ self.view = win32ui.CreateView(doc)
+ self.width = self.height = 0
+ # set up message handlers
+# self.view.OnPrepareDC = self.OnPrepareDC
+ self.view.HookMessage (self.OnSize, win32con.WM_SIZE)
+
+ def OnSize (self, params):
+ lParam = params[3]
+ self.width = win32api.LOWORD(lParam)
+ self.height = win32api.HIWORD(lParam)
+
+ def OnDraw (self, ob, dc):
+ # set sizes used for "non strecth" mode.
+ self.view.SetScrollSizes(win32con.MM_TEXT, self.dib.GetSize())
+ dibSize = self.dib.GetSize()
+ dibRect = (0,0,dibSize[0], dibSize[1])
+ # stretch BMP.
+ #self.dib.Paint(dc, (0,0,self.width, self.height),dibRect)
+ # non stretch.
+ self.dib.Paint(dc)
+
+class DIBDemo:
+ def __init__(self, filename, * bPBM):
+ # init data members
+ f = open(filename, 'rb')
+ dib=win32ui.CreateDIBitmap()
+ if len(bPBM)>0:
+ magic=f.readline()
+ if magic <> "P6\n":
+ print "The file is not a PBM format file"
+ raise "Failed"
+ # check magic?
+ rowcollist=string.split(f.readline())
+ cols=string.atoi(rowcollist[0])
+ rows=string.atoi(rowcollist[1])
+ f.readline() # whats this one?
+ dib.LoadPBMData(f,(cols,rows))
+ else:
+ dib.LoadWindowsFormatFile(f)
+ f.close()
+ # create doc/view
+ self.doc = win32ui.CreateDoc()
+ self.dibView = DIBView( self.doc, dib )
+ self.frame = win32ui.CreateMDIFrame()
+ self.frame.LoadFrame() # this will force OnCreateClient
+ self.doc.SetTitle ('DIB Demo')
+ self.frame.ShowWindow()
+
+ # display the sucka
+ self.frame.ActivateFrame()
+
+ def OnCreateClient( self, createparams, context ):
+ self.dibView.view.CreateWindow(self.frame)
+ return 1
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NotAScript()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/dlgtest.py b/Pythonwin/pywin/Demos/dlgtest.py
new file mode 100644
index 0000000000..b652846feb
--- /dev/null
+++ b/Pythonwin/pywin/Demos/dlgtest.py
@@ -0,0 +1,138 @@
+# A Demo of Pythonwin's Dialog and Property Page support.
+
+###################
+#
+# First demo - use the built-in to Pythonwin "Tab Stop" dialog, but
+# customise it heavily.
+#
+# ID's for the tabstop dialog - out test.
+#
+from win32ui import IDD_SET_TABSTOPS
+from win32ui import IDC_EDIT_TABS
+from win32ui import IDC_PROMPT_TABS
+from win32con import IDOK
+from win32con import IDCANCEL
+
+import win32ui
+import win32con
+
+from pywin.mfc import dialog
+
+class TestDialog(dialog.Dialog):
+ def __init__(self, modal=1):
+ dialog.Dialog.__init__(self, IDD_SET_TABSTOPS)
+ self.counter=0
+ if modal:
+ self.DoModal()
+ else:
+ self.CreateWindow()
+
+ def OnInitDialog(self):
+ # Set the caption of the dialog itself.
+ self.SetWindowText("Used to be Tab Stops!")
+ # Get a child control, remember it, and change its text.
+ self.edit=self.GetDlgItem(IDC_EDIT_TABS) # the text box.
+ self.edit.SetWindowText("Test")
+ # Hook a Windows message for the dialog.
+ self.edit.HookMessage(self.KillFocus, win32con.WM_KILLFOCUS)
+ # Get the prompt control, and change its next.
+ prompt=self.GetDlgItem(IDC_PROMPT_TABS) # the prompt box.
+ prompt.SetWindowText("Prompt")
+ # And the same for the button..
+ cancel=self.GetDlgItem(IDCANCEL) # the cancel button
+ cancel.SetWindowText("&Kill me")
+
+ # And just for demonstration purposes, we hook the notify message for the dialog.
+ # This allows us to be notified when the Edit Control text changes.
+ self.HookCommand(self.OnNotify, IDC_EDIT_TABS)
+
+ def OnNotify(self, controlid, code):
+ if code==win32con.EN_CHANGE:
+ print "Edit text changed!"
+ return 1 # I handled this, so no need to call defaults!
+
+ # kill focus for the edit box.
+ # Simply increment the value in the text box.
+ def KillFocus(self,msg):
+ self.counter=self.counter+1
+ if self.edit != None:
+ self.edit.SetWindowText(str(self.counter))
+
+ # Called when the dialog box is terminating...
+ def OnDestroy(self,msg):
+ del self.edit
+ del self.counter
+
+# A very simply Property Sheet.
+# We only make a new class for demonstration purposes.
+class TestSheet(dialog.PropertySheet):
+ def __init__(self, title):
+ dialog.PropertySheet.__init__(self, title)
+ self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
+ def OnActivate(self, msg):
+ pass
+
+# A very simply Property Page, which will be "owned" by the above
+# Property Sheet.
+# We create a new class, just so we can hook a control notification.
+class TestPage(dialog.PropertyPage):
+ def OnInitDialog(self):
+ # We use the HookNotify function to allow Python to respond to
+ # Windows WM_NOTIFY messages.
+ # In this case, we are interested in BN_CLICKED messages.
+ self.HookNotify(self.OnNotify, win32con.BN_CLICKED)
+
+ def OnNotify(self, std, extra):
+ print "OnNotify", std, extra
+
+# Some code that actually uses these objects.
+def demo(modal = 0):
+ TestDialog(modal)
+
+ # property sheet/page demo
+ ps=win32ui.CreatePropertySheet('Property Sheet/Page Demo')
+ # Create a completely standard PropertyPage.
+ page1=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
+ # Create our custom property page.
+ page2=TestPage(win32ui.IDD_PROPDEMO2)
+ ps.AddPage(page1)
+ ps.AddPage(page2)
+ if modal:
+ ps.DoModal()
+ else:
+ style = win32con.WS_SYSMENU|win32con.WS_POPUP|win32con.WS_CAPTION|win32con.DS_MODALFRAME|win32con.WS_VISIBLE
+ styleex = win32con.WS_EX_DLGMODALFRAME | win32con.WS_EX_PALETTEWINDOW
+ ps.CreateWindow(win32ui.GetMainFrame(), style, styleex)
+
+
+def test(modal=1):
+
+# dlg=dialog.Dialog(1010)
+# dlg.CreateWindow()
+# dlg.EndDialog(0)
+# del dlg
+# return
+ # property sheet/page demo
+ ps=TestSheet('Property Sheet/Page Demo')
+ page1=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
+ page2=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO2)
+ ps.AddPage(page1)
+ ps.AddPage(page2)
+ del page1
+ del page2
+ if modal:
+ ps.DoModal()
+ else:
+ ps.CreateWindow(win32ui.GetMainFrame())
+ return ps
+
+def d():
+ dlg = win32ui.CreateDialog(win32ui.IDD_DEBUGGER)
+ dlg.datalist.append((win32ui.IDC_DBG_RADIOSTACK, "radio"))
+ print "data list is ", dlg.datalist
+ dlg.data['radio']=1
+ dlg.DoModal()
+ print dlg.data['radio']
+
+if __name__=='__main__':
+ demo(1)
diff --git a/Pythonwin/pywin/Demos/dyndlg.py b/Pythonwin/pywin/Demos/dyndlg.py
new file mode 100644
index 0000000000..5d36690ea5
--- /dev/null
+++ b/Pythonwin/pywin/Demos/dyndlg.py
@@ -0,0 +1,73 @@
+# dyndlg.py
+# contributed by Curt Hagenlocher
+
+# Dialog Template params:
+# Parameter 0 - Window caption
+# Parameter 1 - Bounds (rect tuple)
+# Parameter 2 - Window style
+# Parameter 3 - Extended style
+# Parameter 4 - Font tuple
+# Parameter 5 - Menu name
+# Parameter 6 - Window class
+# Dialog item params:
+# Parameter 0 - Window class
+# Parameter 1 - Text
+# Parameter 2 - ID
+# Parameter 3 - Bounds
+# Parameter 4 - Style
+# Parameter 5 - Extended style
+# Parameter 6 - Extra data
+
+
+import win32ui
+import win32con
+from pywin.mfc import dialog, window
+
+def MakeDlgTemplate():
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE
+ dlg = [ ["Select Warehouse", (0, 0, 177, 93), style, None, (8, "MS Sans Serif")], ]
+ dlg.append([130, "Current Warehouse:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
+ dlg.append([130, "ASTORIA", 128, (16, 17, 99, 7), cs | win32con.SS_LEFT])
+ dlg.append([130, "New &Warehouse:", -1, (7, 29, 69, 9), cs | win32con.SS_LEFT])
+ s = win32con.WS_TABSTOP | cs
+# dlg.append([131, None, 130, (5, 40, 110, 48),
+# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
+ dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP])
+
+ dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
+ s = win32con.BS_PUSHBUTTON | s
+ dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
+ dlg.append([128, "&Help", 100, (124, 74, 50, 14), s])
+
+ return dlg
+
+def test1():
+ win32ui.CreateDialogIndirect( MakeDlgTemplate() ).DoModal()
+
+def test2():
+ dialog.Dialog( MakeDlgTemplate() ).DoModal()
+
+def test3():
+ dlg = win32ui.LoadDialogResource(win32ui.IDD_SET_TABSTOPS)
+ dlg[0][0] = 'New Dialog Title'
+ dlg[0][1] = (80, 20, 161, 60)
+ dlg[1][1] = '&Confusion:'
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
+ dlg.append([128, "&Help", 100, (111, 41, 40, 14), cs])
+ dialog.Dialog( dlg ).DoModal()
+
+def test4():
+ page1=dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO1))
+ page2=dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO2))
+ ps=dialog.PropertySheet('Property Sheet/Page Demo', None, [page1, page2])
+ ps.DoModal()
+
+def testall():
+ test1()
+ test2()
+ test3()
+ test4()
+
+if __name__=='__main__':
+ testall()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/fontdemo.py b/Pythonwin/pywin/Demos/fontdemo.py
new file mode 100644
index 0000000000..449e3da82e
--- /dev/null
+++ b/Pythonwin/pywin/Demos/fontdemo.py
@@ -0,0 +1,79 @@
+# Demo of Generic document windows, DC, and Font usage
+# by Dave Brennan (brennan@hal.com)
+
+# usage examples:
+
+# >>> from fontdemo import *
+# >>> d = FontDemo('Hello, Python')
+# >>> f1 = { 'name':'Arial', 'height':36, 'weight':win32con.FW_BOLD}
+# >>> d.SetFont(f1)
+# >>> f2 = {'name':'Courier New', 'height':24, 'italic':1}
+# >>> d.SetFont (f2)
+
+import win32ui
+import win32con
+import win32api
+
+from pywin.mfc import docview
+
+
+# font is a dictionary in which the following elements matter:
+# (the best matching font to supplied parameters is returned)
+# name string name of the font as known by Windows
+# size point size of font in logical units
+# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
+# italic boolean; true if set to anything but None
+# underline boolean; true if set to anything but None
+
+class FontView(docview.ScrollView):
+ def __init__(self, doc, text = 'Python Rules!', font_spec = {'name':'Arial', 'height':42}):
+ docview.ScrollView.__init__(self, doc)
+ self.font = win32ui.CreateFont (font_spec)
+ self.text = text
+ self.width = self.height = 0
+ # set up message handlers
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+ def OnAttachedObjectDeath(self):
+ docview.ScrollView.OnAttachedObjectDeath(self)
+ del self.font
+
+ def SetFont (self, new_font):
+ # Change font on the fly
+ self.font = win32ui.CreateFont (new_font)
+ # redraw the entire client window
+ selfInvalidateRect (None)
+ def OnSize (self, params):
+ lParam = params[3]
+ self.width = win32api.LOWORD(lParam)
+ self.height = win32api.HIWORD(lParam)
+
+ def OnPrepareDC (self, dc, printinfo):
+ # Set up the DC for forthcoming OnDraw call
+ self.SetScrollSizes(win32con.MM_TEXT, (100,100))
+ dc.SetTextColor (win32api.RGB(0,0,255))
+ dc.SetBkColor (win32api.GetSysColor (win32con.COLOR_WINDOW))
+ dc.SelectObject (self.font)
+ dc.SetTextAlign (win32con.TA_CENTER | win32con.TA_BASELINE)
+
+ def OnDraw (self, dc):
+ if (self.width == 0 and self.height == 0):
+ left, top, right, bottom = self.GetClientRect()
+ self.width = right - left
+ self.height = bottom - top
+ x, y = self.width / 2, self.height / 2
+ dc.TextOut (x, y, self.text)
+
+def FontDemo():
+ # create doc/view
+ template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, None, None, FontView)
+ doc=template.OpenDocumentFile(None)
+ doc.SetTitle ('Font Demo')
+# print "template is ", template, "obj is", template._obj_
+ template.close()
+# print "closed"
+# del template
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ FontDemo()
diff --git a/Pythonwin/pywin/Demos/guidemo.py b/Pythonwin/pywin/Demos/guidemo.py
new file mode 100644
index 0000000000..ead7b28fed
--- /dev/null
+++ b/Pythonwin/pywin/Demos/guidemo.py
@@ -0,0 +1,68 @@
+# GUI Demo - just a worker script to invoke all the other demo/test scripts.
+import win32ui
+import __main__
+import sys
+import regutil
+import win32api
+
+demos = [ \
+# ('Font', 'import fontdemo;fontdemo.FontDemo()'),
+ ('Open GL Demo', 'import openGLDemo;openGLDemo.test()'),
+ ('Threaded GUI', 'import threadedgui;threadedgui.ThreadedDemo()'),
+ ('HTML Rich Text', 'import htmlrichedit;htmlrichedit.test()'),
+ ('Tree View Demo', 'import hiertest;hiertest.demoboth()'),
+ ('3-Way Splitter Window', 'import splittst;splittst.demo()'),
+ ('Custom Toolbars and Tooltips', 'import toolbar;toolbar.test()'),
+ ('Progress Bar', 'import progressbar;progressbar.demo()'),
+ ('Dynamic window creation', 'import createwin;createwin.demo()'),
+ ('Various Dialog demos', 'import dlgtest;dlgtest.demo()'),
+ ('OCX Control Demo', 'from ocx import ocxtest;ocxtest.demo()'),
+ ('OCX Serial Port Demo', 'from ocx import ocxserialtest; ocxserialtest.test()'),
+ ('IE4 Control Demo', 'from ocx import webbrowser; webbrowser.Demo()'),
+]
+
+def demo():
+ try:
+ # seeif I can locate the demo files.
+ import fontdemo
+ except ImportError:
+ # else put the demos direectory on the path (if not already)
+ try:
+ instPath = regutil.GetRegistryDefaultValue(regutil.BuildDefaultPythonKey() + "\\InstallPath")
+ except win32api.error:
+ print "The InstallPath can not be located, and the Demos directory is not on the path"
+ instPath="."
+
+ demosDir = win32ui.FullPath(instPath + "\\Demos")
+ for path in sys.path:
+ if win32ui.FullPath(path)==demosDir:
+ break
+ else:
+ sys.path.append(demosDir)
+ import fontdemo
+
+ import sys
+ if "/go" in sys.argv:
+ for name, cmd in demos:
+ try:
+ exec cmd
+ except:
+ print "Demo of %s failed - %s:%s" % (cmd,sys.exc_info()[0], sys.exc_info()[1])
+ return
+ # Otherwise allow the user to select the demo to run
+
+ import pywin.dialogs.list
+ while 1:
+ rc = pywin.dialogs.list.SelectFromLists( "Select a Demo", demos, ['Demo Title'] )
+ if rc is None:
+ break
+ title, cmd = demos[rc]
+ try:
+ exec cmd
+ except:
+ print "Demo of %s failed - %s:%s" % (title,sys.exc_info()[0], sys.exc_info()[1])
+
+if __name__==__main__.__name__:
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ demo()
diff --git a/Pythonwin/pywin/Demos/hiertest.py b/Pythonwin/pywin/Demos/hiertest.py
new file mode 100644
index 0000000000..1d856c1b59
--- /dev/null
+++ b/Pythonwin/pywin/Demos/hiertest.py
@@ -0,0 +1,104 @@
+import win32ui
+import os
+import commctrl
+
+from pywin.tools import hierlist
+from pywin.mfc import docview, window
+
+# directory listbox
+# This has obvious limitations - doesnt track subdirs, etc. Demonstrates
+# simple use of Python code for querying the tree as needed.
+# Only use strings, and lists of strings (from curdir())
+class DirHierList(hierlist.HierList):
+ def __init__(self, root, listBoxID = win32ui.IDC_LIST1):
+ hierlist.HierList.__init__(self, root, win32ui.IDB_HIERFOLDERS, listBoxID)
+ def GetText(self, item):
+ return os.path.basename(item)
+ def GetSubList(self, item):
+ if os.path.isdir(item):
+ ret = map(lambda path, base=item: os.path.join(base, path), os.listdir(item))
+ else:
+ ret = None
+ return ret
+ # if the item is a dir, it is expandable.
+ def IsExpandable(self, item):
+ return os.path.isdir(item)
+ def GetSelectedBitmapColumn(self, item):
+ return self.GetBitmapColumn(item)+6 # Use different color for selection
+
+class TestDocument(docview.Document):
+ def __init__(self, template):
+ docview.Document.__init__(self, template)
+ self.hierlist = hierlist.HierListWithItems(HLIFileDir("\\"), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST)
+
+class HierListView(docview.TreeView):
+ def OnInitialUpdate(self):
+ rc = self._obj_.OnInitialUpdate()
+ self.hierList = self.GetDocument().hierlist
+ self.hierList.HierInit(self.GetParent())
+ self.hierList.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
+ return rc
+
+class HierListFrame(window.MDIChildWnd):
+ pass
+
+def GetTestRoot():
+ tree1 = ('Tree 1',[('Item 1','Item 1 data'),'Item 2',3])
+ tree2 = ('Tree 2',[('Item 2.1','Item 2 data'),'Item 2.2',2.3])
+ return ('Root',[tree1,tree2,'Item 3'])
+
+def demoboth():
+ template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView)
+ template.OpenDocumentFile(None).SetTitle("Hierlist demo")
+
+ demomodeless()
+
+def demomodeless():
+ testList2=DirHierList("\\")
+ dlg=hierlist.HierDialog('hier list test',testList2)
+ dlg.CreateWindow()
+
+def demodlg ():
+ testList2=DirHierList("\\")
+ dlg=hierlist.HierDialog('hier list test',testList2)
+ dlg.DoModal()
+
+def demo():
+ template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView)
+ template.OpenDocumentFile(None).SetTitle("Hierlist demo")
+
+#
+# Demo/Test for HierList items.
+#
+# Easy to make a better directory program.
+#
+class HLIFileDir(hierlist.HierListItem):
+ def __init__( self, filename ):
+ self.filename = filename
+ hierlist.HierListItem.__init__(self)
+ def GetText(self):
+ try:
+ return "%-20s %d bytes" % (os.path.basename(self.filename), os.stat(self.filename)[6])
+ except os.error, details:
+ return "%-20s - %s" % (self.filename, details[1])
+
+ def IsExpandable(self):
+ return os.path.isdir(self.filename)
+ def GetSubList(self):
+ ret = []
+ for newname in os.listdir(self.filename):
+ if newname not in ['.', '..']:
+ ret.append( HLIFileDir( os.path.join(self.filename,newname ) ) )
+ return ret
+
+
+def demohli():
+ template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, hierlist.HierListFrame, hierlist.HierListView)
+ template.OpenDocumentFile(None).SetTitle("Hierlist demo")
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.HaveGoodGUI():
+ demoboth()
+ else:
+ demodlg()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/htmlrichedit.py b/Pythonwin/pywin/Demos/htmlrichedit.py
new file mode 100644
index 0000000000..c216e6ef03
--- /dev/null
+++ b/Pythonwin/pywin/Demos/htmlrichedit.py
@@ -0,0 +1,167 @@
+
+import win32con
+import win32ui
+import win32api
+import sys
+import htmllib
+import string
+import Para
+import urllib
+import urlparse
+import os
+import formatter
+import glob
+
+from pywin.mfc import docview
+
+maskFlags=win32con.CFM_SIZE|win32con.CFM_FACE|win32con.CFM_CHARSET
+styles = { \
+ 'h1' : (maskFlags, 0, 280, 0, 0, 0, 34, 'Arial'), \
+ 'h2' : (maskFlags, 0, 240, 0, 0, 0, 34, 'Arial'), \
+ 'h3' : (maskFlags, 0, 220, 0, 0, 0, 34, 'Arial'), \
+ 'h4' : (maskFlags, 0, 200, 0, 0, 0, 34, 'Arial'), \
+ 'jt' : (win32con.CFM_COLOR,0,0,0,win32api.RGB(0,0,255)), \
+ 'default' : (maskFlags|win32con.CFM_BOLD|win32con.CFM_ITALIC, 0, 200, 0, 0, 0, 34, 'Arial') \
+}
+
+W4GParent=formatter.AbstractWriter
+class RichEditWriter(W4GParent):
+ def __init__(self, richedit):
+ W4GParent.__init__ (self)
+ self.richedit = richedit
+ def close(self):
+ Trace("Writer closing",3)
+ def new_font(self, font):
+ self.SetRTFFont(font)
+ def new_margin(self, margin, level):
+ space = level * 300
+ try:
+ self.richedit.SetParaFormat((win32con.PFM_STARTINDENT|win32con.PFM_OFFSET|win32con.PFM_NUMBERING, 0,0,space,0,0))
+ except win32ui.error:
+ pass # fails occasionaly - maybe if cant do _exactly_ (but seems to do best it can!)
+ def send_label_data(self, data):
+ try:
+ off = self.richedit.GetParaFormat()[3]
+ self.richedit.SetParaFormat((win32con.PFM_NUMBERING|win32con.PFM_OFFSET, win32con.PFN_BULLET, 0, 0, off+500))
+ except win32ui.error:
+ pass
+ def new_spacing(self, spacing):
+ print "new_spacing(%s)" % `spacing`
+ def new_styles(self, styles):
+ print "new_styles(%s)" % `styles`
+ def send_paragraph(self, blankline):
+ self.richedit.ReplaceSel('\r\n')
+ def send_line_break(self):
+ self.richedit.ReplaceSel('\n')
+ def send_hor_rule(self):
+ pass
+# print "send hor"
+
+ def send_flowing_data(self, data):
+ self.richedit.ReplaceSel(data)
+
+ def send_literal_data(self, data):
+ print "send literal", data
+
+ def SetRTFFont( self, font ):
+ if font is None:
+ font = 'default',0,0,0
+
+ face, i, b, tt = font
+ if face is None:
+ cf = (0,0,0,0,0,0,0,"")
+ else:
+ try:
+ cf = styles[face]
+ except KeyError:
+ print "Unknown font - %s - ignored " % `face`
+ return
+ mask = cf[0] | win32con.CFM_ITALIC | win32con.CFM_BOLD
+ effect = cf[1]
+ if i:
+ effect = effect | win32con.CFE_ITALIC
+ if b:
+ effect = effect | win32con.CFE_BOLD
+ if tt:
+ print "have tt - ignoring"
+ cf = mask, effect, cf[2], cf[3], cf[4], cf[5], cf[6], cf[7]
+# print "cf = ", cf
+ self.richedit.SetSelectionCharFormat(cf)
+
+WPParent=htmllib.HTMLParser
+class RichEditParser(WPParent):
+ def __init__(self, formatter, richedit):
+ self.richedit = richedit
+ WPParent.__init__(self, formatter)
+ def close(self):
+ WPParent.close(self)
+
+ def anchor_bgn(self, href, name, type):
+ WPParent.anchor_bgn(self, href, name, type)
+ self.richedit.SetSelectionCharFormat((win32con.CFM_COLOR,0,0,0,win32api.RGB(0,0,255)))
+
+ def anchor_end(self):
+ self.richedit.SetSelectionCharFormat((win32con.CFM_COLOR,0,0,0,win32api.RGB(0,0,0)))
+ # support multiple levels of UL (Unstructured List!)
+ def start_meta(self, attrs):
+ meta_name = meta_value = None
+ for attrname, value in attrs:
+ if attrname == 'name':
+ meta_name = value
+ if attrname == 'value':
+ meta_value = value
+ if meta_name and meta_value:
+ if meta_name == "keywords":
+ print "Meta: ", meta_value
+ def end_meta(self):
+ pass
+
+ def do_img(self, attrs):
+ print "do img - ", attrs
+
+
+class HTMLTemplate(docview.RichEditDocTemplate):
+ def __init__(self):
+ docview.RichEditDocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, docview.RichEditDoc, None, docview.RichEditView)
+ win32ui.GetApp().AddDocTemplate(self)
+
+try:
+ template
+except NameError:
+ template = HTMLTemplate()
+
+def test():
+ import sys, regutil
+ # This should be registered as a HTML file
+ file = regutil.GetRegisteredHelpFile("Main Python Documentation")
+# if sys.argv[1:]: file = sys.argv[1]
+ if not file:
+ print "The main Python HTML page is not registered, and no file was specified..."
+ return
+
+ try:
+ fp = open(file, 'r')
+ except IOError, details:
+ print "Could not open main Python HTML page '%s'" % (file)
+ print details
+ return
+
+ data = fp.read()
+ fp.close()
+ doc = template.OpenDocumentFile()
+ doc.SetTitle("HTML to RichText demo")
+ richedit = doc.GetFirstView()
+ from formatter import DumbWriter
+ w = RichEditWriter(richedit)
+ f = formatter.AbstractFormatter(w)
+ p = RichEditParser(f, richedit)
+ p.feed(data)
+ p.close()
+ doc.SetModifiedFlag(0)
+ return doc
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ test()
+
diff --git a/Pythonwin/pywin/Demos/menutest.py b/Pythonwin/pywin/Demos/menutest.py
new file mode 100644
index 0000000000..18ceeb2bb5
--- /dev/null
+++ b/Pythonwin/pywin/Demos/menutest.py
@@ -0,0 +1,12 @@
+# Run this as a python script, to gray "close" off the edit window system menu.
+from pywin.framework import interact
+import win32con
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ win=interact.edit.currentView.GetParent()
+ menu=win.GetSystemMenu()
+ id=menu.GetMenuItemID(6)
+ menu.EnableMenuItem(id,win32con.MF_BYCOMMAND|win32con.MF_GRAYED)
+ print "The interactive window's 'Close' menu item is now disabled."
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/objdoc.py b/Pythonwin/pywin/Demos/objdoc.py
new file mode 100644
index 0000000000..c455fd8243
--- /dev/null
+++ b/Pythonwin/pywin/Demos/objdoc.py
@@ -0,0 +1,49 @@
+# This is a sample file, and shows the basic framework for using an "Object" based
+# document, rather than a "filename" based document.
+# This is referenced by the Pythonwin .html documentation.
+
+# In the example below, the OpenObject() method is used instead of OpenDocumentFile,
+# and all the core MFC document open functionality is retained.
+
+import win32ui
+from pywin.mfc import docview
+
+class object_template (docview.DocTemplate):
+ def __init__(self):
+ docview.DocTemplate.__init__(self, None, None, None, object_view)
+ def OpenObject(self, object): # Use this instead of OpenDocumentFile.
+ # Look for existing open document
+ for doc in self.GetDocumentList():
+ print "document is ", doc
+ if doc.object is object:
+ doc.GetFirstView().ActivateFrame()
+ return doc
+ # not found - new one.
+ doc = object_document(self, object)
+ frame = self.CreateNewFrame(doc)
+ doc.OnNewDocument()
+ doc.SetTitle(str(object))
+ self.InitialUpdateFrame(frame, doc)
+ return doc
+
+class object_document (docview.Document):
+ def __init__(self, template, object):
+ docview.Document.__init__(self, template)
+ self.object = object
+ def OnOpenDocument (self, name):
+ raise error, "Should not be called if template strings set up correctly"
+ return 0
+
+class object_view (docview.EditView):
+ def OnInitialUpdate (self):
+ self.ReplaceSel("Object is %s" % `self.GetDocument().object`)
+
+def demo ():
+ t = object_template()
+ d = t.OpenObject(win32ui)
+ return (t, d)
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ demo()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/ocx/__init__.py b/Pythonwin/pywin/Demos/ocx/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/Demos/ocx/demoutils.py b/Pythonwin/pywin/Demos/ocx/demoutils.py
new file mode 100644
index 0000000000..818313d115
--- /dev/null
+++ b/Pythonwin/pywin/Demos/ocx/demoutils.py
@@ -0,0 +1,54 @@
+# Utilities for the demos
+
+import sys, win32api, win32con, win32ui
+
+NotScriptMsg = """\
+This demo program is not designed to be run as a Script, but is
+probably used by some other test program. Please try another demo.
+"""
+
+NeedGUIMsg = """\
+This demo program can only be run from inside of Pythonwin
+
+You must start Pythonwin, and select 'Run' from the toolbar or File menu
+"""
+
+
+NeedAppMsg = """\
+This demo program is a 'Pythonwin Application'.
+
+It is more demo code than an example of Pythonwin's capabilities.
+
+To run it, you must execute the command:
+pythonwin.exe /app "%s"
+
+Would you like to execute it now?
+"""
+
+def NotAScript():
+ import win32ui
+ win32ui.MessageBox(NotScriptMsg, "Demos")
+
+def NeedGoodGUI():
+ from pywin.framework.app import HaveGoodGUI
+ rc = HaveGoodGUI()
+ if not rc:
+ win32ui.MessageBox(NeedGUIMsg, "Demos")
+ return rc
+
+def NeedApp():
+ import win32ui
+ rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
+ if rc==win32con.IDYES:
+ try:
+ parent = win32ui.GetMainFrame().GetSafeHwnd()
+ win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
+ except win32api.error, details:
+ win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
+
+
+from pywin.framework.app import HaveGoodGUI
+
+if __name__=='__main__':
+ import demoutils
+ demoutils.NotAScript()
diff --git a/Pythonwin/pywin/Demos/ocx/msoffice.py b/Pythonwin/pywin/Demos/ocx/msoffice.py
new file mode 100644
index 0000000000..8ec42ff4b2
--- /dev/null
+++ b/Pythonwin/pywin/Demos/ocx/msoffice.py
@@ -0,0 +1,127 @@
+# This demo uses some of the Microsoft Office components.
+#
+# It was taken from an MSDN article showing how to embed excel.
+# It is not comlpete yet, but it _does_ show an Excel spreadsheet in a frame!
+#
+
+import win32ui, win32uiole, win32con, regutil
+from pywin.mfc import window, activex, object, docview
+from win32com.client import gencache
+
+#WordModule = gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}', 1033, 8, 0)
+#if WordModule is None:
+# raise ImportError, "Microsoft Word version 8 does not appear to be installed."
+
+
+class OleClientItem(object.CmdTarget):
+ def __init__(self, doc):
+ object.CmdTarget.__init__(self, win32uiole.CreateOleClientItem(doc))
+
+ def OnGetItemPosition(self):
+ # For now return a hard-coded rect.
+ return (10, 10, 210, 210)
+
+ def OnActivate(self):
+ # Allow only one inplace activate item per frame
+ view = self.GetActiveView()
+ item = self.GetDocument().GetInPlaceActiveItem(view)
+ if item is not None and item._obj_ != self._obj_:
+ item.Close()
+ self._obj_.OnActivate()
+
+ def OnChange(self, oleNotification, dwParam):
+ self._obj_.OnChange(oleNotification, dwParam)
+ self.GetDocument().UpdateAllViews(None)
+
+ def OnChangeItemPosition(self, rect):
+ # During in-place activation CEmbed_ExcelCntrItem::OnChangeItemPosition
+ # is called by the server to change the position of the in-place
+ # window. Usually, this is a result of the data in the server
+ # document changing such that the extent has changed or as a result
+ # of in-place resizing.
+ #
+ # The default here is to call the base class, which will call
+ # COleClientItem::SetItemRects to move the item
+ # to the new position.
+ if not self._obj_.OnChangeItemPosition(self, rect):
+ return 0
+
+ # TODO: update any cache you may have of the item's rectangle/extent
+ return 1
+
+class OleDocument(object.CmdTarget):
+ def __init__(self, template):
+ object.CmdTarget.__init__(self, win32uiole.CreateOleDocument(template))
+ self.EnableCompoundFile()
+
+class ExcelView(docview.ScrollView):
+ def OnInitialUpdate(self):
+ self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+
+ self.SetScrollSizes(win32con.MM_TEXT, (100, 100))
+ rc = self._obj_.OnInitialUpdate()
+ self.EmbedExcel()
+ return rc
+
+ def EmbedExcel(self):
+ doc = self.GetDocument()
+ self.clientItem = OleClientItem(doc)
+ self.clientItem.CreateNewItem("Excel.Sheet")
+ self.clientItem.DoVerb(-1, self)
+ doc.UpdateAllViews(None)
+
+ def OnDraw(self, dc):
+ doc = self.GetDocument()
+ pos = doc.GetStartPosition()
+ clientItem, pos = doc.GetNextItem(pos)
+ clientItem.Draw(dc, (10, 10, 210, 210) )
+
+ # Special handling of OnSetFocus and OnSize are required for a container
+ # when an object is being edited in-place.
+ def OnSetFocus(self, msg):
+ item = self.GetDocument().GetInPlaceActiveItem(self)
+ if item is not None and item.GetItemState()==win32uiole.COleClientItem_activeUIState:
+ wnd = item.GetInPlaceWindow()
+ if wnd is not None:
+ wnd.SetFocus()
+ return 0 # Dont get the base version called.
+ return 1 # Call the base version.
+
+ def OnSize (self, params):
+ item = self.GetDocument().GetInPlaceActiveItem(self)
+ if item is not None:
+ item.SetItemRects()
+ return 1 # do call the base!
+
+class OleTemplate(docview.DocTemplate):
+ def __init__(self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None):
+ if MakeDocument is None: MakeDocument = OleDocument
+ if MakeView is None: MakeView = ExcelView
+ docview.DocTemplate.__init__(self, resourceId, MakeDocument, MakeFrame, MakeView)
+
+class WordFrame(window.MDIChildWnd):
+ def __init__(self, doc = None):
+ self._obj_ = win32ui.CreateMDIChild()
+ self._obj_.AttachObject(self)
+ # Dont call base class doc/view version...
+ def Create(self, title, rect = None, parent = None):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
+ self._obj_.CreateWindow(None, title, style, rect, parent)
+
+ rect = self.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.ocx = MyWordControl()
+ self.ocx.CreateControl("Microsoft Word", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 20000)
+
+def Demo():
+ import sys, win32api
+ docName = None
+ if len(sys.argv)>1:
+ docName = win32api.GetFullPathName(sys.argv[1])
+ OleTemplate().OpenDocumentFile(None)
+# f = WordFrame(docName)
+# f.Create("Microsoft Office")
+
+if __name__=='__main__':
+ Demo()
diff --git a/Pythonwin/pywin/Demos/ocx/ocxserialtest.py b/Pythonwin/pywin/Demos/ocx/ocxserialtest.py
new file mode 100644
index 0000000000..47f4cb710c
--- /dev/null
+++ b/Pythonwin/pywin/Demos/ocx/ocxserialtest.py
@@ -0,0 +1,101 @@
+# ocxserialtest.py
+#
+# Sample that uses the mscomm OCX to talk to a serial
+# device.
+
+# Very simple - queries a modem for ATI responses
+
+import win32ui, win32uiole
+import win32con
+from pywin.mfc import dialog, activex
+from win32com.client import gencache
+import pythoncom
+
+SERIAL_SETTINGS = '19200,n,8,1'
+SERIAL_PORT = 2
+
+win32ui.DoWaitCursor(1)
+serialModule = gencache.EnsureModule("{648A5603-2C6E-101B-82B6-000000000014}", 0, 1, 1)
+win32ui.DoWaitCursor(0)
+if serialModule is None:
+ raise ImportError, "MS COMM Control does not appear to be installed on the PC"
+
+
+def MakeDlgTemplate():
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP \
+ | win32con.WS_VISIBLE | win32con.WS_CAPTION \
+ | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE
+ dlg = [ ["Very Basic Terminal",
+ (0, 0, 350, 180), style, None, (8, "MS Sans Serif")], ]
+ s = win32con.WS_TABSTOP | cs
+ dlg.append(["RICHEDIT", None, 132, (5, 5, 340, 170),s | win32con.ES_WANTRETURN | win32con.ES_MULTILINE | win32con.ES_AUTOVSCROLL | win32con.WS_VSCROLL])
+ return dlg
+
+
+####################################
+#
+# Serial Control
+#
+class MySerialControl(activex.Control, serialModule.MSComm):
+ def __init__(self, parent):
+ activex.Control.__init__(self)
+ serialModule.MSComm.__init__(self)
+ self.parent = parent
+ def OnComm(self):
+ self.parent.OnComm()
+
+class TestSerDialog(dialog.Dialog):
+ def __init__(self, *args):
+ apply( dialog.Dialog.__init__, (self,)+args )
+ self.olectl = None
+ def OnComm(self):
+ event = self.olectl.CommEvent
+ if event == serialModule.OnCommConstants.comEvReceive:
+ self.editwindow.ReplaceSel(self.olectl.Input)
+
+ def OnKey(self, key):
+ if self.olectl:
+ self.olectl.Output = chr(key)
+
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.editwindow = self.GetDlgItem(132)
+ self.editwindow.HookAllKeyStrokes(self.OnKey)
+
+ self.olectl = MySerialControl(self)
+ try:
+ self.olectl.CreateControl("OCX",
+ win32con.WS_TABSTOP | win32con.WS_VISIBLE,
+ (7,43,500,300), self._obj_, 131)
+ except win32ui.error:
+ self.MessageBox("The Serial Control could not be created")
+ self.olectl = None
+ self.EndDialog(win32con.IDCANCEL)
+ if self.olectl:
+ self.olectl.Settings = SERIAL_SETTINGS
+ self.olectl.CommPort = SERIAL_PORT
+ self.olectl.RThreshold = 1
+ try:
+ self.olectl.PortOpen = 1
+ except pythoncom.com_error, details:
+ print "Could not open the specified serial port - %s" % (details[2][2])
+ self.EndDialog(win32con.IDCANCEL)
+ return rc
+
+ def OnDestroy(self, msg):
+ if self.olectl:
+ try:
+ self.olectl.PortOpen = 0
+ except pythoncom.com_error, details:
+ print "Error closing port - %s" % (details[2][2])
+ return dialog.Dialog.OnDestroy(self, msg)
+
+def test():
+ d = TestSerDialog(MakeDlgTemplate() )
+ d.DoModal()
+
+if __name__ == "__main__":
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ test()
diff --git a/Pythonwin/pywin/Demos/ocx/ocxtest.py b/Pythonwin/pywin/Demos/ocx/ocxtest.py
new file mode 100644
index 0000000000..b1fa4c4d93
--- /dev/null
+++ b/Pythonwin/pywin/Demos/ocx/ocxtest.py
@@ -0,0 +1,186 @@
+# OCX Tester for Pythonwin
+#
+# This file _is_ ready to run. All that is required is that the OCXs being tested
+# are installed on your machine.
+#
+# The .py files behind the OCXs will be automatically generated and imported.
+
+from pywin.mfc import dialog, window, activex
+import win32ui, win32uiole
+import win32con
+import os, sys, win32api, glob
+from win32com.client import gencache
+
+
+def MakeDlgTemplate():
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE
+ dlg = [ ["OCX Demos", (0, 0, 350, 350), style, None, (8, "MS Sans Serif")], ]
+ s = win32con.WS_TABSTOP | cs
+# dlg.append([131, None, 130, (5, 40, 110, 48),
+# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
+# dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP])
+
+ dlg.append([128, "About", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
+ s = win32con.BS_PUSHBUTTON | s
+ dlg.append([128, "Close", win32con.IDCANCEL, (124, 22, 50, 14), s])
+
+ return dlg
+
+####################################
+#
+# Calendar test code
+#
+
+def GetTestCalendarClass():
+ global calendarParentModule
+ win32ui.DoWaitCursor(1)
+ calendarParentModule = gencache.EnsureModule("{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0)
+ win32ui.DoWaitCursor(0)
+ if calendarParentModule is None:
+ return None
+
+ class TestCalDialog(dialog.Dialog):
+ def OnInitDialog(self):
+
+ class MyCal(activex.Control, calendarParentModule.Calendar):
+ def OnAfterUpdate(self):
+ print "OnAfterUpdate"
+ def OnClick(self):
+ print "OnClick"
+ def OnDblClick(self):
+ print "OnDblClick"
+ def OnKeyDown(self, KeyCode, Shift):
+ print "OnKeyDown", KeyCode, Shift
+ def OnKeyPress(self, KeyAscii):
+ print "OnKeyPress", KeyAscii
+ def OnKeyUp(self, KeyCode, Shift):
+ print "OnKeyUp", KeyCode, Shift
+ def OnBeforeUpdate(self, Cancel):
+ print "OnBeforeUpdate", Cancel
+ def OnNewMonth(self):
+ print "OnNewMonth"
+ def OnNewYear(self):
+ print "OnNewYear"
+
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.olectl = MyCal()
+ try:
+ self.olectl.CreateControl("OCX", win32con.WS_TABSTOP | win32con.WS_VISIBLE, (7,43,500,300), self._obj_, 131)
+ except win32ui.error:
+ self.MessageBox("The Calendar Control could not be created")
+ self.olectl = None
+ self.EndDialog(win32con.IDCANCEL)
+
+ return rc
+ def OnOK(self):
+ self.olectl.AboutBox()
+
+ return TestCalDialog
+
+
+####################################
+#
+# Video Control
+#
+def GetTestVideoModule():
+ global videoControlModule, videoControlFileName
+ win32ui.DoWaitCursor(1)
+ videoControlModule = gencache.EnsureModule("{05589FA0-C356-11CE-BF01-00AA0055595A}", 0, 2, 0)
+ win32ui.DoWaitCursor(0)
+ if videoControlModule is None:
+ return None
+ fnames = glob.glob(os.path.join(win32api.GetWindowsDirectory(), "*.avi"))
+ if not fnames:
+ print "No AVI files available in system directory"
+ return None
+ videoControlFileName = fnames[0]
+ return videoControlModule
+
+def GetTestVideoDialogClass():
+ if GetTestVideoModule() is None:
+ return None
+ class TestVideoDialog(dialog.Dialog):
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ try:
+ self.olectl = activex.MakeControlInstance(videoControlModule.ActiveMovie)
+ self.olectl.CreateControl("", win32con.WS_TABSTOP | win32con.WS_VISIBLE, (7,43,500,300), self._obj_, 131)
+ except win32ui.error:
+ self.MessageBox("The Video Control could not be created")
+ self.olectl = None
+ self.EndDialog(win32con.IDCANCEL)
+ return
+
+ self.olectl.FileName = videoControlFileName
+# self.olectl.Run()
+ return rc
+ def OnOK(self):
+ self.olectl.AboutBox()
+ return TestVideoDialog
+
+###############
+#
+# An OCX in an MDI Frame
+#
+class OCXFrame(window.MDIChildWnd):
+ def __init__(self):
+ pass # Dont call base class doc/view version...
+ def Create(self, controlClass, title, rect = None, parent = None):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
+ self._obj_ = win32ui.CreateMDIChild()
+ self._obj_.AttachObject(self)
+ self._obj_.CreateWindow(None, title, style, rect, parent)
+
+ rect = self.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.ocx = controlClass()
+ self.ocx.CreateControl("", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000)
+
+def MDITest():
+ calendarParentModule = gencache.EnsureModule("{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0)
+ class MyCal(activex.Control, calendarParentModule.Calendar):
+ def OnAfterUpdate(self):
+ print "OnAfterUpdate"
+ def OnClick(self):
+ print "OnClick"
+
+ f = OCXFrame()
+ f.Create(MyCal, "Calendar Test")
+
+
+def test1():
+ klass = GetTestCalendarClass()
+ if klass is None:
+ print "Can not test the MSAccess Calendar control - it does not appear to be installed"
+ return
+
+ d = klass(MakeDlgTemplate() )
+ d.DoModal()
+
+def test2():
+ klass = GetTestVideoDialogClass()
+ if klass is None:
+ print "Can not test the Video OCX - it does not appear to be installed,"
+ print "or no AVI files can be found."
+ return
+ d = klass(MakeDlgTemplate() )
+ d.DoModal()
+ d = None
+
+def test3():
+ d = TestCOMMDialog(MakeDlgTemplate() )
+ d.DoModal()
+ d = None
+
+def testall():
+ test1()
+ test2()
+
+def demo():
+ testall()
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ testall()
diff --git a/Pythonwin/pywin/Demos/ocx/webbrowser.py b/Pythonwin/pywin/Demos/ocx/webbrowser.py
new file mode 100644
index 0000000000..83af271995
--- /dev/null
+++ b/Pythonwin/pywin/Demos/ocx/webbrowser.py
@@ -0,0 +1,54 @@
+# This demo uses the IE4 Web Browser control.
+
+# It catches an "OnNavigate" event, and updates the frame title.
+# (event stuff by Neil Hodgson)
+
+import win32ui, win32con, win32api, regutil
+from pywin.mfc import window, activex
+from win32com.client import gencache
+import sys
+
+WebBrowserModule = gencache.EnsureModule("{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
+if WebBrowserModule is None:
+ raise ImportError, "IE4 does not appear to be installed."
+
+class MyWebBrowser(activex.Control, WebBrowserModule.WebBrowser):
+ def OnBeforeNavigate2(self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel):
+ self.GetParent().OnNavigate(URL)
+ #print "BeforeNavigate2", pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel
+
+class BrowserFrame(window.MDIChildWnd):
+ def __init__(self, url = None):
+ if url is None:
+ self.url = regutil.GetRegisteredHelpFile("Main Python Documentation")
+ else:
+ self.url = url
+ pass # Dont call base class doc/view version...
+ def Create(self, title, rect = None, parent = None):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
+ self._obj_ = win32ui.CreateMDIChild()
+ self._obj_.AttachObject(self)
+ self._obj_.CreateWindow(None, title, style, rect, parent)
+ rect = self.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.ocx = MyWebBrowser()
+ self.ocx.CreateControl("Web Browser", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000)
+ self.ocx.Navigate(self.url)
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+ def OnSize (self, params):
+ rect = self.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.ocx.SetWindowPos(0, rect, 0)
+ def OnNavigate(self, url):
+ title = "Web Browser - %s" % (url,)
+ self.SetWindowText(title)
+
+def Demo():
+ url = None
+ if len(sys.argv)>1:
+ url = win32api.GetFullPathName(sys.argv[1])
+ f = BrowserFrame(url)
+ f.Create("Web Browser")
+
+if __name__=='__main__':
+ Demo()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/openGLDemo.py b/Pythonwin/pywin/Demos/openGLDemo.py
new file mode 100644
index 0000000000..70b434773f
--- /dev/null
+++ b/Pythonwin/pywin/Demos/openGLDemo.py
@@ -0,0 +1,351 @@
+# Ported from the win32 and MFC OpenGL Samples.
+
+from pywin.mfc import docview
+
+from OpenGL.GL import *
+from OpenGL.GLU import *
+import win32con
+import win32ui
+import win32api
+import timer
+
+PFD_TYPE_RGBA = 0
+PFD_TYPE_COLORINDEX = 1
+PFD_MAIN_PLANE = 0
+PFD_OVERLAY_PLANE = 1
+PFD_UNDERLAY_PLANE = (-1)
+PFD_DOUBLEBUFFER = 0x00000001
+PFD_STEREO = 0x00000002
+PFD_DRAW_TO_WINDOW = 0x00000004
+PFD_DRAW_TO_BITMAP = 0x00000008
+PFD_SUPPORT_GDI = 0x00000010
+PFD_SUPPORT_OPENGL = 0x00000020
+PFD_GENERIC_FORMAT = 0x00000040
+PFD_NEED_PALETTE = 0x00000080
+PFD_NEED_SYSTEM_PALETTE = 0x00000100
+PFD_SWAP_EXCHANGE = 0x00000200
+PFD_SWAP_COPY = 0x00000400
+PFD_SWAP_LAYER_BUFFERS = 0x00000800
+PFD_GENERIC_ACCELERATED = 0x00001000
+PFD_DEPTH_DONTCARE = 0x20000000
+PFD_DOUBLEBUFFER_DONTCARE = 0x40000000
+PFD_STEREO_DONTCARE = 0x80000000
+
+
+threeto8 = [0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377]
+twoto8 = [0, 0x55, 0xaa, 0xff]
+oneto8 = [0, 255]
+
+def ComponentFromIndex(i, nbits, shift):
+ # val = (unsigned char) (i >> shift);
+ val = (i >> shift) & 0xF;
+ if nbits==1:
+ val = val & 0x1
+ return oneto8[val]
+ elif nbits==2:
+ val = val & 0x3
+ return twoto8[val]
+ elif nbits==3:
+ val = val & 0x7
+ return threeto8[val]
+ else:
+ return 0;
+
+OpenGLViewParent=docview.ScrollView
+class OpenGLView(OpenGLViewParent):
+ def PreCreateWindow(self, cc):
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+ # An OpenGL window must be created with the following flags and must not
+ # include CS_PARENTDC for the class style. Refer to SetPixelFormat
+ # documentation in the "Comments" section for further information.
+ style = cc[5]
+ style = style | win32con.WS_CLIPSIBLINGS | win32con.WS_CLIPCHILDREN
+ cc = cc[0], cc[1], cc[2], cc[3], cc[4], style, cc[6], cc[7], cc[8]
+ cc = self._obj_.PreCreateWindow(cc)
+ return cc
+
+ def OnSize (self, params):
+ lParam = params[3]
+ cx = win32api.LOWORD(lParam)
+ cy = win32api.HIWORD(lParam)
+ glViewport(0, 0, cx, cy)
+
+ if self.oldrect[2] > cx or self.oldrect[3] > cy:
+ self.RedrawWindow()
+
+ self.OnSizeChange(cx, cy)
+
+ self.oldrect = self.oldrect[0], self.oldrect[1], cx, cy
+
+ def OnInitialUpdate(self):
+ self.SetScaleToFitSize((100,100)) # or SetScrollSizes() - A Pythonwin requirement
+ return self._obj_.OnInitialUpdate()
+# return rc
+
+ def OnCreate(self, cs):
+ self.oldrect = self.GetClientRect()
+ self._InitContexts()
+ self.Init()
+
+ def OnDestroy(self, msg):
+ self.Term()
+ self._DestroyContexts()
+ return OpenGLViewParent.OnDestroy(self, msg)
+
+
+ def OnDraw(self, dc):
+ self.DrawScene()
+
+ def OnEraseBkgnd(self, dc):
+ return 1
+
+ # The OpenGL helpers
+ def _SetupPixelFormat(self):
+ dc = self.dc.GetSafeHdc()
+ pfd = CreatePIXELFORMATDESCRIPTOR()
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER
+ pfd.iPixelType = PFD_TYPE_RGBA
+ pfd.cColorBits = 24
+ pfd.cDepthBits = 32
+ pfd.iLayerType = PFD_MAIN_PLANE
+ pixelformat = ChoosePixelFormat(dc, pfd)
+ SetPixelFormat(dc, pixelformat, pfd)
+ self._CreateRGBPalette()
+
+ def _CreateRGBPalette(self):
+ dc = self.dc.GetSafeHdc()
+ n = GetPixelFormat(dc)
+ pfd = DescribePixelFormat(dc, n)
+ if pfd.dwFlags & PFD_NEED_PALETTE:
+ n = 1 << pfd.cColorBits
+ pal = []
+ for i in range(n):
+ this = ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift), \
+ ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift), \
+ ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift), \
+ 0
+ pal.append(this)
+ hpal = win32ui.CreatePalette(pal)
+ self.dc.SelectPalette(hpal, 0)
+ self.dc.RealizePalette()
+
+ def _InitContexts(self):
+ self.dc = self.GetDC()
+ self._SetupPixelFormat()
+ hrc = wglCreateContext(self.dc.GetSafeHdc())
+ wglMakeCurrent(self.dc.GetSafeHdc(), hrc)
+
+ def _DestroyContexts(self):
+ hrc = wglGetCurrentContext()
+ wglMakeCurrent(0, 0)
+ if hrc: wglDeleteContext(hrc)
+
+ # The methods to support OpenGL
+ def DrawScene(self):
+ assert 0, "You must override this method"
+
+ def Init(self):
+ assert 0, "You must override this method"
+
+ def OnSizeChange(self, cx, cy):
+ pass
+
+ def Term(self):
+ pass
+
+
+class TestView(OpenGLView):
+
+ def OnSizeChange(self, right, bottom):
+ glClearColor( 0.0, 0.0, 0.0, 1.0 );
+ glClearDepth( 1.0 );
+ glEnable(GL_DEPTH_TEST)
+
+ glMatrixMode( GL_PROJECTION )
+ if bottom:
+ aspect = right / bottom
+ else:
+ aspect = 0 # When window created!
+ glLoadIdentity()
+ gluPerspective( 45.0, aspect, 3.0, 7.0 )
+ glMatrixMode( GL_MODELVIEW )
+
+ near_plane = 3.0;
+ far_plane = 7.0;
+ maxObjectSize = 3.0;
+ self.radius = near_plane + maxObjectSize/2.0;
+
+
+ def Init(self):
+ pass
+
+ def DrawScene(self):
+ glClearColor(0.0, 0.0, 0.0, 1.0)
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
+
+ glPushMatrix()
+ glTranslatef(0.0, 0.0, -self.radius);
+
+ self._DrawCone()
+
+ self._DrawPyramid()
+
+ glPopMatrix()
+ glFinish()
+
+ SwapBuffers( wglGetCurrentDC() )
+
+ def _DrawCone(self):
+ glColor3f(0.0, 1.0, 0.0)
+
+ glPushMatrix()
+ glTranslatef(-1.0, 0.0, 0.0);
+ quadObj = gluNewQuadric();
+ gluQuadricDrawStyle(quadObj, GLU_FILL);
+ gluQuadricNormals(quadObj, GLU_SMOOTH);
+ gluCylinder(quadObj, 1.0, 0.0, 1.0, 20, 10);
+# gluDeleteQuadric(quadObj);
+ glPopMatrix();
+
+ def _DrawPyramid(self):
+ glPushMatrix()
+ glTranslatef(1.0, 0.0, 0.0)
+ glBegin(GL_TRIANGLE_FAN)
+ glColor3f(1.0, 0.0, 0.0)
+ glVertex3f(0.0, 1.0, 0.0)
+ glColor3f(0.0, 1.0, 0.0)
+ glVertex3f(-1.0, 0.0, 0.0)
+ glColor3f(0.0, 0.0, 1.0)
+ glVertex3f(0.0, 0.0, 1.0)
+ glColor3f(0.0, 1.0, 0.0)
+ glVertex3f(1.0, 0.0, 0.0)
+ glEnd()
+ glPopMatrix()
+
+class CubeView(OpenGLView):
+ def OnSizeChange(self, right, bottom):
+ glClearColor( 0.0, 0.0, 0.0, 1.0 );
+ glClearDepth( 1.0 );
+ glEnable(GL_DEPTH_TEST)
+
+ glMatrixMode( GL_PROJECTION )
+ if bottom:
+ aspect = right / bottom
+ else:
+ aspect = 0 # When window created!
+ glLoadIdentity()
+ gluPerspective( 45.0, aspect, 3.0, 7.0 )
+ glMatrixMode( GL_MODELVIEW )
+
+ near_plane = 3.0;
+ far_plane = 7.0;
+ maxObjectSize = 3.0;
+ self.radius = near_plane + maxObjectSize/2.0;
+
+ def Init(self):
+ self.busy = 0
+ self.wAngleY = 10.0
+ self.wAngleX = 1.0
+ self.wAngleZ = 5.0
+ self.timerid = timer.set_timer (150, self.OnTimer)
+
+ def OnTimer(self, id, timeVal):
+ self.DrawScene()
+
+ def Term(self):
+ timer.kill_timer(self.timerid)
+
+ def DrawScene(self):
+ if self.busy: return
+ self.busy = 1
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+
+ glTranslatef(0.0, 0.0, -self.radius);
+ glRotatef(self.wAngleX, 1.0, 0.0, 0.0);
+ glRotatef(self.wAngleY, 0.0, 1.0, 0.0);
+ glRotatef(self.wAngleZ, 0.0, 0.0, 1.0);
+
+ self.wAngleX = self.wAngleX + 1.0
+ self.wAngleY = self.wAngleY + 10.0
+ self.wAngleZ = self.wAngleZ + 5.0;
+
+
+ glBegin(GL_QUAD_STRIP);
+ glColor3f(1.0, 0.0, 1.0);
+ glVertex3f(-0.5, 0.5, 0.5);
+
+ glColor3f(1.0, 0.0, 0.0);
+ glVertex3f(-0.5, -0.5, 0.5);
+
+ glColor3f(1.0, 1.0, 1.0);
+ glVertex3f(0.5, 0.5, 0.5);
+
+ glColor3f(1.0, 1.0, 0.0);
+ glVertex3f(0.5, -0.5, 0.5);
+
+ glColor3f(0.0, 1.0, 1.0);
+ glVertex3f(0.5, 0.5, -0.5);
+
+ glColor3f(0.0, 1.0, 0.0);
+ glVertex3f(0.5, -0.5, -0.5);
+
+ glColor3f(0.0, 0.0, 1.0);
+ glVertex3f(-0.5, 0.5, -0.5);
+
+ glColor3f(0.0, 0.0, 0.0);
+ glVertex3f(-0.5, -0.5, -0.5);
+
+ glColor3f(1.0, 0.0, 1.0);
+ glVertex3f(-0.5, 0.5, 0.5);
+
+ glColor3f(1.0, 0.0, 0.0);
+ glVertex3f(-0.5, -0.5, 0.5);
+
+ glEnd();
+
+ glBegin(GL_QUADS);
+ glColor3f(1.0, 0.0, 1.0);
+ glVertex3f(-0.5, 0.5, 0.5);
+
+ glColor3f(1.0, 1.0, 1.0);
+ glVertex3f(0.5, 0.5, 0.5);
+
+ glColor3f(0.0, 1.0, 1.0);
+ glVertex3f(0.5, 0.5, -0.5);
+
+ glColor3f(0.0, 0.0, 1.0);
+ glVertex3f(-0.5, 0.5, -0.5);
+ glEnd();
+
+ glBegin(GL_QUADS);
+ glColor3f(1.0, 0.0, 0.0);
+ glVertex3f(-0.5, -0.5, 0.5);
+
+ glColor3f(1.0, 1.0, 0.0);
+ glVertex3f(0.5, -0.5, 0.5);
+
+ glColor3f(0.0, 1.0, 0.0);
+ glVertex3f(0.5, -0.5, -0.5);
+
+ glColor3f(0.0, 0.0, 0.0);
+ glVertex3f(-0.5, -0.5, -0.5);
+ glEnd();
+
+ glPopMatrix();
+
+ glFinish();
+ SwapBuffers(wglGetCurrentDC());
+
+ self.busy = 0
+
+def test():
+ template = docview.DocTemplate(None, None, None, CubeView )
+# template = docview.DocTemplate(None, None, None, TestView )
+ template.OpenDocumentFile(None)
+
+if __name__=='__main__':
+ test()
diff --git a/Pythonwin/pywin/Demos/progressbar.py b/Pythonwin/pywin/Demos/progressbar.py
new file mode 100644
index 0000000000..869362d3d7
--- /dev/null
+++ b/Pythonwin/pywin/Demos/progressbar.py
@@ -0,0 +1,91 @@
+#
+# Progress bar control example
+#
+# PyCProgressCtrl encapsulates the MFC CProgressCtrl class. To use it,
+# you:
+#
+# - Create the control with win32ui.CreateProgressCtrl()
+# - Create the control window with PyCProgressCtrl.CreateWindow()
+# - Initialize the range if you want it to be other than (0, 100) using
+# PyCProgressCtrl.SetRange()
+# - Either:
+# - Set the step size with PyCProgressCtrl.SetStep(), and
+# - Increment using PyCProgressCtrl.StepIt()
+# or:
+# - Set the amount completed using PyCProgressCtrl.SetPos()
+#
+# Example and progress bar code courtesy of KDL Technologies, Ltd., Hong Kong SAR, China.
+#
+
+from pywin.mfc import dialog
+import win32ui
+import win32con
+
+def MakeDlgTemplate():
+ style = (win32con.DS_MODALFRAME |
+ win32con.WS_POPUP |
+ win32con.WS_VISIBLE |
+ win32con.WS_CAPTION |
+ win32con.WS_SYSMENU |
+ win32con.DS_SETFONT)
+ cs = (win32con.WS_CHILD |
+ win32con.WS_VISIBLE)
+
+ w = 215
+ h = 36
+
+ dlg = [["Progress bar control example",
+ (0, 0, w, h),
+ style,
+ None,
+ (8, "MS Sans Serif")],
+ ]
+
+ s = win32con.WS_TABSTOP | cs
+
+ dlg.append([128,
+ "Tick",
+ win32con.IDOK,
+ (10, h - 18, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
+
+ dlg.append([128,
+ "Cancel",
+ win32con.IDCANCEL,
+ (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
+
+ return dlg
+
+class TestDialog(dialog.Dialog):
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.pbar = win32ui.CreateProgressCtrl()
+ self.pbar.CreateWindow (win32con.WS_CHILD |
+ win32con.WS_VISIBLE,
+ (10, 10, 310, 24),
+ self, 1001)
+ # self.pbar.SetStep (5)
+ self.progress = 0
+ self.pincr = 5
+ return rc
+
+ def OnOK(self):
+ # NB: StepIt wraps at the end if you increment past the upper limit!
+ # self.pbar.StepIt()
+ self.progress = self.progress + self.pincr
+ if self.progress > 100:
+ self.progress = 100
+ if self.progress <= 100:
+ self.pbar.SetPos(self.progress)
+
+def demo(modal = 0):
+ d = TestDialog (MakeDlgTemplate())
+ if modal:
+ d.DoModal()
+ else:
+ d.CreateWindow ()
+
+if __name__=='__main__':
+ demo(1)
+
+# $Header$
+
diff --git a/Pythonwin/pywin/Demos/readme.txt b/Pythonwin/pywin/Demos/readme.txt
new file mode 100644
index 0000000000..1917fd797c
--- /dev/null
+++ b/Pythonwin/pywin/Demos/readme.txt
@@ -0,0 +1,3 @@
+For a good example of all the demos, start Pythonwin, and run the
+script guidemo.py
+
diff --git a/Pythonwin/pywin/Demos/splittst.py b/Pythonwin/pywin/Demos/splittst.py
new file mode 100644
index 0000000000..62310eb5ee
--- /dev/null
+++ b/Pythonwin/pywin/Demos/splittst.py
@@ -0,0 +1,72 @@
+import win32ui
+import win32con
+import fontdemo
+from pywin.mfc import window, docview
+import commctrl
+
+# derive from CMDIChild. This does much work for us.
+
+class SplitterFrame(window.MDIChildWnd):
+ def __init__(self):
+ # call base CreateFrame
+ self.images = None
+ window.MDIChildWnd.__init__(self)
+
+ def OnCreateClient(self, cp, context):
+ splitter = win32ui.CreateSplitter()
+ doc = context.doc
+ frame_rect = self.GetWindowRect()
+ size = ((frame_rect[2] - frame_rect[0]),
+ (frame_rect[3] - frame_rect[1])/2)
+ sub_size = (size[0]/2, size[1])
+ splitter.CreateStatic (self, 2, 1)
+ self.v1 = win32ui.CreateEditView(doc)
+ self.v2 = fontdemo.FontView(doc)
+ # CListControl view
+ self.v3 = win32ui.CreateListView(doc)
+ sub_splitter = win32ui.CreateSplitter()
+ # pass "splitter" so each view knows how to get to the others
+ sub_splitter.CreateStatic (splitter, 1, 2)
+ sub_splitter.CreateView(self.v1, 0, 0, (sub_size))
+ sub_splitter.CreateView(self.v2, 0, 1, (0,0)) # size ignored.
+ splitter.SetRowInfo(0, size[1] ,0)
+ splitter.CreatePane (self.v3, 1, 0, (0,0)) # size ignored.
+ # Setup items in the imagelist
+ self.images = win32ui.CreateImageList(32,32,1,5,5)
+ self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_MAINFRAME))
+ self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_PYTHONCONTYPE))
+ self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_TEXTTYPE))
+ self.v3.SetImageList(self.images, commctrl.LVSIL_NORMAL)
+ self.v3.InsertItem(0, "Icon 1", 0)
+ self.v3.InsertItem(0, "Icon 2", 1)
+ self.v3.InsertItem(0, "Icon 3", 2)
+# self.v3.Arrange(commctrl.LVA_DEFAULT) Hmmm - win95 aligns left always???
+ return 1
+ def OnDestroy(self, msg):
+ window.MDIChildWnd.OnDestroy(self, msg)
+ if self.images:
+ self.images.DeleteImageList()
+ self.images = None
+
+ def InitialUpdateFrame(self, doc, makeVisible):
+ self.v1.ReplaceSel("Hello from Edit Window 1")
+ self.v1.SetModifiedFlag(0)
+
+class SampleTemplate(docview.DocTemplate):
+ def __init__(self):
+ docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None)
+ def InitialUpdateFrame(self, frame, doc, makeVisible):
+# print "frame is ", frame, frame._obj_
+# print "doc is ", doc, doc._obj_
+ self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
+ frame.InitialUpdateFrame(doc, makeVisible)
+
+def demo():
+ template = SampleTemplate()
+ doc=template.OpenDocumentFile(None)
+ doc.SetTitle("Splitter Demo")
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ demo()
\ No newline at end of file
diff --git a/Pythonwin/pywin/Demos/threadedgui.py b/Pythonwin/pywin/Demos/threadedgui.py
new file mode 100644
index 0000000000..ccd7923f3b
--- /dev/null
+++ b/Pythonwin/pywin/Demos/threadedgui.py
@@ -0,0 +1,171 @@
+# Demo of using just windows, without documents and views.
+
+# Also demo of a GUI thread, pretty much direct from the MFC C++ sample MTMDI.
+
+import win32ui
+import win32con
+import win32api
+import timer
+
+from pywin.mfc import window, docview, thread
+
+
+WM_USER_PREPARE_TO_CLOSE = win32con.WM_USER + 32
+
+# font is a dictionary in which the following elements matter:
+# (the best matching font to supplied parameters is returned)
+# name string name of the font as known by Windows
+# size point size of font in logical units
+# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
+# italic boolean; true if set to anything but None
+# underline boolean; true if set to anything but None
+
+# This window is a child window of a frame. It is not the frame window itself.
+class FontWindow(window.Wnd):
+ def __init__(self, text = 'Python Rules!'):
+ window.Wnd.__init__(self)
+ self.text = text
+ self.index = 0
+ self.incr = 1
+ self.width = self.height = 0
+ self.ChangeAttributes()
+ # set up message handlers
+
+ def Create(self, title, style, rect, parent):
+ classStyle = win32con.CS_HREDRAW | win32con.CS_VREDRAW
+ className = win32ui.RegisterWndClass(classStyle, 0, win32con.COLOR_WINDOW+1, 0)
+ self._obj_ = win32ui.CreateWnd()
+ self._obj_.AttachObject(self)
+ self._obj_.CreateWindow(className, title, style, rect, parent, win32ui.AFX_IDW_PANE_FIRST)
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+ self.HookMessage (self.OnPrepareToClose, WM_USER_PREPARE_TO_CLOSE)
+ self.HookMessage (self.OnDestroy, win32con.WM_DESTROY)
+ self.timerid = timer.set_timer (100, self.OnTimer)
+ self.InvalidateRect()
+
+ def OnDestroy (self, msg):
+ timer.kill_timer(self.timerid)
+
+ def OnTimer(self, id, timeVal):
+ self.index = self.index + self.incr
+ if self.index > len(self.text):
+ self.incr = -1
+ self.index = len(self.text)
+ elif self.index < 0:
+ self.incr = 1
+ self.index = 0
+ self.InvalidateRect()
+
+ def OnPaint (self):
+# print "Paint message from thread", win32api.GetCurrentThreadId()
+ dc, paintStruct = self.BeginPaint()
+ self.OnPrepareDC(dc, None)
+
+ if (self.width == 0 and self.height == 0):
+ left, top, right, bottom = self.GetClientRect()
+ self.width = right - left
+ self.height = bottom - top
+ x, y = self.width / 2, self.height / 2
+ dc.TextOut (x, y, self.text[:self.index])
+ self.EndPaint(paintStruct)
+
+ def ChangeAttributes(self):
+ font_spec = {'name':'Arial', 'height':42}
+ self.font = win32ui.CreateFont (font_spec)
+
+ def OnPrepareToClose(self, params):
+ self.DestroyWindow()
+
+ def OnSize (self, params):
+ lParam = params[3]
+ self.width = win32api.LOWORD(lParam)
+ self.height = win32api.HIWORD(lParam)
+
+ def OnPrepareDC (self, dc, printinfo):
+ # Set up the DC for forthcoming OnDraw call
+ dc.SetTextColor (win32api.RGB(0,0,255))
+ dc.SetBkColor (win32api.GetSysColor (win32con.COLOR_WINDOW))
+ dc.SelectObject (self.font)
+ dc.SetTextAlign (win32con.TA_CENTER | win32con.TA_BASELINE)
+
+class FontFrame(window.MDIChildWnd):
+ def __init__(self):
+ pass # Dont call base class doc/view version...
+ def Create(self, title, rect = None, parent = None):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
+ self._obj_ = win32ui.CreateMDIChild()
+ self._obj_.AttachObject(self)
+ self._obj_.CreateWindow(None, title, style, rect, parent)
+ rect = self.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.child = FontWindow("Not threaded")
+ self.child.Create("FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self)
+
+
+class TestThread(thread.WinThread):
+ def __init__(self, parentWindow):
+ self.parentWindow = parentWindow
+ self.child = None
+ thread.WinThread.__init__(self)
+ def InitInstance(self):
+ rect = self.parentWindow.GetClientRect()
+ rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
+ self.child = FontWindow()
+ self.child.Create("FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self.parentWindow)
+ self.SetMainFrame(self.child)
+ return 1
+
+ def ExitInstance(self):
+ return 0
+
+class ThreadedFontFrame(window.MDIChildWnd):
+ def __init__(self):
+ pass # Dont call base class doc/view version...
+ self.thread = None
+ def Create(self, title, rect = None, parent = None):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
+ self._obj_ = win32ui.CreateMDIChild()
+ self._obj_.CreateWindow(None, title, style, rect, parent)
+ self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
+ self._obj_.HookMessage (self.OnSize, win32con.WM_SIZE)
+
+ self.thread = TestThread(self)
+ self.thread.CreateThread()
+
+ def OnSize(self, msg):
+ pass
+
+ def OnDestroy(self, msg):
+ win32ui.OutputDebugString("OnDestroy\n")
+ if self.thread and self.thread.child:
+ child = self.thread.child
+ child.SendMessage(WM_USER_PREPARE_TO_CLOSE, 0, 0)
+ win32ui.OutputDebugString("Destroyed\n")
+
+
+def Demo():
+ f = FontFrame()
+ f.Create("Font Demo")
+
+def ThreadedDemo():
+ rect = win32ui.GetMainFrame().GetClientRect()
+ rect = rect[0], rect[3]*3/4, rect[2]/4, rect[3]
+ incr = rect[2]
+ for i in range(4):
+ if i==0:
+ f = FontFrame()
+ title = "Not threaded"
+ else:
+ f = ThreadedFontFrame()
+ title = "Threaded GUI Demo"
+ f.Create(title, rect)
+ rect = rect[0] + incr, rect[1], rect[2]+incr, rect[3]
+ # Givem a chance to start
+ win32api.Sleep(100)
+ win32ui.PumpWaitingMessages()
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ ThreadedDemo()
+# Demo()
diff --git a/Pythonwin/pywin/Demos/toolbar.py b/Pythonwin/pywin/Demos/toolbar.py
new file mode 100644
index 0000000000..81bbb59c86
--- /dev/null
+++ b/Pythonwin/pywin/Demos/toolbar.py
@@ -0,0 +1,84 @@
+# Demo of ToolBars
+
+# Shows the toolbar control.
+# Demos how to make custom tooltips, etc.
+
+import win32ui
+import win32con
+import win32api
+from pywin.mfc import docview, window, afxres
+import commctrl
+
+class GenericFrame(window.MDIChildWnd):
+ def OnCreateClient(self, cp, context):
+ # handlers for toolbar buttons
+ self.HookCommand (self.OnPrevious, 401)
+ self.HookCommand (self.OnNext, 402)
+ self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXT)
+ self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXTW)
+
+# parent = win32ui.GetMainFrame()
+ parent = self
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | \
+ afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
+
+ buttons = (win32ui.ID_APP_ABOUT,win32ui.ID_VIEW_INTERACTIVE)
+ bitmap = win32ui.IDB_BROWSER_HIER
+ self.toolbar = tb = win32ui.CreateToolBar (parent, style)
+ tb.LoadBitmap(bitmap)
+ tb.SetButtons(buttons)
+
+ tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ tb.SetWindowText("Test")
+ parent.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ parent.DockControlBar(tb)
+ parent.LoadBarState("ToolbarTest")
+ window.MDIChildWnd.OnCreateClient(self, cp, context)
+ return 1
+
+ def OnDestroy(self, msg):
+ self.SaveBarState("ToolbarTest")
+
+ def GetTTText(self, std, extra):
+ (hwndFrom, idFrom, code) = std
+ text, hinst, flags = extra
+ if flags & commctrl.TTF_IDISHWND:
+ return # Not handled
+ if (idFrom==win32ui.ID_APP_ABOUT):
+ return 0, ("It works!", idFrom, code)
+ return None # not handled.
+
+ def GetMessageString(self, id):
+ if id==win32ui.ID_APP_ABOUT:
+ return "Dialog Test\nTest"
+ else:
+ return self._obj_.GetMessageString(id)
+
+ def OnSize (self, params):
+ print 'OnSize called with ', params
+
+ def OnNext (self, id, cmd):
+ print 'OnNext called'
+
+ def OnPrevious (self, id, cmd):
+ print 'OnPrevious called'
+
+msg = """\
+This toolbar was dynamically created.\r
+\r
+The first item's tooltips is provided by Python code.\r
+\r
+(Dont close the window with the toolbar in a floating state - it may not re-appear!)\r
+"""
+
+def test():
+ template = docview.DocTemplate( win32ui.IDR_PYTHONTYPE, None, GenericFrame, docview.EditView)
+ doc = template.OpenDocumentFile(None)
+ doc.SetTitle("Toolbar Test")
+ view = doc.GetFirstView()
+ view.SetWindowText(msg)
+
+if __name__=='__main__':
+ import demoutils
+ if demoutils.NeedGoodGUI():
+ test()
diff --git a/Pythonwin/pywin/IDLE.cfg b/Pythonwin/pywin/IDLE.cfg
new file mode 100644
index 0000000000..b1987b14c1
--- /dev/null
+++ b/Pythonwin/pywin/IDLE.cfg
@@ -0,0 +1,29 @@
+[General]
+# We base this configuration on the default config.
+# You can list "Based On" as many times as you like
+Based On = default
+
+[Keys]
+# Only list keys different to default.
+# Note you may wish to rebind some of the default
+# Pythonwin keys to "Beep" or "DoNothing"
+
+Alt+L = LocateSelectedFile
+Ctrl+Q = AppExit
+
+# Other non-default Pythonwin keys
+Alt+A = EditSelectAll
+Alt+M = LocateModule
+
+# Movement
+Ctrl+D = GotoEndOfFile
+
+# Tabs and other indent features
+Alt+T = <>
+Ctrl+[ = <>
+Ctrl+] = <>
+
+[Keys:Interactive]
+Alt+P = <>
+Alt+N = <>
+
diff --git a/Pythonwin/pywin/__init__.py b/Pythonwin/pywin/__init__.py
new file mode 100644
index 0000000000..564c28fbfa
--- /dev/null
+++ b/Pythonwin/pywin/__init__.py
@@ -0,0 +1,8 @@
+import sys
+
+# Old names for bw compat.
+#import pywin.mfc.object; sys.modules['object'] = pywin.mfc.object
+#import pywin.mfc.dialog; sys.modules['pywindialog'] = pywin.mfc.dialog
+#import pywin.mfc.window; sys.modules['window'] = pywin.mfc.window
+#import pywin.mfc.docview; sys.modules['docview'] = pywin.mfc.docview
+
diff --git a/Pythonwin/pywin/debugger/__init__.py b/Pythonwin/pywin/debugger/__init__.py
new file mode 100644
index 0000000000..12185f870c
--- /dev/null
+++ b/Pythonwin/pywin/debugger/__init__.py
@@ -0,0 +1,89 @@
+import sys
+
+# Inject some methods in the top level name-space.
+currentDebugger = None # Wipe out any old one on reload.
+
+def _GetCurrentDebugger():
+ global currentDebugger
+ if currentDebugger is None:
+ import debugger
+ currentDebugger = debugger.Debugger()
+ return currentDebugger
+
+def GetDebugger():
+ # An error here is not nice - as we are probably trying to
+ # break into the debugger on a Python error, any
+ # error raised by this is usually silent, and causes
+ # big problems later!
+ try:
+ rc = _GetCurrentDebugger()
+ rc.GUICheckInit()
+ return rc
+ except:
+ print "Could not create the debugger!"
+ import traceback
+ traceback.print_exc()
+ return None
+
+def close():
+ if currentDebugger is not None:
+ currentDebugger.close()
+
+def run(cmd,globals=None, locals=None, start_stepping = 1):
+ _GetCurrentDebugger().run(cmd, globals,locals, start_stepping)
+
+def runeval(expression, globals=None, locals=None):
+ return _GetCurrentDebugger().runeval(expression, globals, locals)
+
+def runcall(*args):
+ return apply(_GetCurrentDebugger().runcall, args)
+
+def set_trace():
+ import sys
+ d = _GetCurrentDebugger()
+
+ if d.frameShutdown: return # App closing
+
+ if d.stopframe != d.botframe:
+ # If im not "running"
+ return
+
+ sys.settrace(None) # May be hooked
+ d.reset()
+ d.set_trace()
+
+# "brk" is an alias for "set_trace" ("break" is a reserved word :-(
+brk = set_trace
+
+# Post-Mortem interface
+
+def post_mortem(t=None):
+ if t is None:
+ t = sys.exc_info()[2] # Will be valid if we are called from an except handler.
+ if t is None:
+ try:
+ t = sys.last_traceback
+ except AttributeError:
+ print "No traceback can be found from which to perform post-mortem debugging!"
+ print "No debugging can continue"
+ return
+ p = _GetCurrentDebugger()
+ if p.frameShutdown: return # App closing
+ # No idea why I need to settrace to None - it should have been reset by now?
+ sys.settrace(None)
+ if p.stopframe != p.botframe:
+ # If im "running"
+ print "Can not perform post-mortem debugging while the debugger is active."
+ return
+ p.reset()
+ while t.tb_next <> None: t = t.tb_next
+ p.bAtPostMortem = 1
+ p.prep_run(None)
+ try:
+ p.interaction(t.tb_frame, t)
+ finally:
+ p.bAtPostMortem = 0
+ p.done_run()
+
+def pm(t=None):
+ post_mortem(t)
diff --git a/Pythonwin/pywin/debugger/configui.py b/Pythonwin/pywin/debugger/configui.py
new file mode 100644
index 0000000000..ff0f260700
--- /dev/null
+++ b/Pythonwin/pywin/debugger/configui.py
@@ -0,0 +1,30 @@
+from dbgcon import *
+from pywin.mfc import dialog
+
+class DebuggerOptionsPropPage(dialog.PropertyPage):
+ def __init__(self):
+ dialog.PropertyPage.__init__(self, win32ui.IDD_PP_DEBUGGER)
+
+ def OnInitDialog(self):
+ options = self.options = LoadDebuggerOptions()
+ self.AddDDX(win32ui.IDC_CHECK1, OPT_HIDE)
+ self[OPT_STOP_EXCEPTIONS] = options[OPT_STOP_EXCEPTIONS]
+ self.AddDDX(win32ui.IDC_CHECK2, OPT_STOP_EXCEPTIONS)
+ self[OPT_HIDE] = options[OPT_HIDE]
+ return dialog.PropertyPage.OnInitDialog(self)
+
+ def OnOK(self):
+ self.UpdateData()
+ dirty = 0
+ for key, val in self.items():
+ if self.options.has_key(key):
+ if self.options[key] != val:
+ self.options[key] = val
+ dirty = 1
+ if dirty:
+ SaveDebuggerOptions(self.options)
+ # If there is a debugger open, then set its options.
+ import pywin.debugger
+ if pywin.debugger.currentDebugger is not None:
+ pywin.debugger.currentDebugger.options = self.options
+ return 1
diff --git a/Pythonwin/pywin/debugger/dbgcon.py b/Pythonwin/pywin/debugger/dbgcon.py
new file mode 100644
index 0000000000..1feb21f18d
--- /dev/null
+++ b/Pythonwin/pywin/debugger/dbgcon.py
@@ -0,0 +1,28 @@
+# General constants for the debugger
+
+DBGSTATE_NOT_DEBUGGING = 0
+DBGSTATE_RUNNING = 1
+DBGSTATE_BREAK = 2
+DBGSTATE_QUITTING = 3 # Attempting to back out of the debug session.
+
+LINESTATE_CURRENT = 0x1 # This line is where we are stopped
+LINESTATE_BREAKPOINT = 0x2 # This line is a breakpoint
+LINESTATE_CALLSTACK = 0x4 # This line is in the callstack.
+
+OPT_HIDE = 'hide'
+OPT_STOP_EXCEPTIONS = 'stopatexceptions'
+
+import win32api, win32ui
+
+def DoGetOption(optsDict, optName, default):
+ optsDict[optName] = win32ui.GetProfileVal("Debugger Options", optName, default)
+
+def LoadDebuggerOptions():
+ opts = {}
+ DoGetOption(opts, OPT_HIDE, 0)
+ DoGetOption(opts, OPT_STOP_EXCEPTIONS, 1)
+ return opts
+
+def SaveDebuggerOptions(opts):
+ for key, val in opts.items():
+ win32ui.WriteProfileVal("Debugger Options", key, val)
diff --git a/Pythonwin/pywin/debugger/dbgpyapp.py b/Pythonwin/pywin/debugger/dbgpyapp.py
new file mode 100644
index 0000000000..581fb81cec
--- /dev/null
+++ b/Pythonwin/pywin/debugger/dbgpyapp.py
@@ -0,0 +1,60 @@
+# dbgpyapp.py - Debugger Python application class
+#
+import win32con
+import win32ui
+import sys
+import string
+import os
+from pywin.framework import intpyapp
+
+version = '0.3.0'
+
+class DebuggerPythonApp(intpyapp.InteractivePythonApp):
+ def LoadMainFrame(self):
+ " Create the main applications frame "
+ self.frame = self.CreateMainFrame()
+ self.SetMainFrame(self.frame)
+ self.frame.LoadFrame(win32ui.IDR_DEBUGGER, win32con.WS_OVERLAPPEDWINDOW)
+ self.frame.DragAcceptFiles() # we can accept these.
+ self.frame.ShowWindow(win32con.SW_HIDE);
+# self.frame.ShowWindow(win32ui.GetInitialStateRequest());
+ self.frame.UpdateWindow();
+
+ # but we do rehook, hooking the new code objects.
+ self.HookCommands()
+
+ def InitInstance(self):
+ # Use a registry path of "Python\Pythonwin Debugger
+ win32ui.SetAppName(win32ui.LoadString(win32ui.IDR_DEBUGGER))
+ win32ui.SetRegistryKey("Python")
+ # We _need_ the Scintilla color editor.
+ win32ui.WriteProfileVal("Editor","Module", "pywin.framework.editor.color.coloreditor")
+
+ numMRU = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
+ win32ui.LoadStdProfileSettings(numMRU)
+
+ self.LoadMainFrame()
+
+ # Display the interactive window if the user wants it.
+ from pywin.framework import interact
+ interact.CreateInteractiveWindowUserPreference()
+
+ # Load the modules we use internally.
+ self.LoadSystemModules()
+ # Load additional module the user may want.
+ self.LoadUserModules()
+
+# win32ui.CreateDebuggerThread()
+ win32ui.EnableControlContainer()
+
+ # Load the ToolBar state near the end of the init process, as
+ # there may be Toolbar IDs created by the user or other modules.
+ # By now all these modules should be loaded, so all the toolbar IDs loaded.
+ try:
+ self.frame.LoadBarState("ToolbarDefault")
+ except win32ui.error:
+ # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the
+ # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this
+ # but I need to investigate more how to prevent it (AFAIK, ensuring all the
+ # toolbars are created by now _should_ stop it!)
+ pass
diff --git a/Pythonwin/pywin/debugger/debugger.py b/Pythonwin/pywin/debugger/debugger.py
new file mode 100644
index 0000000000..1f61f24110
--- /dev/null
+++ b/Pythonwin/pywin/debugger/debugger.py
@@ -0,0 +1,897 @@
+# debugger.py
+
+# A debugger for Pythonwin. Built from pdb.
+
+# Mark Hammond (MHammond@skippinet.com.au) - Dec 94.
+
+# usage:
+# >>> import pywin.debugger
+# >>> pywin.debugger.GetDebugger().run("command")
+
+import pdb
+import bdb
+import sys
+import string
+import os
+import regsub
+import types
+
+import win32ui
+import win32api
+import win32con
+import pywin;pywin.editormodulename = "pywin.framework.editor.color.coloreditor"
+import pywin.docking.DockingBar
+from pywin.mfc import dialog, object, afxres, window
+from pywin.framework import app, interact, editor, scriptutils
+from pywin.framework.editor.color.coloreditor import MARKER_CURRENT, MARKER_BREAKPOINT
+from pywin.tools import browser, hierlist
+import commctrl
+import traceback
+
+#import win32traceutil
+
+from dbgcon import *
+
+isInprocApp = -1
+
+error = "pywin.debugger.error"
+
+def SetInteractiveContext(globs, locs):
+ if interact.edit is not None and interact.edit.currentView is not None:
+ interact.edit.currentView.SetContext(globs, locs)
+
+def _LineStateToMarker(ls):
+ if ls==LINESTATE_CURRENT:
+ return MARKER_CURRENT
+# elif ls == LINESTATE_CALLSTACK:
+# return MARKER_CALLSTACK
+ return MARKER_BREAKPOINT
+
+class HierListItem(browser.HLIPythonObject):
+ pass
+
+class HierFrameItem(HierListItem):
+ def __init__(self, frame, debugger):
+ HierListItem.__init__(self, frame, None)
+ self.debugger = debugger
+ def GetText(self):
+ name = self.myobject.f_code.co_name
+ if not name or name == '?' :
+ # See if locals has a '__name__' (ie, a module)
+ if self.myobject.f_locals.has_key('__name__'):
+ name = self.myobject.f_locals['__name__'] + " module"
+ else:
+ name = ''
+
+ return "%s (%s:%d)" % (name, os.path.split(self.myobject.f_code.co_filename)[1], self.myobject.f_lineno)
+ def GetBitmapColumn(self):
+ if self.debugger.curframe is self.myobject:
+ return 7
+ else:
+ return 8
+ def GetSubList(self):
+ ret = []
+ ret.append(HierFrameDict(self.myobject.f_locals, "Locals", 2))
+ ret.append(HierFrameDict(self.myobject.f_globals, "Globals", 1))
+ return ret
+ def IsExpandable(self):
+ return 1
+ def TakeDefaultAction(self):
+ # Set the default frame to be this frame.
+ self.debugger.set_cur_frame(self.myobject)
+ return 1
+
+class HierFrameDict(browser.HLIDict):
+ def __init__(self, dict, name, bitmapColumn):
+ self.bitmapColumn=bitmapColumn
+ browser.HLIDict.__init__(self, dict, name)
+ def GetBitmapColumn(self):
+ return self.bitmapColumn
+
+class NoStackAvailableItem(HierListItem):
+ def __init__(self, why):
+ HierListItem.__init__(self, None, why)
+ def IsExpandable(self):
+ return 0
+ def GetText(self):
+ return self.name
+ def GetBitmapColumn(self):
+ return 8
+
+class HierStackRoot(HierListItem):
+ def __init__( self, debugger ):
+ HierListItem.__init__(self, debugger, None)
+ def GetSubList(self):
+ debugger = self.myobject
+# print self.debugger.stack, self.debugger.curframe
+ ret = []
+ if debugger.debuggerState==DBGSTATE_BREAK:
+ stackUse=debugger.stack[:]
+ stackUse.reverse()
+ for frame, lineno in stackUse:
+ ret.append( HierFrameItem( frame, debugger ) )
+ if frame is debugger.userbotframe: # Dont bother showing frames below our bottom frame.
+ break
+ elif debugger.debuggerState==DBGSTATE_NOT_DEBUGGING:
+ ret.append(NoStackAvailableItem(''))
+ else:
+ ret.append(NoStackAvailableItem(''))
+ return ret
+ def GetText(self):
+ return 'root item'
+ def IsExpandable(self):
+ return 1
+
+class HierListDebugger(hierlist.HierListWithItems):
+ """ Hier List of stack frames, breakpoints, whatever """
+ def __init__(self, debugger):
+ self.debugger = debugger
+ hierlist.HierListWithItems.__init__(self, None, win32ui.IDB_DEBUGGER_HIER, None, win32api.RGB(255,0,0))
+ def Setup(self):
+ root = HierStackRoot(self.debugger)
+ self.AcceptRoot(root)
+ def Refresh(self):
+ self.Setup()
+
+class DebuggerWindow(window.Wnd):
+ def GetDefRect(self):
+ defRect = app.LoadWindowSize("Debugger Windows\\" + self.title)
+ if defRect[2]-defRect[0]==0:
+ defRect = 0, 0, 150, 150
+ return defRect
+
+ def OnDestroy(self, msg):
+ newSize = self.GetWindowPlacement()[4]
+ pywin.framework.app.SaveWindowSize("Debugger Windows\\" + self.title, newSize)
+ return window.Wnd.OnDestroy(self, msg)
+
+ def OnKeyDown(self, msg):
+ key = msg[2]
+ if key in [13, 27, 32]: return 1
+ if key == 46: # delete key
+ self.DeleteSelected()
+ return 0
+ view = scriptutils.GetActiveView()
+ try:
+ firer = view.bindings.fire_key_event
+ except AttributeError:
+ firer = None
+ if firer is not None:
+ return firer(msg)
+ else:
+ return 1
+
+ def DeleteSelected(self):
+ win32api.MessageBeep()
+
+class DebuggerStackWindow(DebuggerWindow):
+ title = "Stack"
+ def __init__(self, debugger):
+ DebuggerWindow.__init__(self, win32ui.CreateTreeCtrl())
+ self.list = HierListDebugger(debugger)
+ def SaveState(self):
+ pass
+ def CreateWindow(self, parent):
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS
+ self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1)
+ self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
+ self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
+ self.list.HierInit (parent, self)
+ self.list.Setup()
+
+ def RespondDebuggerState(self, state):
+ self.list.Refresh()
+
+ def RespondDebuggerData(self):
+ try:
+ handle = self.GetChildItem(0)
+ except win32ui.error:
+ return # No items
+ while 1:
+ item = self.list.ItemFromHandle(handle)
+ col = self.list.GetBitmapColumn(item)
+ selCol = self.list.GetSelectedBitmapColumn(item)
+ if selCol is None: selCol = col
+ if self.list.GetItemImage(handle)!= (col, selCol):
+ self.list.SetItemImage(handle, col, selCol)
+ try:
+ handle = self.GetNextSiblingItem(handle)
+ except win32ui.error:
+ break
+
+class DebuggerListViewWindow(DebuggerWindow):
+ def __init__(self, debugger):
+ DebuggerWindow.__init__(self, win32ui.CreateListCtrl())
+ self.debugger = debugger
+ def CreateWindow(self, parent):
+ list = self
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.LVS_EDITLABELS | commctrl.LVS_REPORT
+ self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1)
+ self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
+ self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
+ list = self
+ title, width = self.columns[0]
+ itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0)
+ list.InsertColumn(0, itemDetails)
+ col = 1
+ for title, width in self.columns[1:]:
+ col = col + 1
+ itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0)
+ list.InsertColumn(col, itemDetails)
+ parent.HookNotify( self.OnListEndLabelEdit, commctrl.LVN_ENDLABELEDIT)
+
+ def RespondDebuggerData(self):
+ pass
+
+ def RespondDebuggerState(self, state):
+ pass
+
+class DebuggerBreakpointsWindow(DebuggerListViewWindow):
+ title = "Breakpoints"
+ columns = [ ("Condition", 70), ("Location", 1024)]
+
+ def SaveState(self):
+ pass
+
+ def OnListEndLabelEdit(self, std, extra):
+ item = extra[0]
+ text = item[4]
+ if text is None: return
+
+ item_id = self.GetItem(item[0])[6]
+
+ from bdb import Breakpoint
+ for bplist in Breakpoint.bplist.values():
+ for bp in bplist:
+ if id(bp)==item_id:
+ if string.lower(string.strip(text))=="none":
+ text = None
+ bp.cond = text
+ break
+ self.RespondDebuggerData()
+
+ def DeleteSelected(self):
+ try:
+ num = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ item_id = self.GetItem(num)[6]
+ from bdb import Breakpoint
+ for bplist in Breakpoint.bplist.values():
+ for bp in bplist:
+ if id(bp)==item_id:
+ self.debugger.clear_break(bp.file, bp.line)
+ break
+ except win32ui.error:
+ win32api.MessageBeep()
+ self.RespondDebuggerData()
+
+ def RespondDebuggerData(self):
+ list = self
+ list.DeleteAllItems()
+ index = -1
+ from bdb import Breakpoint
+ for bplist in Breakpoint.bplist.values():
+ for bp in bplist:
+ baseName = os.path.split(bp.file)[1]
+ cond = bp.cond
+ item = index+1, 0, 0, 0, str(cond), 0, id(bp)
+ index = list.InsertItem(item)
+ list.SetItemText(index, 1, "%s: %s" % (baseName, bp.line))
+
+class DebuggerWatchWindow(DebuggerListViewWindow):
+ title = "Watch"
+ columns = [ ("Expression", 70), ("Value", 1024)]
+
+ def CreateWindow(self, parent):
+ DebuggerListViewWindow.CreateWindow(self, parent)
+ items = string.split(win32ui.GetProfileVal("Debugger Windows\\" + self.title, "Items", ""), "\t")
+ index = -1
+ for item in items:
+ if item:
+ index = self.InsertItem(index+1, item)
+ self.InsertItem(index+1, "")
+
+ def SaveState(self):
+ items = []
+ for i in range(self.GetItemCount()-1):
+ items.append(self.GetItemText(i,0))
+ win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "Items", string.join(items,"\t"))
+ return 1
+
+ def OnListEndLabelEdit(self, std, extra):
+ item = extra[0]
+ itemno = item[0]
+ text = item[4]
+ if text is None: return
+ self.SetItemText(itemno, 0, text)
+ if itemno == self.GetItemCount()-1:
+ self.InsertItem(itemno+1, "")
+ self.RespondDebuggerState(self.debugger.debuggerState)
+
+ def DeleteSelected(self):
+ try:
+ num = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ if num < self.GetItemCount()-1: # We cant delete the last
+ self.DeleteItem(num)
+ except win32ui.error:
+ win32api.MessageBeep()
+
+ def RespondDebuggerState(self, state):
+ globs = locs = None
+ if state==DBGSTATE_BREAK:
+ if self.debugger.curframe:
+ globs = self.debugger.curframe.f_globals
+ locs = self.debugger.curframe.f_locals
+ elif state==DBGSTATE_NOT_DEBUGGING:
+ import __main__
+ globs = locs = __main__.__dict__
+ for i in range(self.GetItemCount()-1):
+ text = self.GetItemText(i, 0)
+ if globs is None:
+ val = ""
+ else:
+ try:
+ val = repr( eval( text, globs, locs) )
+ except SyntaxError:
+ val = "Syntax Error"
+ except:
+ t, v, tb = sys.exc_info()
+ val = string.strip(traceback.format_exception_only(t, v)[0])
+ self.SetItemText(i, 1, val)
+
+def CreateDebuggerDialog(parent, klass, debugger):
+ control = klass(debugger)
+ control.CreateWindow(parent)
+ return control
+
+DebuggerDialogInfos = (
+ (0xe810, DebuggerStackWindow, None),
+ (0xe811, DebuggerBreakpointsWindow, (10, 10)),
+ (0xe812, DebuggerWatchWindow, None),
+ )
+
+def _MakeDebuggerGUI():
+ import dbgpyapp
+ app = dbgpyapp.DebuggerPythonApp()
+ app.InitInstance()
+
+def _CheckNeedGUI():
+ global isInprocApp
+ if isInprocApp==-1:
+ isInprocApp = win32ui.GetApp().IsInproc()
+ if isInprocApp:
+ # MAY Need it - may already have one
+ need = sys.modules.has_key("pywin.debugger.dbgpyapp")==0
+ else:
+ need = 0
+ if need:
+ _MakeDebuggerGUI()
+ else:
+ # Check we have the appropriate editor.
+ import pywin.framework.editor
+ try:
+ import pywin.framework.editor.color.coloreditor
+ ok = pywin.framework.editor.editorTemplate==pywin.framework.editor.color.coloreditor.editorTemplate
+ except ImportError:
+ ok = 0
+ if not ok:
+ msg = "This debugger requires the Pythonwin color editor.\r\nDebugging can not continue.\r\n\r\nWould you like to make the color editor the default?"
+ rc = win32ui.MessageBox(msg, "Can't initialize debugger", win32con.MB_YESNO)
+ if rc == win32con.IDYES:
+ pywin.framework.editor.WriteDefaultEditorModule("pywin.framework.editor.color.coloreditor")
+ win32ui.MessageBox("The debugger will be available when you restart the application.")
+ raise RuntimeError, "Can't initialize debugger, as the required editor is not the default"
+ return need
+
+SKIP_NONE=0
+SKIP_STEP=1
+SKIP_RUN=2
+
+debugger_parent=pdb.Pdb
+class Debugger(debugger_parent):
+ def __init__(self):
+ self.inited = 0
+ self.skipBotFrame = SKIP_NONE
+ self.userbotframe = None
+ self.frameShutdown = 0
+ self.pumping = 0
+ self.debuggerState = DBGSTATE_NOT_DEBUGGING # Assume so, anyway.
+ self.shownLineCurrent = None # The last filename I highlighted.
+ self.shownLineCallstack = None # The last filename I highlighted.
+ self.last_cmd_debugged = ""
+ self.abortClosed = 0
+ debugger_parent.__init__(self)
+
+ # See if any break-points have been set in the editor
+ for doc in editor.editorTemplate.GetDocumentList():
+ lineNo = -1
+ while 1:
+ lineNo = doc.MarkerGetNext(lineNo+1, MARKER_BREAKPOINT)
+ if lineNo <= 0: break
+ self.set_break(doc.GetPathName(), lineNo)
+
+ self.reset()
+ _CheckNeedGUI()
+ self.inForcedGUI = isInprocApp
+ self.options = LoadDebuggerOptions()
+ self.bAtException = self.bAtPostMortem = 0
+
+ def __del__(self):
+ self.close()
+ def close(self, frameShutdown = 0):
+ # abortClose indicates if we have total shutdown
+ # (ie, main window is dieing)
+ if self.pumping:
+ # Can stop pump here, as it only posts a message, and
+ # returns immediately.
+ if not self.StopDebuggerPump(): # User cancelled close.
+ return 0
+ # NOTE - from this point on the close can not be
+ # stopped - the WM_QUIT message is already in the queue.
+ self.frameShutdown = frameShutdown
+ if not self.inited: return 1
+ self.inited = 0
+
+ SetInteractiveContext(None, None)
+
+ frame = win32ui.GetMainFrame()
+ frame.SaveBarState("ToolbarDebugging")
+ # Hide the debuger toolbars (as they wont normally form part of the main toolbar state.
+ for id, klass, float in DebuggerDialogInfos:
+ try:
+ tb = frame.GetControlBar(id)
+ tb.dialog.SaveState()
+ frame.ShowControlBar(tb, 0, 1)
+ except win32ui.error:
+ pass
+
+ # Restore the standard toolbar config
+ try:
+ frame.LoadBarState("ToolbarDefault")
+ except win32ui.error, msg: # When once created toolbars no longer exist.
+ pass
+# print msg # LoadBarState failed (with win32 exception!)
+ self._UnshowCurrentLine()
+ self.set_quit()
+ return 1
+
+ def StopDebuggerPump(self):
+ assert self.pumping, "Can't stop the debugger pump if Im not pumping!"
+ # After stopping a pump, I may never return.
+ if self.GUIAboutToFinishInteract():
+ self.pumping = 0
+ win32ui.StopDebuggerPump() # Posts a message, so we do return.
+ return 1
+ return 0
+
+ def get_option(self, option):
+ """Public interface into debugger options
+ """
+ try:
+ return self.options[option]
+ except KeyError:
+ raise error, "Option %s is not a valid option" % option
+
+ def prep_run(self, cmd):
+ pass
+ def done_run(self, cmd=None):
+ self.RespondDebuggerState(DBGSTATE_NOT_DEBUGGING)
+ self.close()
+ def canonic(self, fname):
+ return string.lower(os.path.abspath(fname))
+ def reset(self):
+ debugger_parent.reset(self)
+ self.userbotframe = None
+ self.UpdateAllLineStates()
+ self._UnshowCurrentLine()
+
+
+ def setup(self, f, t):
+ debugger_parent.setup(self, f, t)
+ self.bAtException = t is not None
+
+ def set_break(self, filename, lineno, temporary=0, cond = None):
+ filename = self.canonic(filename)
+ self.SetLineState(filename, lineno, LINESTATE_BREAKPOINT)
+ return debugger_parent.set_break(self, filename, lineno, temporary, cond)
+
+ def clear_break(self, filename, lineno):
+ filename = self.canonic(filename)
+ self.ResetLineState(filename, lineno, LINESTATE_BREAKPOINT)
+ return debugger_parent.clear_break(self, filename, lineno)
+
+ def cmdloop(self):
+ if self.frameShutdown: return # App in the process of closing - never break in!
+ self.GUIAboutToBreak()
+
+ def print_stack_entry(self, frame):
+ # We dont want a stack printed - our GUI is better :-)
+ pass
+
+ def user_return(self, frame, return_value):
+ # Same as parent, just no "print"
+ # This function is called when a return trap is set here
+ frame.f_locals['__return__'] = return_value
+ self.interaction(frame, None)
+
+ def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
+ # This function is called if an exception occurs,
+ # but only if we are to stop at or just below this level
+ if self.get_option(OPT_STOP_EXCEPTIONS):
+ frame.f_locals['__exception__'] = exc_type, exc_value
+ print "Unhandled exception while debugging..."
+ traceback.print_exception(exc_type, exc_value, exc_traceback)
+ self.interaction(frame, exc_traceback)
+
+ def user_line(self, frame):
+ if frame.f_lineno==0: return
+ debugger_parent.user_line(self, frame)
+
+ def stop_here(self, frame):
+ if frame is self.botframe and self.skipBotFrame == SKIP_RUN:
+ self.set_continue()
+ return 0
+ if frame is self.botframe and self.skipBotFrame == SKIP_STEP:
+ self.set_step()
+ return 0
+ return debugger_parent.stop_here(self, frame)
+
+ def _set_botuser(self, levels_back=2):
+ try:
+ 1/0
+ except:
+ f = sys.exc_info()[2].tb_frame
+ for i in range(levels_back):
+ f = f.f_back
+ self.userbotframe = f
+
+ def run(self, cmd,globals=None, locals=None, start_stepping = 1):
+ if type(cmd) not in [types.StringType, types.CodeType]:
+ raise TypeError, "Only strings can be run"
+ self.last_cmd_debugged = cmd
+ try:
+ if globals is None:
+ import __main__
+ globals = __main__.__dict__
+ if locals is None:
+ locals = globals
+ self.reset()
+ self.prep_run(cmd)
+ sys.settrace(self.trace_dispatch)
+ if type(cmd) <> types.CodeType:
+ cmd = cmd+'\n'
+ try:
+ try:
+ if start_stepping: self.skipBotFrame = SKIP_STEP
+ else: self.skipBotFrame = SKIP_RUN
+ _doexec(cmd, globals, locals)
+ except bdb.BdbQuit:
+ pass
+ finally:
+ self.skipBotFrame = SKIP_NONE
+ self.quitting = 1
+ sys.settrace(None)
+
+ finally:
+ self.done_run(cmd)
+
+ def runeval(self, expr, globals=None, locals=None):
+ self.prep_run(expr)
+ try:
+ debugger_parent.runeval(self, expr, globals, locals)
+ finally:
+ self.done_run(expr)
+
+ def runexec(self, what, globs=None, locs=None):
+ self.reset()
+ sys.settrace(self.trace_dispatch)
+ try:
+ try:
+ exec what in globs, locs
+ except bdb.BdbQuit:
+ pass
+ finally:
+ self.quitting = 1
+ sys.settrace(None)
+
+ def do_set_step(self):
+ if self.GUIAboutToRun():
+ self.set_step()
+
+ def do_set_next(self):
+ if self.GUIAboutToRun():
+ self.set_next(self.curframe)
+
+ def do_set_return(self):
+ if self.GUIAboutToRun():
+ self.set_return(self.curframe)
+
+ def do_set_continue(self):
+ if self.GUIAboutToRun():
+ self.set_continue()
+
+ def set_quit(self):
+ ok = 1
+ if self.pumping:
+ ok = self.StopDebuggerPump()
+ if ok:
+ debugger_parent.set_quit(self)
+
+ def _dump_frame_(self, frame,name=None):
+ if name is None: name = ""
+ if frame:
+ if frame.f_code and frame.f_code.co_filename:
+ fname = os.path.split(frame.f_code.co_filename)[1]
+ else:
+ fname = "??"
+ print `name`, fname, frame.f_lineno, frame
+ else:
+ print `name`, "None"
+
+ def set_trace(self):
+ # Start debugging from _2_ levels up!
+ try:
+ 1 + ''
+ except:
+ frame = sys.exc_info()[2].tb_frame.f_back.f_back
+ self.reset()
+ self._set_botuser(5)
+ while frame:
+ frame.f_trace = self.trace_dispatch
+ self.botframe = frame
+ frame = frame.f_back
+ self.set_step()
+ sys.settrace(self.trace_dispatch)
+
+ def set_cur_frame(self, frame):
+ # Sets the "current" frame - ie, the frame with focus. This is the
+ # frame on which "step out" etc actions are taken.
+ # This may or may not be the top of the stack.
+ assert frame is not None, "You must pass a valid frame"
+ self.curframe = frame
+ for f, index in self.stack:
+ if f is frame:
+ self.curindex = index
+ break
+ else:
+ assert 0, "Can't find the frame in the stack."
+ SetInteractiveContext(frame.f_globals, frame.f_locals)
+ self.GUIRespondDebuggerData()
+ self.ShowCurrentLine()
+
+ def IsBreak(self):
+ return self.debuggerState == DBGSTATE_BREAK
+
+ def IsDebugging(self):
+ return self.debuggerState != DBGSTATE_NOT_DEBUGGING
+
+ def RespondDebuggerState(self, state):
+ if state == self.debuggerState: return
+ if state==DBGSTATE_NOT_DEBUGGING: # Debugger exists, but not doing anything
+ title = ""
+ elif state==DBGSTATE_RUNNING: # Code is running under the debugger.
+ title = " - running"
+ elif state==DBGSTATE_BREAK: # We are at a breakpoint or stepping or whatever.
+ if self.bAtException:
+ if self.bAtPostMortem:
+ title = " - post mortem exception"
+ else:
+ title = " - exception"
+ else:
+ title = " - break"
+ else:
+ raise error, "Invalid debugger state passed!"
+ win32ui.GetMainFrame().SetWindowText(win32ui.LoadString(win32ui.IDR_MAINFRAME) + title)
+ if self.debuggerState == DBGSTATE_QUITTING and state != DBGSTATE_NOT_DEBUGGING:
+ print "Ignoring state change cos Im trying to stop!", state
+ return
+ self.debuggerState = state
+ try:
+ frame = win32ui.GetMainFrame()
+ except win32ui.error:
+ frame = None
+ if frame is not None:
+ for id, klass, float in DebuggerDialogInfos:
+ cb = win32ui.GetMainFrame().GetControlBar(id).dialog
+ cb.RespondDebuggerState(state)
+ # Tell each open editor window about the state transition
+ for doc in editor.editorTemplate.GetDocumentList():
+ doc.OnDebuggerStateChange(state)
+ self.ShowCurrentLine()
+
+ #
+ # GUI debugger interface.
+ #
+ def GUICheckInit(self):
+ if self.inited: return
+ self.inited = 1
+ frame = win32ui.GetMainFrame()
+
+ # And the other dockable debugger dialogs.
+ for id, klass, float in DebuggerDialogInfos:
+ try:
+ frame.GetControlBar(id)
+ exists=1
+ except win32ui.error:
+ exists=0
+ if exists: continue
+ bar = pywin.docking.DockingBar.DockingBar()
+ bar.CreateWindow(win32ui.GetMainFrame(), CreateDebuggerDialog, klass.title, id, childCreatorArgs=(klass, self))
+ bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
+ bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ if float is None:
+ frame.DockControlBar(bar)
+ else:
+ frame.FloatControlBar(bar, float, afxres.CBRS_ALIGN_ANY)
+ try:
+ frame.LoadBarState("ToolbarDebugging")
+ except win32ui.error, details:
+ print "LoadBarState failed - %s" % details
+
+ # ALWAYS show debugging toolbar, regardless of saved state
+ tb = frame.GetControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)
+ frame.ShowControlBar(tb, 1, 1)
+ self.GUIRespondDebuggerData()
+
+# frame.RecalcLayout()
+
+ def GetDebuggerBar(self, barName):
+ frame = win32ui.GetMainFrame()
+ for id, klass, float in DebuggerDialogInfos:
+ if klass.title == barName:
+ return frame.GetControlBar(id)
+ assert 0, "Can't find a bar of that name!"
+
+ def GUIRespondDebuggerData(self):
+ if not self.inited: # GUI not inited - no toolbars etc.
+ return
+
+ for id, klass, float in DebuggerDialogInfos:
+ cb = win32ui.GetMainFrame().GetControlBar(id).dialog
+ cb.RespondDebuggerData()
+
+ def GUIAboutToRun(self):
+ if not self.StopDebuggerPump():
+ return 0
+ self._UnshowCurrentLine()
+ self.RespondDebuggerState(DBGSTATE_RUNNING)
+ SetInteractiveContext(None, None)
+ return 1
+
+ def GUIAboutToBreak(self):
+ "Called as the GUI debugger is about to get context, and take control of the running program."
+ self.GUICheckInit()
+ self.RespondDebuggerState(DBGSTATE_BREAK)
+ self.GUIAboutToInteract()
+ if self.pumping:
+ print "!!! Already pumping - outa here"
+ return
+ self.pumping = 1
+ win32ui.StartDebuggerPump() # NOTE - This will NOT return until the user is finished interacting
+ assert not self.pumping, "Should not be pumping once the pump has finished"
+ if self.frameShutdown: # User shut down app while debugging
+ win32ui.GetMainFrame().PostMessage(win32con.WM_CLOSE)
+
+ def GUIAboutToInteract(self):
+ "Called as the GUI is about to perform any interaction with the user"
+ frame = win32ui.GetMainFrame()
+ # Remember the enabled state of our main frame
+ # may be disabled primarily if a modal dialog is displayed.
+ # Only get at enabled via GetWindowLong.
+ self.bFrameEnabled = frame.IsWindowEnabled()
+ self.oldForeground = None
+ fw = win32ui.GetForegroundWindow()
+ if fw is not frame:
+ self.oldForeground = fw
+# fw.EnableWindow(0) Leave enabled for now?
+ frame.EnableWindow(1)
+ if self.inForcedGUI and not frame.IsWindowVisible():
+ frame.ShowWindow(win32con.SW_SHOW)
+ frame.UpdateWindow()
+ if self.curframe:
+ SetInteractiveContext(self.curframe.f_globals, self.curframe.f_locals)
+ else:
+ SetInteractiveContext(None, None)
+ self.GUIRespondDebuggerData()
+
+ def GUIAboutToFinishInteract(self):
+ """Called as the GUI is about to finish any interaction with the user
+ Returns non zero if we are allowed to stop interacting"""
+ if self.oldForeground is not None:
+ win32ui.GetMainFrame().EnableWindow(0)
+ self.oldForeground.EnableWindow(1)
+# self.oldForeground.SetForegroundWindow() - fails??
+ if not self.inForcedGUI:
+ return 1 # Never a problem, and nothing else to do.
+ # If we are running a forced GUI, we may never get an opportunity
+ # to interact again. Therefore we perform a "SaveAll", to makesure that
+ # any documents are saved before leaving.
+ for template in win32ui.GetApp().GetDocTemplateList():
+ for doc in template.GetDocumentList():
+ if not doc.SaveModified():
+ return 0
+ # All documents saved - now hide the app and debugger.
+ if self.get_option(OPT_HIDE):
+ frame = win32ui.GetMainFrame()
+ frame.ShowWindow(win32con.SW_HIDE)
+ return 1
+
+ #
+ # Pythonwin interface - all stuff to do with showing source files,
+ # changing line states etc.
+ #
+ def ShowLineState(self, fileName, lineNo, lineState):
+ # Set the state of a line, open if not already
+ self.ShowLineNo(fileName, lineNo)
+ self.SetLineState(fileName, lineNo, lineState)
+
+ def SetLineState(self, fileName, lineNo, lineState):
+ # Set the state of a line if the document is open.
+ doc = editor.editorTemplate.FindOpenDocument(fileName)
+ if doc is not None:
+ marker = _LineStateToMarker(lineState)
+ doc.MarkerAdd(lineNo, marker)
+
+ def ResetLineState(self, fileName, lineNo, lineState):
+ # Set the state of a line if the document is open.
+ doc = editor.editorTemplate.FindOpenDocument(fileName)
+ if doc is not None:
+ marker = _LineStateToMarker(lineState)
+ doc.MarkerDelete(lineNo, marker)
+
+ def UpdateDocumentLineStates(self, doc):
+ # Show all lines in their special status color. If the doc is open
+ # all line states are reset.
+ doc.MarkerDeleteAll( MARKER_BREAKPOINT )
+ doc.MarkerDeleteAll( MARKER_CURRENT )
+ fname = self.canonic(doc.GetPathName())
+ # Now loop over all break-points
+ for line in self.breaks.get(fname, []):
+ doc.MarkerAdd(line, MARKER_BREAKPOINT)
+ # And the current line if in this document.
+ if self.shownLineCurrent and fname == self.shownLineCurrent[0]:
+ doc.MarkerAdd(self.shownLineCurrent[1], MARKER_CURRENT)
+ if self.shownLineCallstack and fname == self.shownLineCallstack[0]:
+ doc.MarkerAdd(self.shownLineCallstack[1], MARKER_CURRENT)
+
+ def UpdateAllLineStates(self):
+ for doc in editor.editorTemplate.GetDocumentList():
+ self.UpdateDocumentLineStates(doc)
+
+ def ShowCurrentLine(self):
+ # Show the current line. Only ever 1 current line - undoes last current
+ # The "Current Line" is self.curframe.
+ # The "Callstack Line" is the top of the stack.
+ # If current == callstack, only show as current.
+ self._UnshowCurrentLine() # un-highlight the old one.
+ if self.curframe:
+ fileName = self.canonic(self.curframe.f_code.co_filename)
+ lineNo = self.curframe.f_lineno
+ self.shownLineCurrent = fileName, lineNo
+ self.ShowLineState(fileName, lineNo, LINESTATE_CURRENT)
+
+ def _UnshowCurrentLine(self):
+ "Unshow the current line, and forget it"
+ if self.shownLineCurrent is not None:
+ fname, lineno = self.shownLineCurrent
+ self.ResetLineState(fname, lineno, LINESTATE_CURRENT)
+ self.shownLineCurrent = None
+
+ def ShowLineNo( self, filename, lineno ):
+ wasOpen = editor.editorTemplate.FindOpenDocument(filename) is not None
+ if os.path.isfile(filename) and scriptutils.JumpToDocument(filename, lineno):
+ if not wasOpen:
+ doc = editor.editorTemplate.FindOpenDocument(filename)
+ if doc is not None:
+ self.UpdateDocumentLineStates(doc)
+ return 1
+ return 0
+ return 1
+ else:
+ # Can't find the source file - linecache may have it?
+ import linecache
+ line = linecache.getline(filename, lineno)
+ print "%s(%d): %s" % (os.path.basename(filename), lineno, string.expandtabs(line[:-1],4))
+ return 0
+
+def _doexec(cmd, globals, locals):
+ exec cmd in globals, locals
\ No newline at end of file
diff --git a/Pythonwin/pywin/debugger/fail.py b/Pythonwin/pywin/debugger/fail.py
new file mode 100644
index 0000000000..ce6e3671f1
--- /dev/null
+++ b/Pythonwin/pywin/debugger/fail.py
@@ -0,0 +1,48 @@
+# NOTE NOTE - This module is designed to fail!
+#
+# The ONLY purpose for this script is testing/demoing the
+# Pythonwin debugger package.
+
+# It does nothing useful, and it even doesnt do that!
+
+import pywin.debugger, sys, time
+import traceback
+
+def a():
+ a=1
+ try:
+ b()
+ except:
+ # Break into the debugger with the exception information.
+ pywin.debugger.post_mortem(sys.exc_info()[2])
+ a=1
+ a=2
+ a=3
+ a=4
+ pass
+
+def b():
+ b=1
+ pywin.debugger.set_trace()
+ # After importing or running this module, you are likely to be
+ # sitting at the next line. This is because we explicitely
+ # broke into the debugger using the "set_trace() function
+ # "pywin.debugger.brk()" is a shorter alias for this.
+ c()
+ pass
+
+def c():
+ c=1
+ d()
+
+def d():
+ d=1
+ e(d)
+ raise ValueError, "Hi"
+
+def e(arg):
+ e=1
+ time.sleep(1)
+ return e
+
+a()
diff --git a/Pythonwin/pywin/default.cfg b/Pythonwin/pywin/default.cfg
new file mode 100644
index 0000000000..a2494a97df
--- /dev/null
+++ b/Pythonwin/pywin/default.cfg
@@ -0,0 +1,172 @@
+# The default keyboard etc configuration file for Pythonwin.
+#
+# The format of this file is very similar to a Windows INI file.
+# Sections are identified with [Section] lines, but comments
+# use the standatd Python # character. Depending on the section,
+# lines may not be in the standard "key=value" format.
+
+# NOTE: You should not need to modify this file.
+# Simply create a new .CFG file, and add an entry:
+# [General]
+# BasedOn = Default
+#
+# and add your customisations. Then select your new configuration
+# from the Pythonwin View/Options/Editor dialog.
+# This way you get to add your own customisations,
+# but still take advantage of changes to the default
+# configuration in new releases.
+
+# See IDLE.cfg for an example extension configuration.
+#
+##########################################################################
+
+[IDLE Extensions]
+
+# The list of IDLE extensions to load. The extensions
+# AutoIndent, AutoFormat and possibly others are
+# "built-in", so do not need specifying.
+
+FormatParagraph
+CallTips
+
+
+[Keys]
+
+# The list of _default_ key definitions.
+# See [Keys:Interactive] and [Keys:Editor] below for further defs.
+
+#Events of the format <>
+# are events defined in IDLE extensions.
+
+Alt+Q = <>
+
+Ctrl+W = ViewWhitespace
+
+# Auto-complete, call-tips, etc.
+Alt+/ = <>
+Shift-9 = <>
+Shift-0 = <>
+Up = <>
+Down = <>
+Left = <>
+Right = <>
+. = KeyDot
+
+# Debugger - These are the MSVC default keys, for want of a better choice.
+F9 = DbgBreakpointToggle
+F5 = DbgGo
+Shift+F5 = DbgClose
+F11 = DbgStep
+F10 = DbgStepOver
+Shift+F11 = DbgStepOut
+
+
+[Keys:Editor]
+# Key bindings specific to the editor
+F2 = GotoNextBookmark
+Ctrl+F2 = ToggleBookmark
+Ctrl+G = GotoLine
+
+Alt+I = ShowInteractiveWindow
+Alt-B = AddBanner # A sample Event defined in this file.
+
+# Block operations
+Alt+3 = <>
+Shift+Alt+3 = <>
+Alt+4 = <> # IDLE default.
+Alt+5 = <>
+Alt+6 = <>
+
+# Tabs and other indent features
+Back = <>
+Ctrl+T = <>
+Alt+U = <>
+Enter = <>
+Tab = TabKey
+Shift-Tab = <>
+
+[Keys:Interactive]
+# Key bindings specific to the interactive window.
+# History for the interactive window
+Ctrl+Up = <>
+Ctrl+Down = <>
+Enter = ProcessEnter
+Ctrl+Enter = ProcessEnter
+Shift+Enter = ProcessEnter
+Esc = ProcessEsc
+Alt+I = WindowBack # Toggle back to previous window.
+Home = InteractiveHome # A sample Event defined in this file.
+Shift+Home = InteractiveHomeExtend # A sample Event defined in this file.
+
+# When docked, the Ctrl+Tab and Shift+Ctrl+Tab keys dont work as expected.
+Ctrl+Tab = MDINext
+Ctrl+Shift+Tab = MDIPrev
+
+[Extensions]
+# Python event handlers specific to this config file.
+# All functions not starting with an "_" are assumed
+# to be events, and take 2 params:
+# * editor_window is the same object passed to IDLE
+# extensions. editor_window.text is a text widget
+# that conforms to the Tk text widget interface.
+# * event is the event being fired. Will always be None
+# in the current implementation.
+
+# Simply by defining these functions, they are available as
+# events.
+# Note that we bind keystrokes to these events in the various
+# [Keys] sections.
+
+# Add a simple file/class/function simple banner
+def AddBanner(editor_window, event):
+
+ text = editor_window.text
+ big_line = "#" * 70
+ banner = "%s\n# \n# \n# \n%s\n" % (big_line, big_line)
+
+ # Insert at the start of the current line.
+ pos = text.index("insert linestart")
+
+ text.undo_block_start() # Allow action to be undone as a single unit.
+ text.insert(pos, banner)
+ text.undo_block_stop()
+
+ # Now set the insert point to the middle of the banner.
+ import string
+ line, col = map(int, string.split(pos, "."))
+ text.mark_set("insert", "%d.1 lineend" % (line+2, ) )
+
+
+# Here is a sample event bound to the "Home" key in the
+# interactive window
+def InteractiveHome(editor_window, event):
+ _DoInteractiveHome(editor_window.text, 0)
+
+def InteractiveHomeExtend(editor_window, event):
+ _DoInteractiveHome(editor_window.text, 1)
+
+def _DoInteractiveHome(text, extend):
+ import sys
+ of_interest = "insert linestart + %d c" % len(sys.ps1)
+ if not text.compare("insert", "==", of_interest) and \
+ text.get("insert linestart", of_interest) in [sys.ps1, sys.ps2]: # Not sys.ps? line
+ end = of_interest
+ else:
+ end = "insert linestart"
+
+ if extend: start = "insert"
+ else: start = end
+ text.tag_add("sel", start, end)
+
+
+# A couple of generic events.
+def Beep(editor_window, event):
+ editor_window.text.beep()
+
+def DoNothing(editor_window, event):
+ pass
+
+def ContinueEvent(editor_window, event):
+ # Almost an "unbind" - allows Pythonwin/MFC to handle the keystroke
+ return 1
+
diff --git a/Pythonwin/pywin/dialogs/__init__.py b/Pythonwin/pywin/dialogs/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/dialogs/ideoptions.py b/Pythonwin/pywin/dialogs/ideoptions.py
new file mode 100644
index 0000000000..89bf2216e2
--- /dev/null
+++ b/Pythonwin/pywin/dialogs/ideoptions.py
@@ -0,0 +1,111 @@
+# The property page to define generic IDE options for Pythonwin
+
+from pywin.mfc import dialog
+from pywin.framework import interact
+import win32ui
+import win32con
+
+buttonControlMap = {
+ win32ui.IDC_BUTTON1: win32ui.IDC_EDIT1,
+ win32ui.IDC_BUTTON2: win32ui.IDC_EDIT2,
+ win32ui.IDC_BUTTON3: win32ui.IDC_EDIT3,
+}
+
+class OptionsPropPage(dialog.PropertyPage):
+ def __init__(self):
+ dialog.PropertyPage.__init__(self, win32ui.IDD_PP_IDE)
+ self.AddDDX(win32ui.IDC_CHECK1, "bShowAtStartup")
+ self.AddDDX(win32ui.IDC_CHECK2, "bDocking")
+ self.AddDDX(win32ui.IDC_EDIT4, 'MRUSize', "i")
+
+ def OnInitDialog(self):
+
+ edit = self.GetDlgItem(win32ui.IDC_EDIT1)
+ edit.SetDefaultCharFormat(interact.formatInput)
+ edit.SetWindowText("Input Text")
+
+ edit = self.GetDlgItem(win32ui.IDC_EDIT2)
+ edit.SetDefaultCharFormat(interact.formatOutput)
+ edit.SetWindowText("Output Text")
+
+ edit = self.GetDlgItem(win32ui.IDC_EDIT3)
+ edit.SetDefaultCharFormat(interact.formatOutputError)
+ edit.SetWindowText("Error Text")
+
+ self['bShowAtStartup'] = interact.LoadPreference("Show at startup", 1)
+ self['bDocking'] = interact.LoadPreference("Docking", 0)
+ self['MRUSize'] = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
+
+ # Hook the button clicks.
+ self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON1)
+ self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON2)
+ self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON3)
+
+ # Ensure the spin control remains in range.
+ spinner = self.GetDlgItem(win32ui.IDC_SPIN1)
+ spinner.SetRange(1, 16)
+
+ return dialog.PropertyPage.OnInitDialog(self)
+
+ # Called to save away the new format tuple for the specified item.
+ def HandleCharFormatChange(self, id, code):
+ if code == win32con.BN_CLICKED:
+ editId = buttonControlMap.get(id)
+ assert editId is not None, "Format button has no associated edit control"
+ editControl = self.GetDlgItem(editId)
+ existingFormat = editControl.GetDefaultCharFormat()
+ flags = win32con.CF_SCREENFONTS
+ d=win32ui.CreateFontDialog(existingFormat, flags, None, self)
+ if d.DoModal()==win32con.IDOK:
+ cf = d.GetCharFormat()
+ editControl.SetDefaultCharFormat(cf)
+ self.SetModified(1)
+ return 0 # We handled this fully!
+
+ def OnOK(self):
+ # Handle the edit controls - get all the fonts, put them back into interact, then
+ # get interact to save its stuff!
+ controlAttrs = [(win32ui.IDC_EDIT1, "formatInput"), (win32ui.IDC_EDIT2, "formatOutput"), (win32ui.IDC_EDIT3, "formatOutputError")]
+ for id, attr in controlAttrs:
+ control = self.GetDlgItem(id)
+ fmt = control.GetDefaultCharFormat()
+ setattr(interact, attr, fmt)
+ interact.SaveFontPreferences()
+
+ # Save the other interactive window options.
+ interact.SavePreference("Show at startup", self['bShowAtStartup'])
+ interact.SavePreference("Docking", self['bDocking'])
+
+ # And the other options.
+ win32ui.WriteProfileVal("Settings","Recent File List Size", self['MRUSize'])
+
+ return 1
+ def ChangeFormat(self, fmtAttribute, fmt):
+ dlg = win32ui.CreateFontDialog(fmt)
+ if dlg.DoModal() <> win32con.IDOK: return None
+ return dlg.GetCharFormat()
+
+ def OnFormatTitle(self, command, code):
+ fmt = self.GetFormat(interact.formatTitle)
+ if fmt:
+ formatTitle = fmt
+ SaveFontPreferences()
+
+ def OnFormatInput(self, command, code):
+ global formatInput
+ fmt = self.GetFormat(formatInput)
+ if fmt:
+ formatInput = fmt
+ SaveFontPreferences()
+ def OnFormatOutput(self, command, code):
+ global formatOutput
+ fmt = self.GetFormat(formatOutput)
+ if fmt:
+ formatOutput = fmt
+ SaveFontPreferences()
+ def OnFormatError(self, command, code):
+ global formatOutputError
+ fmt = self.GetFormat(formatOutputError)
+ if fmt:
+ formatOutputError = fmt
+ SaveFontPreferences()
diff --git a/Pythonwin/pywin/dialogs/list.py b/Pythonwin/pywin/dialogs/list.py
new file mode 100644
index 0000000000..2cfa9d7d90
--- /dev/null
+++ b/Pythonwin/pywin/dialogs/list.py
@@ -0,0 +1,122 @@
+from pywin.mfc import dialog
+import win32ui, win32con, commctrl, win32api
+
+class ListDialog (dialog.Dialog):
+
+ def __init__ (self, title, list):
+ dialog.Dialog.__init__ (self, self._maketemplate(title))
+ self.HookMessage (self.on_size, win32con.WM_SIZE)
+ self.HookNotify(self.OnListItemChange, commctrl.LVN_ITEMCHANGED)
+ self.HookCommand(self.OnListClick, win32ui.IDC_LIST1)
+ self.items = list
+
+ def _maketemplate(self, title):
+ style = win32con.WS_DLGFRAME | win32con.WS_SYSMENU | win32con.WS_VISIBLE
+ ls = (
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE |
+ commctrl.LVS_ALIGNLEFT |
+ commctrl.LVS_REPORT
+ )
+ bs = (
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE
+ )
+ return [ [title, (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
+ ["SysListView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), ls],
+ [128, "OK", win32con.IDOK, (10, 0, 50, 14), bs | win32con.BS_DEFPUSHBUTTON],
+ [128, "Cancel",win32con.IDCANCEL,(0, 0, 50, 14), bs],
+ ]
+
+ def FillList(self):
+ size = self.GetWindowRect()
+ width = size[2] - size[0] - (10)
+ itemDetails = (commctrl.LVCFMT_LEFT, width, "Item", 0)
+ self.itemsControl.InsertColumn(0, itemDetails)
+ index = 0
+ for item in self.items:
+ index = self.itemsControl.InsertItem(index+1, str(item), 0)
+
+ def OnListClick(self, id, code):
+ if code==commctrl.NM_DBLCLK:
+ self.EndDialog(win32con.IDOK)
+ return 1
+
+ def OnListItemChange(self,std, extra):
+ (hwndFrom, idFrom, code), (itemNotify, sub, newState, oldState, change, point, lparam) = std, extra
+ oldSel = (oldState & commctrl.LVIS_SELECTED)<>0
+ newSel = (newState & commctrl.LVIS_SELECTED)<>0
+ if oldSel <> newSel:
+ try:
+ self.selecteditem = itemNotify
+ self.butOK.EnableWindow(1)
+ except win32ui.error:
+ self.selecteditem = None
+
+
+ def OnInitDialog (self):
+ rc = dialog.Dialog.OnInitDialog (self)
+ self.itemsControl = self.GetDlgItem(win32ui.IDC_LIST1)
+ self.butOK = self.GetDlgItem(win32con.IDOK)
+ self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
+
+ self.FillList()
+
+ size = self.GetWindowRect()
+ self.LayoutControls(size[2]-size[0], size[3]-size[1])
+ self.butOK.EnableWindow(0) # wait for first selection
+ return rc
+
+ def LayoutControls(self, w, h):
+ self.itemsControl.MoveWindow((0,0,w,h-30))
+ self.butCancel.MoveWindow((10, h-24, 60, h-4))
+ self.butOK.MoveWindow((w-60, h-24, w-10, h-4))
+
+ def on_size (self, params):
+ lparam = params[3]
+ w = win32api.LOWORD(lparam)
+ h = win32api.HIWORD(lparam)
+ self.LayoutControls(w, h)
+
+class ListsDialog(ListDialog):
+ def __init__(self, title, list, colHeadings = ['Item']):
+ ListDialog.__init__(self, title, list)
+ self.colHeadings = colHeadings
+
+ def FillList(self):
+ index = 0
+ size = self.GetWindowRect()
+ width = size[2] - size[0] - (10) - win32api.GetSystemMetrics(win32con.SM_CXVSCROLL)
+ numCols = len(self.colHeadings)
+
+ for col in self.colHeadings:
+ itemDetails = (commctrl.LVCFMT_LEFT, width/numCols, col, 0)
+ self.itemsControl.InsertColumn(index, itemDetails)
+ index = index + 1
+ index = 0
+ for items in self.items:
+ index = self.itemsControl.InsertItem(index+1, str(items[0]), 0)
+ for itemno in range(1,numCols):
+ item = items[itemno]
+ self.itemsControl.SetItemText(index, itemno, str(item))
+
+def SelectFromList (title, lst):
+ dlg = ListDialog(title, lst)
+ if dlg.DoModal()==win32con.IDOK:
+ return dlg.selecteditem
+ else:
+ return None
+
+def SelectFromLists (title, lists, headings):
+ dlg = ListsDialog(title, lists, headings)
+ if dlg.DoModal()==win32con.IDOK:
+ return dlg.selecteditem
+ else:
+ return None
+
+def test():
+# print SelectFromList('Single list', [1,2,3])
+ print SelectFromLists('Multi-List', [ ('1',1, 'a'), ('2',2, 'b'), ('3',3, 'c' )], ['Col 1', 'Col 2'])
+
+if __name__=='__main__':
+ test()
diff --git a/Pythonwin/pywin/dialogs/login.py b/Pythonwin/pywin/dialogs/login.py
new file mode 100644
index 0000000000..efaa15a272
--- /dev/null
+++ b/Pythonwin/pywin/dialogs/login.py
@@ -0,0 +1,121 @@
+'''login -- PythonWin user ID and password dialog box
+
+(Adapted from originally distributed with Mark Hammond's PythonWin -
+this now replaces it!)
+
+login.GetLogin() displays a modal "OK/Cancel" dialog box with input
+fields for a user ID and password. The password field input is masked
+with *'s. GetLogin takes two optional parameters, a window title, and a
+default user ID. If these parameters are omitted, the title defaults to
+"Login", and the user ID is left blank. GetLogin returns a (userid, password)
+tuple. GetLogin can be called from scripts running on the console - i.e. you
+don't need to write a full-blown GUI app to use it.
+
+login.GetPassword() is similar, except there is no username field.
+
+Example:
+import pywin.dialogs.login
+title = "FTP Login"
+def_user = "fred"
+userid, password = pywin.dialogs.login.GetLogin(title, def_user)
+
+Jim Eggleston, 28 August 1996
+Merged with dlgpass and moved to pywin.dialogs by Mark Hammond Jan 1998.
+'''
+
+import win32ui
+import win32api
+import win32con
+from pywin.mfc import dialog
+
+def MakeLoginDlgTemplate(title):
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE
+
+ # Window frame and title
+ dlg = [ [title, (0, 0, 184, 40), style, None, (8, "MS Sans Serif")], ]
+
+ # ID label and text box
+ dlg.append([130, "User ID:", -1, (7, 9, 69, 9), cs | win32con.SS_LEFT])
+ s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
+ dlg.append(['EDIT', None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s])
+
+ # Password label and text box
+ dlg.append([130, "Password:", -1, (7, 22, 69, 9), cs | win32con.SS_LEFT])
+ s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
+ dlg.append(['EDIT', None, win32ui.IDC_EDIT2, (50, 20, 60, 12), s | win32con.ES_PASSWORD])
+
+ # OK/Cancel Buttons
+ s = cs | win32con.WS_TABSTOP
+ dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
+ s = win32con.BS_PUSHBUTTON | s
+ dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 20, 50, 14), s])
+ return dlg
+
+def MakePasswordDlgTemplate(title):
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ cs = win32con.WS_CHILD | win32con.WS_VISIBLE
+ # Window frame and title
+ dlg = [ [title, (0, 0, 177, 45), style, None, (8, "MS Sans Serif")], ]
+
+ # Password label and text box
+ dlg.append([130, "Password:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
+ s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
+ dlg.append(['EDIT', None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s | win32con.ES_PASSWORD])
+
+ # OK/Cancel Buttons
+ s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
+ dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
+ dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
+ return dlg
+
+class LoginDlg(dialog.Dialog):
+ Cancel = 0
+ def __init__(self, title):
+ dialog.Dialog.__init__(self, MakeLoginDlgTemplate(title) )
+ self.AddDDX(win32ui.IDC_EDIT1,'userid')
+ self.AddDDX(win32ui.IDC_EDIT2,'password')
+
+def GetLogin(title='Login', userid='', password=''):
+ d = LoginDlg(title)
+ d['userid'] = userid
+ d['password'] = password
+ if d.DoModal() != win32con.IDOK:
+ return (None, None)
+ else:
+ return (d['userid'], d['password'])
+
+class PasswordDlg(dialog.Dialog):
+ def __init__(self, title):
+ dialog.Dialog.__init__(self, MakePasswordDlgTemplate(title) )
+ self.AddDDX(win32ui.IDC_EDIT1,'password')
+
+def GetPassword(title='Password', password=''):
+ d = PasswordDlg(title)
+ d['password'] = password
+ if d.DoModal()!=win32con.IDOK:
+ return None
+ return d['password']
+
+if __name__ == "__main__":
+ import sys
+ title = 'Login'
+ def_user = ''
+ if len(sys.argv) > 1:
+ title = sys.argv[1]
+ if len(sys.argv) > 2:
+ def_userid = sys.argv[2]
+ userid, password = GetLogin(title, def_user)
+ if userid == password == None:
+ print "User pressed Cancel"
+ else:
+ print "User ID: ", userid
+ print "Password:", password
+ newpassword = GetPassword("Reenter just for fun", password)
+ if newpassword is None:
+ print "User cancelled"
+ else:
+ what = ""
+ if newpassword != password:
+ what = "not "
+ print "The passwords did %smatch" % (what)
diff --git a/Pythonwin/pywin/dialogs/status.py b/Pythonwin/pywin/dialogs/status.py
new file mode 100644
index 0000000000..b8bb09e84a
--- /dev/null
+++ b/Pythonwin/pywin/dialogs/status.py
@@ -0,0 +1,106 @@
+# No cancel button.
+
+from pywin.mfc import dialog
+import win32ui
+import win32con
+
+def MakeProgressDlgTemplate(caption, staticText = ""):
+ style = (win32con.DS_MODALFRAME |
+ win32con.WS_POPUP |
+ win32con.WS_VISIBLE |
+ win32con.WS_CAPTION |
+ win32con.WS_SYSMENU |
+ win32con.DS_SETFONT)
+ cs = (win32con.WS_CHILD |
+ win32con.WS_VISIBLE)
+
+ w = 215
+ h = 36 # With button
+ h = 40
+
+ dlg = [[caption,
+ (0, 0, w, h),
+ style,
+ None,
+ (8, "MS Sans Serif")],
+ ]
+
+ s = win32con.WS_TABSTOP | cs
+
+ dlg.append([130, staticText, 1000, (7, 7, w-7, h-32), cs | win32con.SS_LEFT])
+
+# dlg.append([128,
+# "Cancel",
+# win32con.IDCANCEL,
+# (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
+
+ return dlg
+
+class CStatusProgressDialog(dialog.Dialog):
+ def __init__(self, title, msg = "", maxticks = 100, tickincr = 1):
+ self.initMsg = msg
+ templ = MakeProgressDlgTemplate(title, msg)
+ dialog.Dialog.__init__(self, templ)
+ self.maxticks = maxticks
+ self.tickincr = tickincr
+ self.pbar = None
+
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ self.static = self.GetDlgItem(1000)
+ self.pbar = win32ui.CreateProgressCtrl()
+ self.pbar.CreateWindow (win32con.WS_CHILD |
+ win32con.WS_VISIBLE,
+ (10, 30, 310, 44),
+ self, 1001)
+ self.pbar.SetRange(0, self.maxticks)
+ self.pbar.SetStep(self.tickincr)
+ self.progress = 0
+ self.pincr = 5
+ return rc
+
+ def Close(self):
+ self.EndDialog(0)
+
+ def SetMaxTicks(self, maxticks):
+ if self.pbar is not None:
+ self.pbar.SetRange(0, maxticks)
+
+ def Tick(self):
+ if self.pbar is not None:
+ self.pbar.StepIt()
+ win32ui.PumpWaitingMessages(0, -1)
+
+ def SetTitle(self, text):
+ self.SetWindowText(text)
+
+ def SetText(self, text):
+ self.SetDlgItemText(1000, text)
+
+ def Set(self, pos, max = None):
+ if self.pbar is not None:
+ self.pbar.SetPos(pos)
+ win32ui.PumpWaitingMessages(0, -1)
+ if max is not None:
+ self.pbar.SetRange(0, max)
+
+
+def StatusProgressDialog(title, msg = "", maxticks = 100, parent = None):
+ d = CStatusProgressDialog (title, msg, maxticks)
+ d.CreateWindow (parent)
+ return d
+
+def demo():
+ d = StatusProgressDialog("A Demo", "Doing something...")
+ import win32api
+ for i in range(100):
+ if i == 50:
+ d.SetText("Getting there...")
+ if i==90:
+ d.SetText("Nearly done...")
+ win32api.Sleep(20)
+ d.Tick()
+ d.Close()
+
+if __name__=='__main__':
+ demo()
diff --git a/Pythonwin/pywin/docking/DockingBar.py b/Pythonwin/pywin/docking/DockingBar.py
new file mode 100644
index 0000000000..7040c45ed3
--- /dev/null
+++ b/Pythonwin/pywin/docking/DockingBar.py
@@ -0,0 +1,526 @@
+# DockingBar.py
+
+# Ported directly (comments and all) from the samples at www.codeguru.com
+
+# WARNING: Use at your own risk, as this interface is highly likely to change.
+# Currently we support only one child per DockingBar. Later we need to add
+# support for multiple children.
+
+import win32api, win32con, win32ui
+from pywin.mfc import afxres, window
+import struct
+
+clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT)
+clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
+
+def CenterPoint(rect):
+ width = rect[2]-rect[0]
+ height = rect[3]-rect[1]
+ return rect[0] + width/2, rect[1] + height/2
+
+def OffsetRect(rect, (x, y) ):
+ return rect[0]+x, rect[1]+y, rect[2]+x, rect[3]+y
+
+def DeflateRect(rect, (x,y) ):
+ return rect[0]+x, rect[1]+y, rect[2]-x, rect[3]-y
+
+def PtInRect(rect, pt):
+ return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3]
+
+class DockingBar(window.Wnd):
+ def __init__(self, obj=None):
+ if obj is None:
+ obj = win32ui.CreateControlBar()
+ window.Wnd.__init__(self, obj)
+ self.dialog = None
+ self.nDockBarID = 0
+ self.sizeMin = 32, 32
+ self.sizeHorz = 200, 200
+ self.sizeVert = 200, 200
+ self.sizeFloat = 200, 200
+ self.bTracking = 0
+ self.bInRecalcNC = 0
+ self.cxEdge = 6
+ self.cxBorder = 3
+ self.cxGripper = 20
+ self.brushBkgd = win32ui.CreateBrush()
+ self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE))
+
+ # Support for diagonal resizing
+ self.cyBorder = 3
+ self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION)
+ self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN)
+ self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN)
+
+ def OnUpdateCmdUI(self, target, bDisableIfNoHndler):
+ return self.UpdateDialogControls(target, bDisableIfNoHndler)
+
+ def CreateWindow(self, parent, childCreator, title, id, style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT, childCreatorArgs=()):
+ assert not ((style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC)), "Invalid style"
+
+ # save the style
+ self._obj_.dwStyle = style & afxres.CBRS_ALL
+
+ cursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
+ wndClass = win32ui.RegisterWndClass(win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0)
+
+ self._obj_.CreateWindow(wndClass, title, style, (0,0,0,0), parent, id)
+
+ # Create the child dialog
+ self.dialog = apply(childCreator, (self,) + childCreatorArgs)
+
+ # use the dialog dimensions as default base dimensions
+ assert self.dialog.IsWindow(), "The childCreator function %s did not create a window!" % childCreator
+ rect = self.dialog.GetWindowRect()
+ self.sizeHorz = self.sizeVert = self.sizeFloat = rect[2]-rect[0], rect[3]-rect[1]
+
+ self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder
+ self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1]
+ self.HookMessages()
+
+ def CalcFixedLayout(self, bStretch, bHorz):
+ rectTop = self.dockSite.GetControlBar(afxres.AFX_IDW_DOCKBAR_TOP).GetWindowRect()
+ rectLeft = self.dockSite.GetControlBar(afxres.AFX_IDW_DOCKBAR_LEFT).GetWindowRect()
+ if bStretch:
+ nHorzDockBarWidth = 32767
+ nVertDockBarHeight = 32767
+ else:
+ nHorzDockBarWidth = rectTop[2]-rectTop[0] + 4
+ nVertDockBarHeight = rectLeft[3]-rectLeft[1] + 4
+
+ if self.IsFloating():
+ return self.sizeFloat
+ if bHorz:
+ return nHorzDockBarWidth, self.sizeHorz[1]
+ return self.sizeVert[0], nVertDockBarHeight
+
+ def CalcDynamicLayout(self, length, mode):
+ # Support for diagonal sizing.
+ if self.IsFloating():
+ self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0)
+ if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK):
+ flags = win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOZORDER |\
+ win32con.SWP_NOACTIVATE | win32con.SWP_FRAMECHANGED
+ self.SetWindowPos(0, (0, 0, 0, 0,), flags)
+ self.dockSite.RecalcLayout()
+ return self._obj_.CalcDynamicLayout(length, mode)
+
+ if mode & win32ui.LM_MRUWIDTH:
+ return self.sizeFloat
+ if mode & win32ui.LM_COMMIT:
+ self.sizeFloat = length, self.sizeFloat[1]
+ return self.sizeFloat
+ # More diagonal sizing.
+ if self.IsFloating():
+ dc = self.dockContext
+ pt = win32api.GetCursorPos()
+ windowRect = self.GetParent().GetParent().GetWindowRect()
+
+ hittest = dc.nHitTest
+ if hittest==win32con.HTTOPLEFT:
+ cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
+ cy = max(windowRect[3] - self.cCaptionSize - pt[1],self.cMinHeight) - 1
+ self.sizeFloat = cx, cy
+
+ top = min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) - self.cyBorder
+ left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
+ dc.rectFrameDragHorz = left, top, dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
+ return self.sizeFloat
+ if hittest==win32con.HTTOPRIGHT:
+ cx = max(pt[0] - windowRect[0], self.cMinWidth)
+ cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
+ self.sizeFloat = cx, cy
+
+ top = min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) - self.cyBorder
+ dc.rectFrameDragHorz = dc.rectFrameDragHorz[0], top, dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
+ return self.sizeFloat
+
+ if hittest==win32con.HTBOTTOMLEFT:
+ cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
+ cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
+ self.sizeFloat = cx, cy
+
+ left = min(pt[0], windowRect[2] -self.cMinWidth) - 1
+ dc.rectFrameDragHorz = left, dc.rectFrameDragHorz[1], dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
+ return self.sizeFloat
+
+ if hittest==win32con.HTBOTTOMRIGHT:
+ cx = max(pt[0] - windowRect[0], self.cMinWidth)
+ cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
+ self.sizeFloat = cx, cy
+ return self.sizeFloat
+
+ if mode & win32ui.LM_LENGTHY:
+ self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length)
+ return self.sizeFloat
+ else:
+ return max(self.sizeMin[0], length), self.sizeFloat[1]
+
+ def OnWindowPosChanged(self, msg):
+ if self.GetSafeHwnd()==0 or self.dialog is None:
+ return 0
+ lparam = msg[3]
+ format = "iiiiiii"
+ bytes = win32ui.GetBytes( lparam, struct.calcsize(format) )
+ hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes)
+
+ if self.bInRecalcNC:
+ rc = self.GetClientRect()
+ self.dialog.MoveWindow(rc)
+ return 0
+ # Find on which side are we docked
+ nDockBarID = self.GetParent().GetDlgCtrlID()
+ # Return if dropped at same location
+ # no docking side change and no size change
+ if (nDockBarID == self.nDockBarID) and \
+ (flags & win32con.SWP_NOSIZE) and \
+ ((self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY):
+ return
+ self.nDockBarID = nDockBarID
+
+ # Force recalc the non-client area
+ self.bInRecalcNC = 1
+ try:
+ swpflags = win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED
+ self.SetWindowPos(0, (0,0,0,0), swpflags)
+ finally:
+ self.bInRecalcNC = 0
+ return 0
+
+ # This is a virtual and not a message hook.
+ def OnSetCursor(self, window, nHitTest, wMouseMsg):
+ if nHitTest != win32con.HTSIZE or self.bTracking:
+ return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg)
+
+ if self.IsHorz():
+ win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS))
+ else:
+ win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE))
+ return 1
+
+ # Mouse Handling
+ def OnLButtonUp(self, msg):
+ if not self.bTracking:
+ return 1 # pass it on.
+ self.StopTracking(1)
+ return 0 # Dont pass on
+
+ def OnLButtonDown(self, msg):
+ # UINT nFlags, CPoint point)
+ # only start dragging if clicked in "void" space
+ if self.dockBar is not None:
+ # start the drag
+ pt = msg[5]
+ pt = self.ClientToScreen(pt)
+ self.dockContext.StartDrag(pt)
+ return 0
+ return 1
+
+ def OnNcLButtonDown(self, msg):
+ if self.bTracking: return 0
+ nHitTest = wparam = msg[2]
+ pt = msg[5]
+
+ if nHitTest==win32con.HTSYSMENU and not self.IsFloating():
+ self.GetDockingFrame().ShowControlBar(self, 0, 0)
+ elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating():
+ self.dockContext.ToggleDocking()
+ elif nHitTest == win32con.HTCAPTION and not self.IsFloating() and self.dockBar is not None:
+ self.dockContext.StartDrag(pt)
+ elif nHitTest == win32con.HTSIZE and not self.IsFloating():
+ self.StartTracking()
+ else:
+ return 1
+ return 0
+
+ def OnLButtonDblClk(self, msg):
+ # only toggle docking if clicked in "void" space
+ if self.dockBar is not None:
+ # toggle docking
+ self.dockContext.ToggleDocking()
+ return 0
+ return 1
+
+ def OnNcLButtonDblClk(self, msg):
+ nHitTest = wparam = msg[2]
+ # UINT nHitTest, CPoint point)
+ if self.dockBar is not None and nHitTest == win32con.HTCAPTION:
+ # toggle docking
+ self.dockContext.ToggleDocking()
+ return 0
+ return 1
+
+ def OnMouseMove(self, msg):
+ flags = wparam = msg[2]
+ lparam = msg[3]
+ if self.IsFloating() or not self.bTracking:
+ return 1
+
+ # Convert unsigned 16 bit to signed 32 bit.
+ x=win32api.LOWORD(lparam)
+ if x & 0x8000: x = x | 0xFFFF0000
+ y = win32api.HIWORD(lparam)
+ if y & 0x8000: y = y | 0xFFFF0000
+ pt = x, y
+ cpt = CenterPoint(self.rectTracker)
+ pt = self.ClientToWnd(pt)
+ if self.IsHorz():
+ if cpt[1] != pt[1]:
+ self.OnInvertTracker(self.rectTracker)
+ self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1]))
+ self.OnInvertTracker(self.rectTracker)
+ else:
+ if cpt[0] != pt[0]:
+ self.OnInvertTracker(self.rectTracker)
+ self.rectTracker = OffsetRect(self.rectTracker, (pt[0]-cpt[0], 0))
+ self.OnInvertTracker(self.rectTracker)
+
+ return 0 # Dont pass it on.
+
+# def OnBarStyleChange(self, old, new):
+
+ def OnNcCalcSize(self, bCalcValid, (rc0, rc1, rc2, pos)):
+ self.rectBorder = self.GetWindowRect()
+ self.rectBorder = OffsetRect( self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1]) )
+
+ dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY
+
+ if self.nDockBarID==afxres.AFX_IDW_DOCKBAR_TOP:
+ dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM;
+ rc0.left = rc0.left + self.cxGripper
+ rc0.bottom = rc0.bottom-self.cxEdge
+ rc0.top = rc0.top + self.cxBorder
+ rc0.right = rc0.right - self.cxBorder
+ self.rectBorder = self.rectBorder[0], self.rectBorder[3]-self.cxEdge, self.rectBorder[2], self.rectBorder[3]
+ elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_BOTTOM:
+ dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP
+ rc0.left = rc0.left + self.cxGripper
+ rc0.top = rc0.top + self.cxEdge
+ rc0.bottom = rc0.bottom - self.cxBorder
+ rc0.right = rc0.right - self.cxBorder
+ self.rectBorder = self.rectBorder[0], self.rectBorder[1], self.rectBorder[2], self.rectBorder[1]+self.cxEdge
+ elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_LEFT:
+ dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT
+ rc0.right = rc0.right - self.cxEdge
+ rc0.left = rc0.left + self.cxBorder
+ rc0.bottom = rc0.bottom - self.cxBorder
+ rc0.top = rc0.top + self.cxGripper
+ self.rectBorder = self.rectBorder[2] - self.cxEdge, self.rectBorder[1], self.rectBorder[2], self.rectBorder[3]
+ elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_RIGHT:
+ dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT
+ rc0.left = rc0.left + self.cxEdge
+ rc0.right = rc0.right - self.cxBorder
+ rc0.bottom = rc0.bottom - self.cxBorder
+ rc0.top = rc0.top + self.cxGripper
+ self.rectBorder = self.rectBorder[0], self.rectBorder[1], self.rectBorder[0]+self.cxEdge, self.rectBorder[3]
+ else:
+ self.rectBorder = 0,0,0,0
+
+ self.SetBarStyle(dwBorderStyle)
+ return 0
+
+ def OnNcPaint(self, msg):
+ self.EraseNonClient()
+ dc = self.GetWindowDC()
+ ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT)
+ cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
+ dc.Draw3dRect(self.rectBorder, ctl, cbr)
+
+ self.DrawGripper(dc)
+
+ rect = self.GetClientRect()
+ self.InvalidateRect( rect, 1)
+ return 0
+
+ def OnNcHitTest(self, pt): # A virtual, not a hooked message.
+ if self.IsFloating():
+ return 1
+
+ ptOrig = pt
+ rect = self.GetWindowRect()
+ pt = pt[0] - rect[0], pt[1] - rect[1]
+
+ if PtInRect(self.rectClose, pt):
+ return win32con.HTSYSMENU
+ elif PtInRect(self.rectUndock, pt):
+ return win32con.HTMINBUTTON
+ elif PtInRect(self.rectGripper, pt):
+ return win32con.HTCAPTION
+ elif PtInRect(self.rectBorder, pt):
+ return win32con.HTSIZE
+ else:
+ return self._obj_.OnNcHitTest(ptOrig)
+
+ def StartTracking(self):
+ self.SetCapture()
+
+ # make sure no updates are pending
+ self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW)
+ self.dockSite.LockWindowUpdate()
+
+ self.ptOld = CenterPoint(self.rectBorder)
+ self.bTracking = 1
+
+ self.rectTracker = self.rectBorder;
+ if not self.IsHorz():
+ l, t, r, b = self.rectTracker
+ b = b - 4
+ self.rectTracker = l, t, r, b
+
+ self.OnInvertTracker(self.rectTracker);
+
+ def OnCaptureChanged(self, msg):
+ hwnd = lparam = msg[3]
+ if self.bTracking and hwnd != self.GetSafeHwnd():
+ self.StopTracking(0) # cancel tracking
+ return 1
+
+ def StopTracking(self, bAccept):
+ self.OnInvertTracker(self.rectTracker)
+ self.dockSite.UnlockWindowUpdate()
+ self.bTracking = 0
+ self.ReleaseCapture()
+ if not bAccept: return
+
+ rcc = self.dockSite.GetWindowRect()
+ if self.IsHorz():
+ newsize = self.sizeHorz[1]
+ maxsize = newsize + (rcc[3]-rcc[1])
+ minsize = self.sizeMin[1]
+ else:
+ newsize = self.sizeVert[0]
+ maxsize = newsize + (rcc[2]-rcc[0])
+ minsize = self.sizeMin[0]
+
+ pt = CenterPoint(self.rectTracker)
+ if self.nDockBarID== afxres.AFX_IDW_DOCKBAR_TOP:
+ newsize = newsize + (pt[1] - self.ptOld[1])
+ elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_BOTTOM:
+ newsize = newsize + (- pt[1] + self.ptOld[1])
+ elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_LEFT:
+ newsize = newsize + (pt[0] - self.ptOld[0])
+ elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_RIGHT:
+ newsize = newsize + (- pt[0] + self.ptOld[0])
+ newsize = max(minsize, min(maxsize, newsize))
+ if self.IsHorz():
+ self.sizeHorz = self.sizeHorz[0], newsize
+ else:
+ self.sizeVert = newsize, self.sizeVert[1]
+ self.dockSite.RecalcLayout()
+ return 0
+
+ def OnInvertTracker(self, rect):
+ assert rect[2]-rect[0]>0 and rect[3]-rect[1]>0, "rect is empty"
+ assert self.bTracking
+ rcc = self.GetWindowRect()
+ rcf = self.dockSite.GetWindowRect()
+
+ rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1]))
+ rect = DeflateRect(rect, (1, 1));
+
+ flags = win32con.DCX_WINDOW|win32con.DCX_CACHE|win32con.DCX_LOCKWINDOWUPDATE
+ dc = self.dockSite.GetDCEx(None, flags)
+ try:
+ brush = win32ui.GetHalftoneBrush()
+ oldBrush = dc.SelectObject(brush)
+
+ dc.PatBlt((rect[0], rect[1]), (rect[2]-rect[0], rect[3]-rect[1]), win32con.PATINVERT)
+ dc.SelectObject(oldBrush)
+ finally:
+ self.dockSite.ReleaseDC(dc)
+
+ def IsHorz(self):
+ return self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP or \
+ self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM
+
+ def ClientToWnd(self, pt):
+ x, y=pt
+ if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
+ y = y + self.cxEdge
+ elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
+ x = x + self.cxEdge
+ return x,y
+
+ def DrawGripper(self, dc):
+ # no gripper if floating
+ if self._obj_.dwStyle & afxres.CBRS_FLOATING:
+ return
+
+ # -==HACK==-
+ # in order to calculate the client area properly after docking,
+ # the client area must be recalculated twice (I have no idea why)
+ self.dockSite.RecalcLayout()
+ # -==END HACK==-
+
+ gripper = self.GetWindowRect()
+ gripper = self.ScreenToClient( gripper )
+ gripper = OffsetRect( gripper, (-gripper[0], -gripper[1]) )
+ gl, gt, gr, gb = gripper
+
+ if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ:
+ # gripper at left
+ self.rectGripper = gl, gt + 40, gl+20, gb
+ # draw close box
+ self.rectClose = gl+7, gt + 10, gl+19, gt+22
+ dc.DrawFrameControl(self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE)
+ # draw docking toggle box
+ self.rectUndock = OffsetRect(self.rectClose, (0,13))
+ dc.DrawFrameControl(self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX);
+
+ gt = gt + 38
+ gb = gb - 10
+ gl = gl + 10
+ gr = gl + 3
+ gripper = gl, gt, gr, gb
+ dc.Draw3dRect( gripper, clrBtnHilight, clrBtnShadow )
+ dc.Draw3dRect( OffsetRect(gripper, (4,0)), clrBtnHilight, clrBtnShadow )
+ else:
+ # gripper at top
+ self.rectGripper = gl, gt, gr-40, gt+20
+ # draw close box
+ self.rectClose = gr-21, gt+7, gr-10, gt+18
+ dc.DrawFrameControl(self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE)
+ # draw docking toggle box
+ self.rectUndock = OffsetRect( self.rectClose, (-13,0) )
+ dc.DrawFrameControl(self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX)
+ gr = gr - 38;
+ gl = gl + 10
+ gt = gt + 10
+ gb = gt + 3
+
+ gripper = gl, gt, gr, gb
+ dc.Draw3dRect( gripper, clrBtnHilight, clrBtnShadow )
+ dc.Draw3dRect( OffsetRect(gripper, (0,4) ), clrBtnHilight, clrBtnShadow )
+
+ def HookMessages(self):
+ self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP)
+ self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN)
+ self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK)
+ self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN)
+ self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK)
+ self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE)
+ self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT)
+ self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED)
+ self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED)
+# self.HookMessage(self.OnSize, win32con.WM_SIZE)
+
+def EditCreator(parent):
+ d = win32ui.CreateEdit()
+ es = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | win32con.ES_MULTILINE | win32con.ES_WANTRETURN
+ d.CreateWindow( es, (0,0,150,150), parent, 1000)
+ return d
+
+def test():
+ import pywin.mfc.dialog
+ global bar
+ bar = DockingBar()
+ creator = EditCreator
+ bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo")
+# win32ui.GetMainFrame().ShowControlBar(bar, 1, 0)
+ bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
+ bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
+
+
+if __name__=='__main__':
+ test()
diff --git a/Pythonwin/pywin/docking/__init__.py b/Pythonwin/pywin/docking/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/framework/__init__.py b/Pythonwin/pywin/framework/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/framework/app.py b/Pythonwin/pywin/framework/app.py
new file mode 100644
index 0000000000..f157561604
--- /dev/null
+++ b/Pythonwin/pywin/framework/app.py
@@ -0,0 +1,372 @@
+# App.py
+# Application stuff.
+# The application is responsible for managing the main frame window.
+#
+# We also grab the FileOpen command, to invoke our Python editor
+" The PythonWin application code. Manages most aspects of MDI, etc "
+import win32con
+import win32api
+import win32ui
+import sys
+import string
+import os
+from pywin.mfc import window, dialog, thread, afxres
+import traceback
+from pywin.framework import scriptutils
+
+## NOTE: App and AppBuild should NOT be used - instead, you should contruct your
+## APP class manually whenever you like (just ensure you leave these 2 params None!)
+## Whoever wants the generic "Application" should get it via win32iu.GetApp()
+
+# These are "legacy"
+AppBuilder = None
+App = None # default - if used, must end up a CApp derived class.
+
+# Helpers that should one day be removed!
+def AddIdleHandler(handler):
+ print "app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead."
+ return win32ui.GetApp().AddIdleHandler(handler)
+def DeleteIdleHandler(handler):
+ print "app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead."
+ return win32ui.GetApp().DeleteIdleHandler(handler)
+
+# Helper for writing a Window position by name, and later loading it.
+def SaveWindowSize(section,rect,state=""):
+ """ Writes a rectangle to an INI file
+ Args: section = section name in the applications INI file
+ rect = a rectangle in a (cy, cx, y, x) tuple
+ (same format as CREATESTRUCT position tuples)."""
+ left, top, right, bottom = rect
+ if state: state = state + " "
+ win32ui.WriteProfileVal(section,state+"left",left)
+ win32ui.WriteProfileVal(section,state+"top",top)
+ win32ui.WriteProfileVal(section,state+"right",right)
+ win32ui.WriteProfileVal(section,state+"bottom",bottom)
+
+def LoadWindowSize(section, state=""):
+ """ Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)"""
+ if state: state = state + " "
+ left = win32ui.GetProfileVal(section,state+"left",0)
+ top = win32ui.GetProfileVal(section,state+"top",0)
+ right = win32ui.GetProfileVal(section,state+"right",0)
+ bottom = win32ui.GetProfileVal(section,state+"bottom",0)
+ return (left, top, right, bottom)
+
+def RectToCreateStructRect(rect):
+ return (rect[3]-rect[1], rect[2]-rect[0], rect[1], rect[0] )
+
+
+# Define FrameWindow and Application objects
+#
+# The Main Frame of the application.
+class MainFrame(window.MDIFrameWnd):
+ sectionPos = "Main Window"
+ statusBarIndicators = ( afxres.ID_SEPARATOR, #// status line indicator
+ afxres.ID_INDICATOR_CAPS,
+ afxres.ID_INDICATOR_NUM,
+ afxres.ID_INDICATOR_SCRL,
+ win32ui.ID_INDICATOR_LINENUM,
+ win32ui.ID_INDICATOR_COLNUM )
+
+ def OnCreate(self, cs):
+ self._CreateStatusBar()
+ return 0
+
+ def _CreateStatusBar(self):
+ self.statusBar = win32ui.CreateStatusBar(self)
+ self.statusBar.SetIndicators(self.statusBarIndicators)
+ self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM)
+ self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM)
+
+ def OnUpdatePosIndicator(self, cmdui):
+ editControl = scriptutils.GetActiveEditControl()
+ value = " " * 5
+ if editControl is not None:
+ try:
+ startChar, endChar = editControl.GetSel()
+ lineNo = editControl.LineFromChar(startChar)
+ colNo = endChar - editControl.LineIndex(lineNo)
+
+ if cmdui.m_nID==win32ui.ID_INDICATOR_LINENUM:
+ value = "%0*d" % (5, lineNo + 1)
+ else:
+ value = "%0*d" % (3, colNo + 1)
+ except win32ui.error:
+ pass
+ cmdui.SetText(value)
+ cmdui.Enable()
+
+ def PreCreateWindow(self, cc):
+ cc = self._obj_.PreCreateWindow(cc)
+ pos = LoadWindowSize(self.sectionPos)
+ self.startRect = pos
+ if pos[2] - pos[0]:
+ rect = RectToCreateStructRect(pos)
+ cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
+ return cc
+
+ def OnDestroy(self, msg):
+ # use GetWindowPlacement(), as it works even when min'd or max'd
+ rectNow = self.GetWindowPlacement()[4]
+ if rectNow != self.startRect:
+ SaveWindowSize(self.sectionPos, rectNow)
+ return 0
+
+class CApp(thread.WinApp):
+ " A class for the application "
+ def __init__(self):
+ self.oldCallbackCaller = None
+ thread.WinApp.__init__(self, win32ui.GetApp() )
+ self.idleHandlers = []
+
+ def InitInstance(self):
+ " Called to crank up the app "
+ numMRU = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
+ win32ui.LoadStdProfileSettings(numMRU)
+# self._obj_.InitMDIInstance()
+ if win32api.GetVersionEx()[0]<4:
+ win32ui.SetDialogBkColor()
+ win32ui.Enable3dControls()
+
+ # install a "callback caller" - a manager for the callbacks
+# self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager)
+ self.LoadMainFrame()
+ self.SetApplicationPaths()
+
+ def ExitInstance(self):
+ " Called as the app dies - too late to prevent it here! "
+ win32ui.OutputDebug("Application shutdown\n")
+ # Restore the callback manager, if any.
+ try:
+ win32ui.InstallCallbackCaller(self.oldCallbackCaller)
+ except AttributeError:
+ pass
+ if self.oldCallbackCaller:
+ del self.oldCallbackCaller
+ self.frame=None # clean Python references to the now destroyed window object.
+ self.idleHandlers = []
+ # Attempt cleanup if not already done!
+ if self._obj_: self._obj_.AttachObject(None)
+ self._obj_ = None
+ global App
+ global AppBuilder
+ App = None
+ AppBuilder = None
+ return 0
+
+ def HaveIdleHandler(self, handler):
+ return handler in self.idleHandlers
+ def AddIdleHandler(self, handler):
+ self.idleHandlers.append(handler)
+ def DeleteIdleHandler(self, handler):
+ self.idleHandlers.remove(handler)
+ def OnIdle(self, count):
+ try:
+ ret = 0
+ handlers = self.idleHandlers[:] # copy list, as may be modified during loop
+ for handler in handlers:
+ try:
+ thisRet = handler(handler, count)
+ except:
+ print "Idle handler %s failed" % (`handler`)
+ traceback.print_exc()
+ print "Idle handler removed from list"
+ self.DeleteIdleHandler(handler)
+ thisRet = 0
+ ret = ret or thisRet
+ return ret
+ except KeyboardInterrupt:
+ pass
+ def CreateMainFrame(self):
+ return MainFrame()
+
+ def LoadMainFrame(self):
+ " Create the main applications frame "
+ self.frame = self.CreateMainFrame()
+ self.SetMainFrame(self.frame)
+ self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW)
+ self.frame.DragAcceptFiles() # we can accept these.
+ self.frame.ShowWindow(win32ui.GetInitialStateRequest())
+ self.frame.UpdateWindow()
+ self.HookCommands()
+
+ def OnHelp(self,id, code):
+ try:
+ import regutil
+ if id==win32ui.ID_HELP_GUI_REF:
+ helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference")
+ helpCmd = win32con.HELP_CONTENTS
+ else:
+ helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation")
+ helpCmd = win32con.HELP_FINDER
+ if helpFile is None:
+ win32ui.MessageBox("The help file is not registered!")
+ else:
+ import help
+ help.OpenHelpFile(helpFile, helpCmd)
+ except:
+ t, v, tb = sys.exc_info()
+ win32ui.MessageBox("Internal error in help file processing\r\n%s: %s" % (t,v))
+
+ def DoLoadModules(self, modules):
+ # XXX - this should go, but the debugger uses it :-(
+ # dont do much checking!
+ for module in modules:
+ __import__(module)
+
+ def HookCommands(self):
+ self.frame.HookMessage(self.OnDropFiles,win32con.WM_DROPFILES)
+ self.HookCommand(self.HandleOnFileOpen,win32ui.ID_FILE_OPEN)
+ self.HookCommand(self.HandleOnFileNew,win32ui.ID_FILE_NEW)
+ self.HookCommand(self.OnFileMRU,win32ui.ID_FILE_MRU_FILE1)
+ self.HookCommand(self.OnHelpAbout,win32ui.ID_APP_ABOUT)
+ self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON)
+ self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF)
+ # Hook for the right-click menu.
+ self.frame.GetWindow(win32con.GW_CHILD).HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
+
+ def SetApplicationPaths(self):
+ # Load the users/application paths
+ new_path = []
+ apppath=string.splitfields(win32ui.GetProfileVal('Python','Application Path',''),';')
+ for path in apppath:
+ if len(path)>0:
+ new_path.append(win32ui.FullPath(path))
+ for extra_num in range(1,11):
+ apppath=string.splitfields(win32ui.GetProfileVal('Python','Application Path %d'%extra_num,''),';')
+ if len(apppath) == 0:
+ break
+ for path in apppath:
+ if len(path)>0:
+ new_path.append(win32ui.FullPath(path))
+ sys.path = new_path + sys.path
+
+ def OnRClick(self,params):
+ " Handle right click message "
+ # put up the entire FILE menu!
+ menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0)
+ menu.TrackPopupMenu(params[5]) # track at mouse position.
+ return 0
+
+ def OnDropFiles(self,msg):
+ " Handle a file being dropped from file manager "
+ hDropInfo = msg[2]
+ self.frame.SetActiveWindow() # active us
+ nFiles = win32api.DragQueryFile(hDropInfo)
+ try:
+ for iFile in range(0,nFiles):
+ fileName = win32api.DragQueryFile(hDropInfo, iFile)
+ win32ui.GetApp().OpenDocumentFile( fileName )
+ finally:
+ win32api.DragFinish(hDropInfo);
+
+ return 0
+
+# No longer used by Pythonwin, as the C++ code has this same basic functionality
+# but handles errors slightly better.
+# It all still works, tho, so if you need similar functionality, you can use it.
+# Therefore I havent deleted this code completely!
+# def CallbackManager( self, ob, args = () ):
+# """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK'
+# to the frame-work. """
+# import traceback
+# try:
+# ret = apply(ob, args)
+# return ret
+# except:
+# # take copies of the exception values, else other (handled) exceptions may get
+# # copied over by the other fns called.
+# win32ui.SetStatusText('An exception occured in a windows command handler.')
+# t, v, tb = sys.exc_info()
+# traceback.print_exception(t, v, tb.tb_next)
+# try:
+# sys.stdout.flush()
+# except (NameError, AttributeError):
+# pass
+
+ # Command handlers.
+ def OnFileMRU( self, id, code ):
+ " Called when a File 1-n message is recieved "
+ fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1]
+ win32ui.GetApp().OpenDocumentFile(fileName)
+
+ def HandleOnFileOpen( self, id, code ):
+ " Called when FileOpen message is received "
+ win32ui.GetApp().OnFileOpen()
+
+ def HandleOnFileNew( self, id, code ):
+ " Called when FileNew message is received "
+ win32ui.GetApp().OnFileNew()
+
+ def OnHelpAbout( self, id, code ):
+ " Called when HelpAbout message is received. Displays the About dialog. "
+ win32ui.InitRichEdit()
+ dlg=AboutBox()
+ dlg.DoModal()
+
+scintilla = "Scintilla is Copyright 1998-1999 Neil Hodgson (http://www.hare.net.au/~neilh/ScintillaDownload.html)"
+idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others."
+contributors = "Thanks to the following people for making significant contributions: Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson. (let me know if I have forgotten you!)"
+# The About Box
+class AboutBox(dialog.Dialog):
+ def __init__(self, idd=win32ui.IDD_ABOUTBOX):
+ dialog.Dialog.__init__(self, idd)
+ def OnInitDialog(self):
+ text = "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s" % (win32ui.copyright, sys.copyright, scintilla, idle, contributors)
+ self.SetDlgItemText(win32ui.IDC_EDIT1, text)
+ # Get the build number
+ try:
+ ver = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\Pythonwin\\Build")
+ except:
+ ver = "number is unknown"
+ self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, "Pythonwin build " + ver)
+
+def Win32RawInput(prompt=None):
+ "Provide raw_input() for gui apps"
+ # flush stderr/out first.
+ try:
+ sys.stdout.flush()
+ sys.stderr.flush()
+ except:
+ pass
+ if prompt is None: prompt = ""
+ ret=dialog.GetSimpleInput(prompt)
+ if ret==None:
+ raise KeyboardInterrupt, "operation cancelled"
+ return ret
+
+def Win32Input(prompt=None):
+ "Provide input() for gui apps"
+ return eval(raw_input(prompt))
+
+sys.modules['__builtin__'].raw_input=Win32RawInput
+sys.modules['__builtin__'].input=Win32Input
+
+try:
+ # LocatePythonFile used to be here.
+ # THIS WILL BE DELETED SOON.
+ from scriptutils import LocatePythonFile
+except:
+ pass
+
+def HaveGoodGUI():
+ """Returns true if we currently have a good gui available.
+ """
+ return sys.modules.has_key("pywin.framework.startup")
+
+def CreateDefaultGUI( appClass = None):
+ """Creates a default GUI environment
+ """
+ if appClass is None:
+ import intpyapp # Bring in the default app - could be param'd later.
+ appClass = intpyapp.InteractivePythonApp
+ # Create and init the app.
+ appClass().InitInstance()
+
+def CheckCreateDefaultGUI():
+ """Checks and creates if necessary a default GUI environment.
+ """
+ rc = HaveGoodGUI()
+ if not rc:
+ CreateDefaultGUI()
+ return rc
diff --git a/Pythonwin/pywin/framework/bitmap.py b/Pythonwin/pywin/framework/bitmap.py
new file mode 100644
index 0000000000..641ea84773
--- /dev/null
+++ b/Pythonwin/pywin/framework/bitmap.py
@@ -0,0 +1,143 @@
+import win32ui
+import win32con
+import win32api
+import string
+import os
+import app
+import sys
+
+from pywin.mfc import docview, window
+
+bStretch = 1
+
+class BitmapDocument(docview.Document):
+ "A bitmap document. Holds the bitmap data itself."
+ def __init__(self, template):
+ docview.Document.__init__(self, template)
+ self.bitmap=None
+ def OnNewDocument(self):
+ # I can not create new bitmaps.
+ win32ui.MessageBox("Bitmaps can not be created.")
+ def OnOpenDocument(self, filename):
+ self.bitmap=win32ui.CreateBitmap()
+ # init data members
+ f = open(filename, 'rb')
+ try:
+ try:
+ self.bitmap.LoadBitmapFile(f)
+ except IOError:
+ win32ui.MessageBox("Could not load the bitmap from %s" % filename)
+ return 0
+ finally:
+ f.close()
+ self.size = self.bitmap.GetSize()
+ return 1
+ def DeleteContents(self):
+ self.bitmap=None
+
+class BitmapView(docview.ScrollView):
+ "A view of a bitmap. Obtains data from document."
+ def __init__(self, doc):
+ docview.ScrollView.__init__(self, doc)
+ self.width = self.height = 0
+ # set up message handlers
+ self.HookMessage (self.OnSize, win32con.WM_SIZE)
+
+ def OnInitialUpdate(self):
+ doc = self.GetDocument()
+ if doc.bitmap:
+ bitmapSize = doc.bitmap.GetSize()
+ self.SetScrollSizes(win32con.MM_TEXT, bitmapSize)
+
+ def OnSize (self, params):
+ lParam = params[3]
+ self.width = win32api.LOWORD(lParam)
+ self.height = win32api.HIWORD(lParam)
+
+ def OnDraw (self, dc):
+ # set sizes used for "non stretch" mode.
+ doc = self.GetDocument()
+ if doc.bitmap is None: return
+ bitmapSize = doc.bitmap.GetSize()
+ if bStretch:
+ # stretch BMP.
+ viewRect = (0,0,self.width, self.height)
+ bitmapRect = (0,0,bitmapSize[0], bitmapSize[1])
+ doc.bitmap.Paint(dc, viewRect, bitmapRect)
+ else:
+ # non stretch.
+ doc.bitmap.Paint(dc)
+
+class BitmapFrame(window.MDIChildWnd):
+ def OnCreateClient( self, createparams, context ):
+ borderX = win32api.GetSystemMetrics(win32con.SM_CXFRAME)
+ borderY = win32api.GetSystemMetrics(win32con.SM_CYFRAME)
+ titleY = win32api.GetSystemMetrics(win32con.SM_CYCAPTION) # includes border
+ # try and maintain default window pos, else adjust if cant fit
+ # get the main client window dimensions.
+ mdiClient = win32ui.GetMainFrame().GetWindow(win32con.GW_CHILD)
+ clientWindowRect=mdiClient.ScreenToClient(mdiClient.GetWindowRect())
+ clientWindowSize=(clientWindowRect[2]-clientWindowRect[0],clientWindowRect[3]-clientWindowRect[1])
+ left, top, right, bottom=mdiClient.ScreenToClient(self.GetWindowRect())
+# width, height=context.doc.size[0], context.doc.size[1]
+# width = width+borderX*2
+# height= height+titleY+borderY*2-1
+# if (left+width)>clientWindowSize[0]:
+# left = clientWindowSize[0] - width
+# if left<0:
+# left = 0
+# width = clientWindowSize[0]
+# if (top+height)>clientWindowSize[1]:
+# top = clientWindowSize[1] - height
+# if top<0:
+# top = 0
+# height = clientWindowSize[1]
+# self.frame.MoveWindow((left, top, left+width, top+height),0)
+ window.MDIChildWnd.OnCreateClient(self, createparams, context)
+ return 1
+
+
+class BitmapTemplate(docview.DocTemplate):
+ def __init__(self):
+ docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, BitmapDocument, BitmapFrame, BitmapView)
+ def MatchDocType(self, fileName, fileType):
+ doc = self.FindOpenDocument(fileName)
+ if doc: return doc
+ ext = string.lower(os.path.splitext(fileName)[1])
+ if ext =='.bmp': # removed due to PIL! or ext=='.ppm':
+ return win32ui.CDocTemplate_Confidence_yesAttemptNative
+ return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
+# return win32ui.CDocTemplate_Confidence_noAttempt
+
+# For debugging purposes, when this module may be reloaded many times.
+try:
+ win32ui.GetApp().RemoveDocTemplate(bitmapTemplate)
+except NameError:
+ pass
+
+bitmapTemplate = BitmapTemplate()
+bitmapTemplate.SetDocStrings('\nBitmap\nBitmap\nBitmap (*.bmp)\n.bmp\nPythonBitmapFileType\nPython Bitmap File')
+win32ui.GetApp().AddDocTemplate(bitmapTemplate)
+
+# This works, but just didnt make it through the code reorg.
+#class PPMBitmap(Bitmap):
+# def LoadBitmapFile(self, file ):
+# magic=file.readline()
+# if magic <> "P6\n":
+# raise TypeError, "The file is not a PPM format file"
+# rowcollist=string.split(file.readline())
+# cols=string.atoi(rowcollist[0])
+# rows=string.atoi(rowcollist[1])
+# file.readline() # whats this one?
+# self.bitmap.LoadPPMFile(file,(cols,rows))
+
+
+def t():
+ bitmapTemplate.OpenDocumentFile('d:\\winnt\\arcade.bmp')
+ #OpenBMPFile( 'd:\\winnt\\arcade.bmp')
+
+def demo():
+ import glob
+ winDir=win32api.GetWindowsDirectory()
+ for fileName in glob.glob1(winDir, '*.bmp')[:2]:
+ bitmapTemplate.OpenDocumentFile(os.path.join(winDir, fileName))
diff --git a/Pythonwin/pywin/framework/cmdline.py b/Pythonwin/pywin/framework/cmdline.py
new file mode 100644
index 0000000000..c29fefd14a
--- /dev/null
+++ b/Pythonwin/pywin/framework/cmdline.py
@@ -0,0 +1,50 @@
+# cmdline - command line utilities.
+import sys
+import win32ui
+import string
+
+def ParseArgs( str ):
+ import string
+ ret=[]
+ pos = 0
+ length=len(str)
+ while pos=length:
+ break
+ if str[pos]=='"':
+ pos=pos+1
+ try:
+ endPos = string.index(str, '"', pos)-1
+ nextPos = endPos+2
+ except ValueError:
+ endPos=length
+ nextPos=endPos+1
+ else:
+ endPos = pos
+ while endPos0:
+ path=win32ui.FullPath(path)
+ else:
+ path=win32ui.FullPath(os.curdir)
+ # must check that the command line arg's path is in sys.path
+ for syspath in sys.path:
+ if win32ui.FullPath(syspath)==path:
+ break
+ else:
+ sys.path.append(path)
+ return os.path.splitext(fname)[0]
\ No newline at end of file
diff --git a/Pythonwin/pywin/framework/dbgcommands.py b/Pythonwin/pywin/framework/dbgcommands.py
new file mode 100644
index 0000000000..4c3b3c50c4
--- /dev/null
+++ b/Pythonwin/pywin/framework/dbgcommands.py
@@ -0,0 +1,163 @@
+# Command Handlers for the debugger.
+
+# Not in the debugger package, as I always want these interfaces to be
+# available, even if the debugger has not yet been (or can not be)
+# imported
+import win32ui, win32con
+import scriptutils
+
+IdToBarNames = {
+ win32ui.IDC_DBG_STACK : ("Stack",0),
+ win32ui.IDC_DBG_BREAKPOINTS : ("Breakpoints",0),
+ win32ui.IDC_DBG_WATCH : ("Watch",1),
+ }
+
+class DebuggerCommandHandler:
+ def HookCommands(self):
+ commands = ( (self.OnStep, None, win32ui.IDC_DBG_STEP),
+ (self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT),
+ (self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER),
+ (self.OnGo, None, win32ui.IDC_DBG_GO),
+ (self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE),
+ (self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD),
+ (self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR),
+# (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR),
+ )
+
+ frame = win32ui.GetMainFrame()
+
+ for methHandler, methUpdate, id in commands:
+ frame.HookCommand(methHandler, id);
+ if not methUpdate is None:
+ frame.HookCommandUpdate(methUpdate, id)
+
+ for id in IdToBarNames.keys():
+ frame.HookCommand( self.OnDebuggerBar, id)
+ frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id)
+
+ def OnDebuggerToolbar(self, id, code):
+ if code==0:
+ return not win32ui.GetMainFrame().OnBarCheck(id)
+
+ def OnUpdateDebuggerToolbar(self, cmdui):
+ win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
+ cmdui.Enable(1)
+
+ def _GetDebugger(self):
+ try:
+ import pywin.debugger
+ return pywin.debugger.currentDebugger
+ except ImportError:
+ return None
+
+ def _DoOrStart(self, doMethod, startFlag):
+ d=self._GetDebugger()
+ if d is not None and d.IsDebugging():
+ method = getattr(d, doMethod)
+ method()
+ else:
+ scriptutils.RunScript(defName=None, defArgs=None, bShowDialog = 0, debuggingType=startFlag)
+
+ def OnStep(self, msg, code):
+ self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP)
+
+ def OnStepOver(self, msg, code):
+ self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP)
+
+ def OnStepOut(self, msg, code):
+ d=self._GetDebugger()
+ if d is not None and d.IsDebugging():
+ d.do_set_return()
+
+ def OnGo(self, msg, code):
+ self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO)
+
+ def OnClose(self, msg, code):
+ d=self._GetDebugger()
+ if d is not None:
+ if d.IsDebugging():
+ d.set_quit()
+ else:
+ d.close()
+
+ def OnUpdateClose(self, cmdui):
+ d=self._GetDebugger()
+ if d is not None and d.inited:
+ cmdui.Enable(1)
+ else:
+ cmdui.Enable(0)
+
+ def OnAdd(self, msg, code):
+ doc = scriptutils.GetActiveEditorDocument()
+ if doc is None:
+ win32ui.MessageBox('There is no active window - no breakpoint can be added')
+ pathName = doc.GetPathName()
+ view = doc.GetFirstView()
+ lineNo = view.LineFromChar(view.GetSel()[0])+1
+ # If I have a debugger, then tell it, otherwise just add a marker
+ d=self._GetDebugger()
+ if d is None:
+ import pywin.framework.editor.color.coloreditor
+ doc.MarkerToggle(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
+ else:
+ if d.get_break(pathName, lineNo):
+ win32ui.SetStatusText('Clearing breakpoint',1)
+ rc = d.clear_break(pathName, lineNo)
+ else:
+ win32ui.SetStatusText('Setting breakpoint',1)
+ rc = d.set_break(pathName, lineNo)
+ if rc:
+ win32ui.MessageBox(rc)
+ d.GUIRespondDebuggerData()
+
+ def OnClearAll(self, msg, code):
+ win32ui.SetStatusText('Clearing all breakpoints')
+ d=self._GetDebugger()
+ if d is None:
+ import pywin.framework.editor
+ import pywin.framework.editor.color.coloreditor
+ for doc in pywin.framework.editor.editorTemplate.GetDocumentList():
+ doc.MarkerDeleteAll(pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
+ else:
+ d.clear_all_breaks()
+ d.UpdateAllLineStates()
+ d.GUIRespondDebuggerData()
+
+ def OnUpdateOnlyBreak(self, cmdui):
+ d=self._GetDebugger()
+ ok = d is not None and d.IsBreak()
+ cmdui.Enable(ok)
+
+ def OnUpdateAddBreakpoints(self, cmdui):
+ doc = scriptutils.GetActiveEditorDocument()
+ if doc is None:
+ enabled = 0
+ else:
+ enabled = 1
+ view = doc.GetFirstView()
+ lineNo = view.LineFromChar(view.GetSel()[0])+1
+ import pywin.framework.editor.color.coloreditor
+ cmdui.SetCheck(doc.MarkerAtLine(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT) != 0)
+ cmdui.Enable(enabled)
+
+ def OnUpdateClearAllBreakpoints(self, cmdui):
+ d=self._GetDebugger()
+ cmdui.Enable(d is None or len(d.breaks)!=0)
+
+ def OnUpdateDebuggerBar(self, cmdui):
+ name, always = IdToBarNames.get(cmdui.m_nID)
+ enabled = always
+ d=self._GetDebugger()
+ if d is not None and d.IsDebugging() and name is not None:
+ enabled = 1
+ bar = d.GetDebuggerBar(name)
+ cmdui.SetCheck(bar.IsWindowVisible())
+ cmdui.Enable(enabled)
+
+ def OnDebuggerBar(self, id, code):
+ name = IdToBarNames.get(id)[0]
+ d=self._GetDebugger()
+ if d is not None and name is not None:
+ bar = d.GetDebuggerBar(name)
+ newState = not bar.IsWindowVisible()
+ win32ui.GetMainFrame().ShowControlBar(bar, newState, 1)
diff --git a/Pythonwin/pywin/framework/dlgappcore.py b/Pythonwin/pywin/framework/dlgappcore.py
new file mode 100644
index 0000000000..8555384e53
--- /dev/null
+++ b/Pythonwin/pywin/framework/dlgappcore.py
@@ -0,0 +1,68 @@
+# dlgappcore.
+#
+# base classes for dialog based apps.
+
+import app
+import win32ui
+import win32con
+import win32api
+import sys
+from pywin.mfc import dialog
+import regsub
+
+error = "Dialog Application Error"
+
+class AppDialog(dialog.Dialog):
+ "The dialog box for the application"
+ def __init__(self, id, dll=None):
+ self.iconId = win32ui.IDR_MAINFRAME
+ dialog.Dialog.__init__(self, id, dll)
+
+ def OnInitDialog(self):
+ return dialog.Dialog.OnInitDialog(self)
+
+ # Provide support for a dlg app using an icon
+ def OnPaint(self):
+ if not self.IsIconic(): return self._obj_.OnPaint()
+ self.DefWindowProc(win32con.WM_ICONERASEBKGND, dc.GetHandleOutput(), 0)
+ left, top, right, bottom = self.GetClientRect()
+ left = (right - win32api.GetSystemMetrics(win32con.SM_CXICON)) >> 1
+ top = (bottom - win32api.GetSystemMetrics(win32con.SM_CYICON)) >> 1
+ hIcon = win32ui.GetApp().LoadIcon(self.iconId)
+ self.GetDC().DrawIcon((left, top), hIcon)
+
+ # Only needed to provide a minimized icon (and this seems
+ # less important under win95/NT4
+ def OnEraseBkgnd(self, dc):
+ if self.IsIconic():
+ return 1
+ else:
+ return self._obj_.OnEraseBkgnd(dc)
+ def OnQueryDragIcon(self):
+ return win32ui.GetApp().LoadIcon(self.iconId)
+
+
+
+class DialogApp(app.CApp):
+ "An application class, for an app with main dialog box"
+ def InitInstance(self):
+# win32ui.SetProfileFileName('dlgapp.ini')
+ win32ui.LoadStdProfileSettings()
+ win32ui.EnableControlContainer()
+ win32ui.Enable3dControls()
+ self.dlg = self.frame = self.CreateDialog()
+
+ if self.frame is None:
+ raise error, "No dialog was created by CreateDialog()"
+ return
+
+ self._obj_.InitDlgInstance(self.dlg)
+ self.PreDoModal()
+ self.dlg.PreDoModal()
+ self.dlg.DoModal()
+
+ def CreateDialog(self):
+ pass
+ def PreDoModal(self):
+ pass
+
diff --git a/Pythonwin/pywin/framework/editor/ModuleBrowser.py b/Pythonwin/pywin/framework/editor/ModuleBrowser.py
new file mode 100644
index 0000000000..25e74c2e9e
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/ModuleBrowser.py
@@ -0,0 +1,185 @@
+# ModuleBrowser.py - A view that provides a module browser for an editor document.
+import pywin.mfc.docview
+import win32ui
+import win32con
+import commctrl
+import win32api
+from pywin.tools import hierlist, browser
+import pywin.framework.scriptutils
+import afxres
+
+import pyclbr
+
+class HierListCLBRModule(hierlist.HierListItem):
+ def __init__(self, modName, clbrdata):
+ self.modName = modName
+ self.clbrdata = clbrdata
+ def GetText(self):
+ return self.modName
+ def GetSubList(self):
+ ret = []
+ for item in self.clbrdata.values():
+ if item.__class__ != pyclbr.Class: # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
+ ret.append(HierListCLBRFunction( item ) )
+ else:
+ ret.append(HierListCLBRClass( item) )
+ ret.sort()
+ return ret
+ def IsExpandable(self):
+ return 1
+
+class HierListCLBRItem(hierlist.HierListItem):
+ def __init__(self, name, file, lineno, suffix = ""):
+ self.name = name
+ self.file = file
+ self.lineno = lineno
+ self.suffix = suffix
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+ def GetText(self):
+ return self.name + self.suffix
+ def TakeDefaultAction(self):
+ if self.file:
+ pywin.framework.scriptutils.JumpToDocument(self.file, self.lineno, bScrollToTop = 1)
+ else:
+ win32ui.SetStatusText("The source of this object is unknown")
+ def PerformItemSelected(self):
+ if self.file is None:
+ msg = "%s - source can not be located." % (self.name, )
+ else:
+ msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
+ win32ui.SetStatusText(msg)
+
+class HierListCLBRClass(HierListCLBRItem):
+ def __init__(self, clbrclass, suffix = ""):
+ try:
+ name = clbrclass.name
+ file = clbrclass.file
+ lineno = clbrclass.lineno
+ self.super = clbrclass.super
+ self.methods = clbrclass.methods
+ except AttributeError:
+ name = clbrclass
+ file = lineno = None
+ self.super = []; self.methods = {}
+ HierListCLBRItem.__init__(self, name, file, lineno, suffix)
+ def GetSubList(self):
+ r1 = []
+ for c in self.super:
+ r1.append(HierListCLBRClass(c, " (Parent class)"))
+ r1.sort()
+ r2=[]
+ for meth, lineno in self.methods.items():
+ r2.append(HierListCLBRMethod(meth, self.file, lineno))
+ r2.sort()
+ return r1+r2
+ def IsExpandable(self):
+ return len(self.methods) + len(self.super)
+ def GetBitmapColumn(self):
+ return 21
+
+class HierListCLBRFunction(HierListCLBRClass):
+ def GetBitmapColumn(self):
+ return 22
+
+class HierListCLBRMethod(HierListCLBRItem):
+ def GetBitmapColumn(self):
+ return 22
+
+class HierListCLBRErrorItem(hierlist.HierListItem):
+ def __init__(self, text):
+ self.text = text
+ def GetText(self):
+ return self.text
+ def GetSubList(self):
+ return [HierListCLBRErrorItem(self.text)]
+ def IsExpandable(self):
+ return 0
+
+class HierListCLBRErrorRoot(HierListCLBRErrorItem):
+ def IsExpandable(self):
+ return 1
+
+class BrowserView(pywin.mfc.docview.TreeView):
+ def OnInitialUpdate(self):
+ self.list = None
+ rc = self._obj_.OnInitialUpdate()
+ self.HookMessage(self.OnSize, win32con.WM_SIZE)
+ self.bDirty = 0
+ return rc
+
+ def OnDestroy(self, msg):
+ self.DestroyList()
+ return pywin.mfc.docview.TreeView.OnDestroy(self, msg)
+
+ def OnActivateView(self, activate, av, dv):
+# print "AV", self.bDirty, activate
+ if activate:
+ self.CheckRefreshList()
+ return self._obj_.OnActivateView(activate, av, dv)
+
+ def _MakeRoot(self):
+ path = self.GetDocument().GetPathName()
+ if not path:
+ return HierListCLBRErrorRoot("Error: Can not browse a file until it is saved")
+ else:
+ mod, path = pywin.framework.scriptutils.GetPackageModuleName(path)
+ if self.bDirty:
+ what = "Refreshing"
+ # Hack for pyclbr being too smart
+ try:
+ del pyclbr._modules[mod]
+ except (KeyError, AttributeError):
+ pass
+ else:
+ what = "Building"
+ win32ui.SetStatusText("%s class list - please wait..." % (what,), 1)
+ win32ui.DoWaitCursor(1)
+ try:
+ reader = pyclbr.readmodule_ex # new version post 1.5.2
+ except AttributeError:
+ reader = pyclbr.readmodule
+ try:
+ data = reader(mod, [path])
+ if data:
+ return HierListCLBRModule(mod, data)
+ else:
+ return HierListCLBRErrorRoot("No Python classes in module.")
+
+ finally:
+ win32ui.DoWaitCursor(0)
+ win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
+
+ def DestroyList(self):
+ list = getattr(self, "list", None) # If the document was not successfully opened, we may not have a list.
+ self.list = None
+ if list is not None:
+ list.HierTerm()
+
+ def CheckMadeList(self):
+ if self.list is not None: return
+ root = self._MakeRoot()
+ self.list = list = hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER)
+ list.HierInit(self.GetParentFrame(), self)
+ list.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
+
+ def CheckRefreshList(self):
+ if self.bDirty:
+ if self.list is None:
+ self.CheckMadeList()
+ else:
+ self.list.AcceptRoot(self._MakeRoot())
+ self.bDirty = 0
+
+ def OnSize(self, params):
+ lparam = params[3]
+ w = win32api.LOWORD(lparam)
+ h = win32api.HIWORD(lparam)
+ if w != 0:
+ self.CheckMadeList()
+ elif w == 0:
+ self.DestroyList()
+ return 1
+
+ def _UpdateUIForState(self):
+ self.bDirty = 1
\ No newline at end of file
diff --git a/Pythonwin/pywin/framework/editor/__init__.py b/Pythonwin/pywin/framework/editor/__init__.py
new file mode 100644
index 0000000000..391b424cc4
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/__init__.py
@@ -0,0 +1,94 @@
+# __init__ for the Pythonwin editor package.
+#
+# We lookup the INI file for the users "default editor".
+# We import that editor here, so that as far as the rest of Pythonwin
+# cares, it just uses the default editor package.
+
+# TODO -
+# If the import of the users perferred module fails, but _after_
+# the module does an import of the default editor module,
+# then the defauly editor module will not be correctly
+# registered.
+
+import win32ui, sys, win32con
+
+defaultCharacterFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
+
+def GetDefaultEditorModuleName():
+ import pywin
+ # If someone has set pywin.editormodulename, then this is what we use
+ try:
+ prefModule = pywin.editormodulename
+ except AttributeError:
+ prefModule = win32ui.GetProfileVal("Editor","Module", "")
+ return prefModule
+
+def WriteDefaultEditorModule(module):
+ try:
+ module = module.__name__
+ except:
+ pass
+ win32ui.WriteProfileVal("Editor", "Module", module)
+
+def LoadDefaultEditor():
+ prefModule = GetDefaultEditorModuleName()
+ restorePrefModule = None
+ mod = None
+ if prefModule:
+ try:
+ mod = __import__(prefModule)
+ except 'xx':
+ msg = "Importing your preferred editor ('%s') failed.\n\nError %s: %s\n\nAn attempt will be made to load the default editor.\n\nWould you like this editor disabled in the future?" % (prefModule, sys.exc_info()[0], sys.exc_info()[1])
+ rc = win32ui.MessageBox(msg, "Error importing editor", win32con.MB_YESNO)
+ if rc == win32con.IDNO:
+ restorePrefModule = prefModule
+ WriteDefaultEditorModule("")
+ del rc
+
+ try:
+ # Try and load the default one - dont catch errors here.
+ if mod is None:
+ prefModule = "pywin.framework.editor.color.coloreditor"
+ mod = __import__(prefModule)
+
+ # Get at the real module.
+ mod = sys.modules[prefModule]
+
+ # Do a "from mod import *"
+ globals().update(mod.__dict__)
+
+ finally:
+ # Restore the users default editor if it failed and they requested not to disable it.
+ if restorePrefModule:
+ WriteDefaultEditorModule(restorePrefModule)
+
+def GetEditorOption(option, defaultValue, min=None, max = None):
+ rc = win32ui.GetProfileVal("Editor", option, defaultValue)
+ if min is not None and rc < min: rc = defaultValue
+ if max is not None and rc > max: rc = defaultValue
+ return rc
+
+def SetEditorOption(option, newValue):
+ win32ui.WriteProfileVal("Editor", option, newValue)
+
+def DeleteEditorOption(option):
+ try:
+ win32ui.WriteProfileVal("Editor", option, None)
+ except win32ui.error:
+ pass
+
+# Load and save font tuples
+def GetEditorFontOption(option, default = None):
+ if default is None: default = defaultCharacterFormat
+ fmt = GetEditorOption( option, "" )
+ if fmt == "": return default
+ try:
+ return eval(fmt)
+ except:
+ print "WARNING: Invalid font setting in registry - setting ignored"
+ return default
+
+def SetEditorFontOption(option, newValue):
+ SetEditorOption(option, str(newValue))
+
+LoadDefaultEditor()
diff --git a/Pythonwin/pywin/framework/editor/color/__init__.py b/Pythonwin/pywin/framework/editor/color/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/framework/editor/color/coloreditor.py b/Pythonwin/pywin/framework/editor/color/coloreditor.py
new file mode 100644
index 0000000000..7c7a197a24
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/color/coloreditor.py
@@ -0,0 +1,362 @@
+# Color Editor originally by Neil Hodgson, but restructured by mh to integrate
+# even tighter into Pythonwin.
+import win32ui
+import win32con
+import win32api
+import afxres
+import regex
+import regsub
+import string
+import sys
+import array
+import struct
+import traceback
+
+import pywin.scintilla.keycodes
+from pywin.scintilla import bindings
+
+from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
+#from pywin.framework.editor import EditorPropertyPage
+
+from pywin.mfc import docview, window, dialog, afxres
+
+# Define a few common markers
+MARKER_BOOKMARK = 0
+MARKER_BREAKPOINT = 1
+MARKER_CURRENT = 2
+
+# XXX - copied from debugger\dbgcon.py
+DBGSTATE_NOT_DEBUGGING = 0
+DBGSTATE_RUNNING = 1
+DBGSTATE_BREAK = 2
+
+from pywin.scintilla.document import CScintillaDocument
+from pywin.framework.editor.document import EditorDocumentBase
+from pywin.scintilla.scintillacon import * # For the marker definitions
+import pywin.scintilla.view
+
+class SyntEditDocument(EditorDocumentBase, CScintillaDocument):
+ "A SyntEdit document. "
+ def OnOpenDocument(self, filename):
+ rc = CScintillaDocument.OnOpenDocument(self, filename)
+ self._DocumentStateChanged()
+ return rc
+ def ReloadDocument(self):
+ EditorDocumentBase.ReloadDocument(self)
+ self._ApplyOptionalToViews("SCISetSavePoint")
+ # All Marker functions are 1 based.
+ def MarkerAdd( self, lineNo, marker ):
+ self._ApplyOptionalToViews("MarkerAdd", lineNo, marker)
+ def MarkerToggle( self, lineNo, marker ):
+ self._ApplyOptionalToViews("MarkerToggle", lineNo, marker)
+ def MarkerDelete( self, lineNo, marker ):
+ self._ApplyOptionalToViews("MarkerDelete", lineNo, marker)
+ def MarkerDeleteAll( self, marker ):
+ self._ApplyOptionalToViews("MarkerDeleteAll", marker)
+ def MarkerGetNext(self, lineNo, marker):
+ return self.GetFirstView().MarkerGetNext(lineNo, marker)
+ def MarkerAtLine(self, lineNo, marker):
+ return self.GetFirstView().MarkerAtLine(lineNo, marker)
+ def OnDebuggerStateChange(self, state):
+ self._ApplyOptionalToViews("OnDebuggerStateChange", state)
+
+SyntEditViewParent=pywin.scintilla.view.CScintillaView
+class SyntEditView(SyntEditViewParent):
+ "A view of a SyntEdit. Obtains data from document."
+ def __init__(self, doc):
+ SyntEditViewParent.__init__(self, doc)
+ self.bCheckingFile = 0
+
+ def OnInitialUpdate(self):
+ SyntEditViewParent.OnInitialUpdate(self)
+ # set up styles
+ self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
+
+ # Define the markers
+ self.SCIMarkerDefine(MARKER_BOOKMARK, SC_MARK_ROUNDRECT)
+ self.SCIMarkerSetBack(MARKER_BOOKMARK, win32api.RGB(0, 0xff, 0xff))
+ self.SCIMarkerSetFore(MARKER_BOOKMARK, win32api.RGB(0x0, 0x0, 0x0))
+
+ self.SCIMarkerDefine(MARKER_CURRENT, SC_MARK_ARROW)
+ self.SCIMarkerSetBack(MARKER_CURRENT, win32api.RGB(0, 0x7f, 0x7f))
+
+ self._UpdateUIForState()
+ self.SCIMarkerDefine(MARKER_BREAKPOINT, SC_MARK_CIRCLE)
+ # Marker background depends on debugger state
+ self.SCIMarkerSetFore(MARKER_BREAKPOINT, win32api.RGB(0x0, 0, 0))
+ # Get the current debugger state.
+ try:
+ import pywin.debugger
+ if pywin.debugger.currentDebugger is None:
+ state = DBGSTATE_NOT_DEBUGGING
+ else:
+ state = pywin.debugger.currentDebugger.debuggerState
+ except ImportError:
+ state = DBGSTATE_NOT_DEBUGGING
+ self.OnDebuggerStateChange(state)
+ self.GetDocument().GetDocTemplate().CheckIDLEMenus(self.idle)
+
+ def _GetSubConfigNames(self):
+ return ["editor"] # Allow [Keys:Editor] sections to be specific to us
+
+ def DoConfigChange(self):
+ SyntEditViewParent.DoConfigChange(self)
+ tabSize = GetEditorOption("Tab Size", 4, 2)
+ indentSize = GetEditorOption("Indent Size", 4, 2)
+ bUseTabs = GetEditorOption("Use Tabs", 0)
+ bSmartTabs = GetEditorOption("Smart Tabs", 1)
+ ext = self.idle.IDLEExtension("AutoIndent") # Required extension.
+
+ # Auto-indent has very complicated behaviour. In a nutshell, the only
+ # way to get sensible behaviour from it is to ensure tabwidth != indentsize.
+ # Further, usetabs will only ever go from 1->0, never 0->1.
+ # This is _not_ the behaviour Pythonwin wants:
+ # * Tab width is arbitary, so should have no impact on smarts.
+ # * bUseTabs setting should reflect how new files are created, and
+ # if Smart Tabs disabled, existing files are edited
+ # * If "Smart Tabs" is enabled, bUseTabs should have no bearing
+ # for existing files (unless of course no context can be determined)
+ #
+ # So for smart tabs we configure the widget with completely dummy
+ # values (ensuring tabwidth != indentwidth), ask it to guess, then
+ # look at the values it has guessed, and re-configure
+ if bSmartTabs:
+ ext.config(usetabs=1, tabwidth=5, indentwidth=4)
+ ext.set_indentation_params(1)
+ if ext.indentwidth==5:
+ # Either 5 literal spaces, or a single tab character. Assume a tab
+ usetabs = 1
+ indentwidth = tabSize
+ else:
+ # Either Indented with spaces, and indent size has been guessed or
+ # an empty file (or no context found - tough!)
+ if self.GetTextLength()==0: # emtpy
+ usetabs = bUseTabs
+ indentwidth = indentSize
+ else: # guessed.
+ indentwidth = ext.indentwidth
+ usetabs = 0
+ # Tab size can never be guessed - set at user preference.
+ ext.config(usetabs=usetabs, indentwidth=indentwidth, tabwidth=tabSize)
+ else:
+ # Dont want smart-tabs - just set the options!
+ ext.config(usetabs=bUseTabs, tabwidth=tabSize, indentwidth=indentSize)
+ self.SCISetTabWidth(tabSize)
+
+ self.SCISetViewWS( GetEditorOption("View Whitespace", 0) )
+
+ def OnDebuggerStateChange(self, state):
+ if state == DBGSTATE_NOT_DEBUGGING:
+ # Indicate breakpoints arent really usable.
+ self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xff, 0xff, 0xff))
+ else:
+ self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0x80, 0, 0))
+
+ def HookHandlers(self):
+ SyntEditViewParent.HookHandlers(self)
+
+ # Idle time document reloaded handlers.
+ self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
+ self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
+ self.GetParentFrame().HookNotify(self.OnModifyAttemptRO, SCN_MODIFYATTEMPTRO)
+
+ def _PrepareUserStateChange(self):
+ return self.GetSel(), self.GetFirstVisibleLine()
+ def _EndUserStateChange(self, info):
+ scrollOff = info[1] - self.GetFirstVisibleLine()
+ if scrollOff:
+ self.LineScroll(scrollOff)
+ # Make sure we dont reset the cursor beyond the buffer.
+ max = self.GetTextLength()
+ newPos = min(info[0][0], max), min(info[0][1], max)
+ self.SetSel(newPos)
+
+ def _UpdateUIForState(self):
+ self.SetReadOnly(self.GetDocument()._IsReadOnly())
+
+ def MarkerAdd( self, lineNo, marker ):
+ self.SCIMarkerAdd(lineNo-1, marker)
+
+ def MarkerAtLine(self, lineNo, marker):
+ markerState = self.SCIMarkerGet(lineNo-1)
+ return markerState & (1<>", event)
+
+ def ShowInteractiveWindowEvent(self, event):
+ import pywin.framework.interact
+ pywin.framework.interact.ShowInteractiveWindow()
+
+ #
+ # Support for checking the external file to see if it is changed.
+ # We set up an idle time handler only when the view has focus.
+ # This handler continually checks the file behind this document, but
+ # as it is in idle time, no one notices :-)
+ #
+ def _AddReloadIdleHandler(self):
+ win32ui.GetApp().AddIdleHandler(self.CheckExternalDocumentUpdated)
+
+ def _DeleteReloadIdleHandler(self):
+ if win32ui.GetApp().HaveIdleHandler(self.CheckExternalDocumentUpdated):
+ win32ui.GetApp().DeleteIdleHandler(self.CheckExternalDocumentUpdated)
+
+ def CheckExternalDocumentUpdated(self, handler, count):
+ if self.bCheckingFile: return
+ self.bCheckingFile = 1
+ try:
+ self.GetDocument().CheckExternalDocumentUpdated()
+ except:
+ traceback.print_exc()
+ print "The idle handler checking for changes to the file on disk failed!"
+ self._DeleteReloadIdleHandler()
+ self.bCheckingFile = 0
+ return 0 # No more idle handling required.
+
+from pywin.framework.editor.template import EditorTemplateBase
+class SyntEditTemplate(EditorTemplateBase):
+ def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
+ if makeDoc is None: makeDoc = SyntEditDocument
+ if makeView is None: makeView = SyntEditView
+ self.bSetMenus = 0
+ EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
+
+ def CheckIDLEMenus(self, idle):
+ if self.bSetMenus: return
+ self.bSetMenus = 1
+
+ submenu = win32ui.CreatePopupMenu()
+ newitems = idle.GetMenuItems("edit")
+ flags=win32con.MF_STRING|win32con.MF_ENABLED
+ for text, event in newitems:
+ id = bindings.event_to_commands.get(event)
+ if id is not None:
+ keyname = pywin.scintilla.view.configManager.get_key_binding( event, ["editor"] )
+ if keyname is not None:
+ text = text + "\t" + keyname
+ submenu.AppendMenu(flags, id, text)
+
+ mainMenu = self.GetSharedMenu()
+ editMenu = mainMenu.GetSubMenu(1)
+ editMenu.AppendMenu(win32con.MF_SEPARATOR, 0, "")
+ editMenu.AppendMenu(win32con.MF_STRING | win32con.MF_POPUP | win32con.MF_ENABLED, submenu.GetHandle(), "&Source Code")
+
+ def _CreateDocTemplate(self, resourceId):
+ return win32ui.CreateDocTemplate(resourceId)
+
+ def CreateWin32uiDocument(self):
+ return self.DoCreateDoc()
+
+ def GetPythonPropertyPages(self):
+ """Returns a list of property pages
+ """
+ from pywin.scintilla import configui
+ return EditorTemplateBase.GetPythonPropertyPages(self) + [configui.ScintillaFormatPropertyPage()]
+
+# For debugging purposes, when this module may be reloaded many times.
+try:
+ win32ui.GetApp().RemoveDocTemplate(editorTemplate)
+except NameError:
+ pass
+
+editorTemplate = SyntEditTemplate()
+win32ui.GetApp().AddDocTemplate(editorTemplate)
diff --git a/Pythonwin/pywin/framework/editor/configui.py b/Pythonwin/pywin/framework/editor/configui.py
new file mode 100644
index 0000000000..cc84df6062
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/configui.py
@@ -0,0 +1,108 @@
+from pywin.mfc import dialog
+import document
+import win32ui
+import win32con
+
+from pywin.framework.editor import GetEditorOption, SetEditorOption, DeleteEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat, editorTemplate
+import pywin.scintilla.config
+######################################################
+#
+# Property Page for editor options
+#
+class EditorPropertyPage(dialog.PropertyPage):
+ def __init__(self):
+ dialog.PropertyPage.__init__(self, win32ui.IDD_PP_EDITOR)
+ self.autooptions = []
+ self._AddEditorOption(win32ui.IDC_TAB_SIZE, "i", "Tab Size", 4)
+ self._AddEditorOption(win32ui.IDC_INDENT_SIZE, "i", "Indent Size", 4)
+ self._AddEditorOption(win32ui.IDC_USE_TABS, "i", "Use Tabs", 0)
+ self._AddEditorOption(win32ui.IDC_AUTO_RELOAD, "i", "Auto Reload", 1)
+ self._AddEditorOption(win32ui.IDC_COMBO1, "i", "Backup Type", document.BAK_DOT_BAK_BAK_DIR)
+ self._AddEditorOption(win32ui.IDC_USE_SMART_TABS, "i", "Smart Tabs", 1)
+ self._AddEditorOption(win32ui.IDC_VIEW_WHITESPACE, "i", "View Whitespace", 0)
+ self._AddEditorOption(win32ui.IDC_AUTOCOMPLETE, "i", "Autocomplete Attributes", 1)
+ self._AddEditorOption(win32ui.IDC_CALLTIPS, "i", "Show Call Tips", 1)
+
+ self.AddDDX(win32ui.IDC_VSS_INTEGRATE, "bVSS")
+ self.AddDDX(win32ui.IDC_EDITOR_COLOR, "Module", "i")
+ mod = GetEditorOption("Module", "")
+ self['Module'] = not mod or mod=="pywin.framework.editor.color.coloreditor"
+
+ self.AddDDX(win32ui.IDC_KEYBOARD_CONFIG, "Configs", "l")
+ self["Configs"] = pywin.scintilla.config.find_config_files()
+
+ def _AddEditorOption(self, idd, typ, optionName, defaultVal):
+ self.AddDDX(idd, optionName, typ)
+ self[optionName] = GetEditorOption(optionName, defaultVal)
+ self.autooptions.append(optionName, defaultVal)
+
+ def OnInitDialog(self):
+ for name, val in self.autooptions:
+ self[name] = GetEditorOption(name, val)
+
+ # Note that these MUST be in the same order as the BAK constants.
+ cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
+ cbo.AddString("None")
+ cbo.AddString(".BAK File")
+ cbo.AddString("TEMP dir")
+ cbo.AddString("Own dir")
+
+ # Source Safe
+ bVSS = GetEditorOption("Source Control Module", "") == "pywin.framework.editor.vss"
+ self['bVSS'] = bVSS
+
+ rc = dialog.PropertyPage.OnInitDialog(self)
+
+ try:
+ self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).SelectString(-1, GetEditorOption("Keyboard Config", "default"))
+ except win32ui.error:
+ import traceback
+ traceback.print_exc()
+ pass
+
+ return rc
+
+ def OnOK(self):
+ for name, defVal in self.autooptions:
+ SetEditorOption(name, self[name])
+ if self['bVSS']:
+ SetEditorOption("Source Control Module", "pywin.framework.editor.vss")
+ else:
+ if GetEditorOption("Source Control Module", "")=='pywin.framework.editor.vss':
+ SetEditorOption("Source Control Module", "")
+ rc = self['Module']
+ if rc == 0:
+ # Delete the option!
+ SetEditorOption('Module', 'pywin.framework.editor.editor')
+ else:
+ # Color editor!
+ DeleteEditorOption('Module')
+
+ # Keyboard config
+ configname = self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).GetWindowText()
+ if configname:
+ if configname == "default":
+ DeleteEditorOption("Keyboard Config")
+ else:
+ SetEditorOption("Keyboard Config", configname)
+
+ import pywin.scintilla.view
+ pywin.scintilla.view.LoadConfiguration()
+
+ # Now tell all views we have changed.
+ for doc in editorTemplate.GetDocumentList():
+ for view in doc.GetAllViews():
+ try:
+ fn = view.OnConfigChange
+ except AttributeError:
+ continue
+ fn()
+ return 1
+
+
+def testpp():
+ ps = dialog.PropertySheet("Editor Options")
+ ps.AddPage(EditorPropertyPage())
+ ps.DoModal()
+
+
diff --git a/Pythonwin/pywin/framework/editor/document.py b/Pythonwin/pywin/framework/editor/document.py
new file mode 100644
index 0000000000..e7c415b5ff
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/document.py
@@ -0,0 +1,218 @@
+from pywin.mfc import docview, object
+from pywin.framework.editor import GetEditorOption
+import win32ui
+import os
+import win32con
+import string
+import traceback
+import win32api
+
+BAK_NONE=0
+BAK_DOT_BAK=1
+BAK_DOT_BAK_TEMP_DIR=2
+BAK_DOT_BAK_BAK_DIR=3
+
+ParentEditorDocument=docview.Document
+class EditorDocumentBase(ParentEditorDocument):
+ def __init__(self, template):
+ self.bAutoReload = GetEditorOption("Auto Reload", 1)
+ self.bDeclinedReload = 0 # Has the user declined to reload.
+ self.fileStat = None
+
+ # what sort of bak file should I create.
+ # default to write to %temp%/bak/filename.ext
+ self.bakFileType=GetEditorOption("Backup Type", BAK_DOT_BAK_BAK_DIR)
+
+ # Should I try and use VSS integration?
+ self.scModuleName=GetEditorOption("Source Control Module", "")
+ self.scModule = None # Loaded when first used.
+ # Skip the direct parent
+ object.CmdTarget.__init__(self, template.CreateWin32uiDocument())
+
+ # Helper for reflecting functions to views.
+ def _ApplyToViews(self, funcName, *args):
+ for view in self.GetAllViews():
+ func = getattr(view, funcName)
+ apply(func, args)
+ def _ApplyOptionalToViews(self, funcName, *args):
+ for view in self.GetAllViews():
+ func = getattr(view, funcName, None)
+ if func is not None:
+ apply(func, args)
+
+ def OnSaveDocument( self, fileName ):
+ win32ui.SetStatusText("Saving file...",1)
+ # rename to bak if required.
+ dir, basename = os.path.split(fileName)
+ if self.bakFileType==BAK_DOT_BAK:
+ bakFileName=dir+'\\'+os.path.splitext(basename)[0]+'.bak'
+ elif self.bakFileType==BAK_DOT_BAK_TEMP_DIR:
+ bakFileName=win32api.GetTempPath()+'\\'+os.path.splitext(basename)[0]+'.bak'
+ elif self.bakFileType==BAK_DOT_BAK_BAK_DIR:
+ tempPath=os.path.join(win32api.GetTempPath(),'bak')
+ try:
+ os.mkdir(tempPath,0)
+ except os.error:
+ pass
+ bakFileName=os.path.join(tempPath,basename)
+ try:
+ os.unlink(bakFileName) # raise NameError if no bakups wanted.
+ except (os.error, NameError):
+ pass
+ try:
+ os.rename(fileName, bakFileName)
+ except (os.error, NameError):
+ pass
+ try:
+ self.SaveFile(fileName)
+ except IOError, details:
+ win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
+ return 0
+ self.SetModifiedFlag(0) # No longer dirty
+ self.bDeclinedReload = 0 # They probably want to know if it changes again!
+ win32ui.AddToRecentFileList(fileName)
+ self.SetPathName(fileName)
+ win32ui.SetStatusText("Ready")
+ self._DocumentStateChanged()
+ return 1
+
+ # Support for reloading the document from disk - presumably after some
+ # external application has modified it (or possibly source control has
+ # checked it out.
+ def ReloadDocument(self):
+ """Reloads the document from disk. Assumes the file has
+ been saved and user has been asked if necessary - it just does it!
+ """
+ win32ui.SetStatusText("Reloading document. Please wait...", 1)
+ self.SetModifiedFlag(0)
+ # Loop over all views, saving their state, then reload the document
+ views = self.GetAllViews()
+ states = []
+ for view in views:
+ try:
+ info = view._PrepareUserStateChange()
+ except AttributeError: # Not our editor view?
+ info = None
+ states.append(info)
+ self.OnOpenDocument(self.GetPathName())
+ for view, info in map(None, views, states):
+ if info is not None:
+ view._EndUserStateChange(info)
+ win32ui.SetStatusText("Document reloaded.")
+
+ # Reloading the file
+ def CheckExternalDocumentUpdated(self):
+ if self.bDeclinedReload or not self.GetPathName():
+ return
+ try:
+ newstat = os.stat(self.GetPathName())
+ except os.error, (code, msg):
+ print "Warning on file %s - %s" % (self.GetPathName(), msg)
+ self.bDeclinedReload = 1
+ return
+ changed = (self.fileStat is None) or \
+ self.fileStat[0] != newstat[0] or \
+ self.fileStat[6] != newstat[6] or \
+ self.fileStat[8] != newstat[8] or \
+ self.fileStat[9] != newstat[9]
+ if changed:
+ question = None
+ if self.IsModified():
+ question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it and LOSE THE CHANGES in the source editor?" % self.GetPathName()
+ mbStyle = win32con.MB_YESNO | win32con.MB_DEFBUTTON2 # Default to "No"
+ else:
+ if not self.bAutoReload:
+ question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it?" % self.GetPathName()
+ mbStyle = win32con.MB_YESNO # Default to "Yes"
+ if question:
+ rc = win32ui.MessageBox(question, None, mbStyle)
+ if rc!=win32con.IDYES:
+ self.bDeclinedReload = 1
+ return
+ self.ReloadDocument()
+
+ def _DocumentStateChanged(self):
+ """Called whenever the documents state (on disk etc) has been changed
+ by the editor (eg, as the result of a save operation)
+ """
+ if self.GetPathName():
+ try:
+ self.fileStat = os.stat(self.GetPathName())
+ except os.error:
+ self.fileStat = None
+ else:
+ self.fileStat = None
+ self._UpdateUIForState()
+ self._ApplyOptionalToViews("_UpdateUIForState")
+
+ # Read-only document support - make it obvious to the user
+ # that the file is read-only.
+ def _IsReadOnly(self):
+ return self.fileStat is not None and (self.fileStat[0] & 128)==0
+
+ def _UpdateUIForState(self):
+ """Change the title to reflect the state of the document -
+ eg ReadOnly, Dirty, etc
+ """
+ filename = self.GetPathName()
+ if not filename: return # New file - nothing to do
+ try:
+ # This seems necessary so the internal state of the window becomes
+ # "visible". without it, it is still shown, but certain functions
+ # (such as updating the title) dont immediately work?
+ self.GetFirstView().ShowWindow(win32con.SW_SHOW)
+ title = win32ui.GetFileTitle(filename)
+ except win32ui.error:
+ title = filename
+ if self._IsReadOnly():
+ title = title + " (read-only)"
+ self.SetTitle(title)
+
+ def MakeDocumentWritable(self):
+ if not self.scModuleName: # No Source Control support.
+ win32ui.SetStatusText("Document is read-only, and no source-control system is configured")
+ win32api.MessageBeep()
+ return 0
+
+ # We have source control support - check if the user wants to use it.
+ msg = "Would you like to check this file out?"
+ defButton = win32con.MB_YESNO
+ if self.IsModified():
+ msg = msg + "\r\n\r\nALL CHANGES IN THE EDITOR WILL BE LOST"
+ defButton = win32con.MB_YESNO
+ if win32ui.MessageBox(msg, None, defButton)!=win32con.IDYES:
+ return 0
+
+ # Now call on the module to do it.
+ if self.scModule is None:
+ try:
+ self.scModule = __import__(self.scModuleName)
+ for part in string.split(self.scModuleName,'.')[1:]:
+ self.scModule = getattr(self.scModule, part)
+ except:
+ traceback.print_exc()
+ print "Error loading source control module."
+ return 0
+
+ if self.scModule.CheckoutFile(self.GetPathName()):
+ self.ReloadDocument()
+ return 1
+ return 0
+
+ def CheckMakeDocumentWritable(self):
+ if self._IsReadOnly():
+ return self.MakeDocumentWritable()
+ return 1
+
+ def SaveModified(self):
+ # Called as the document is closed. If we are about
+ # to prompt for a save, bring the document to the foreground.
+ if self.IsModified():
+ frame = self.GetFirstView().GetParentFrame()
+ try:
+ frame.MDIActivate()
+ frame.AutoRestore()
+ except:
+ print "Could not bring document to foreground"
+ return self._obj_.SaveModified()
+
diff --git a/Pythonwin/pywin/framework/editor/editor.py b/Pythonwin/pywin/framework/editor/editor.py
new file mode 100644
index 0000000000..d57608e279
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/editor.py
@@ -0,0 +1,490 @@
+#####################################################################
+#
+# editor.py
+#
+# A general purpose text editor, built on top of the win32ui edit
+# type, which is built on an MFC CEditView
+#
+#
+# We now support reloading of externally modified documented
+# (eg, presumably by some other process, such as source control or
+# another editor.
+# We also suport auto-loading of externally modified files.
+# - if the current document has not been modified in this
+# editor, but has been modified on disk, then the file
+# can be automatically reloaded.
+#
+# Note that it will _always_ prompt you if the file in the editor has been modified.
+
+
+import win32ui
+import win32api
+import win32con
+import regex
+import regsub
+import string
+import sys, os
+import traceback
+from pywin.mfc import docview, dialog, afxres
+
+from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
+
+patImport=regex.symcomp('import \(.*\)')
+patIndent=regex.compile('^\\([ \t]*[~ \t]\\)')
+
+ID_LOCATE_FILE = 0xe200
+ID_GOTO_LINE = 0xe2001
+
+# Key Codes that modify the bufffer when Ctrl or Alt are NOT pressed.
+MODIFYING_VK_KEYS = [win32con.VK_BACK, win32con.VK_TAB, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
+for k in range(48, 91):
+ MODIFYING_VK_KEYS.append(k)
+
+# Key Codes that modify the bufffer when Ctrl is pressed.
+MODIFYING_VK_KEYS_CTRL = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
+
+# Key Codes that modify the bufffer when Alt is pressed.
+MODIFYING_VK_KEYS_ALT = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
+
+
+# The editor itself starts here.
+# Using the MFC Document/View model, we have an EditorDocument, which is responsible for
+# managing the contents of the file, and a view which is responsible for rendering it.
+#
+# Due to a limitation in the Windows edit controls, we are limited to one view
+# per document, although nothing in this code assumes this (I hope!)
+
+isRichText=1 # We are using the Rich Text control. This has not been tested with value "0" for quite some time!
+
+#ParentEditorDocument=docview.Document
+from document import EditorDocumentBase
+ParentEditorDocument=EditorDocumentBase
+class EditorDocument(ParentEditorDocument):
+ #
+ # File loading and saving operations
+ #
+ def OnOpenDocument(self, filename):
+ #
+ # handle Unix and PC text file format.
+ #
+
+ # Get the "long name" of the file name, as it may have been translated
+ # to short names by the shell.
+ self.SetPathName(filename) # Must set this early!
+ # Now do the work!
+ self.BeginWaitCursor()
+ win32ui.SetStatusText("Loading file...",1)
+ try:
+ f = open(filename,"rb")
+ except IOError:
+ win32ui.MessageBox(filename + '\nCan not find this file\nPlease verify that the correct path and file name are given')
+ self.EndWaitCursor()
+ return 0
+ raw=f.read()
+ f.close()
+ contents = self.TranslateLoadedData(raw)
+ rc = 0
+ if win32ui.IsWin32s() and len(contents)>62000: # give or take a few bytes
+ win32ui.MessageBox("This file is too big for Python on Windows 3.1\r\nPlease use another editor to view this file.")
+ else:
+ try:
+ self.GetFirstView().SetWindowText(contents)
+ rc = 1
+ except TypeError: # Null byte in file.
+ win32ui.MessageBox("This file contains NULL bytes, and can not be edited")
+ rc = 0
+
+ self.EndWaitCursor()
+ self.SetModifiedFlag(0) # No longer dirty
+ self._DocumentStateChanged()
+ return rc
+
+ def TranslateLoadedData(self, data):
+ """Given raw data read from a file, massage it suitable for the edit window"""
+ # if a CR in the first 250 chars, then perform the expensive translate
+ if string.find(data[:250],'\r')==-1:
+ win32ui.SetStatusText("Translating from Unix file format - please wait...",1)
+ return regsub.gsub('\r*\n','\r\n',data)
+ else:
+ return data
+
+ def SaveFile(self, fileName):
+ if isRichText:
+ view = self.GetFirstView()
+ view.SaveTextFile(fileName)
+ else: # Old style edit view window.
+ self.GetFirstView().SaveFile(fileName)
+ try:
+ # Make sure line cache has updated info about me!
+ import linecache
+ linecache.checkcache()
+ except:
+ pass
+
+ #
+ # Color state stuff
+ #
+ def SetAllLineColors(self, color = None):
+ for view in self.GetAllViews():
+ view.SetAllLineColors(color)
+
+ def SetLineColor(self, lineNo, color):
+ "Color a line of all views"
+ for view in self.GetAllViews():
+ view.SetLineColor(lineNo, color)
+
+
+# def StreamTextOut(self, data): ### This seems unreliable???
+# self.saveFileHandle.write(data)
+# return 1 # keep em coming!
+
+#ParentEditorView=docview.EditView
+ParentEditorView=docview.RichEditView
+class EditorView(ParentEditorView):
+ def __init__(self, doc):
+ ParentEditorView.__init__(self, doc)
+ if isRichText:
+ self.SetWordWrap(win32ui.CRichEditView_WrapNone)
+
+ self.addToMRU = 1
+ self.HookHandlers()
+ self.bCheckingFile = 0
+
+ self.defCharFormat = GetEditorFontOption("Default Font", defaultCharacterFormat)
+
+ # Smart tabs override everything else if context can be worked out.
+ self.bSmartTabs = GetEditorOption("Smart Tabs", 1)
+
+ self.tabSize = GetEditorOption("Tab Size", 8)
+ self.indentSize = GetEditorOption("Indent Size", 8)
+ # If next indent is at a tab position, and useTabs is set, a tab will be inserted.
+ self.bUseTabs = GetEditorOption("Use Tabs", 1)
+
+ def OnInitialUpdate(self):
+ rc = self._obj_.OnInitialUpdate()
+ self.SetDefaultCharFormat(self.defCharFormat)
+ return rc
+
+ def OnDestroy(self, msg):
+ self._DeleteReloadIdleHandler()
+ return ParentEditorView.OnDestroy(self, msg)
+
+ def CutCurLine(self):
+ curLine = self._obj_.LineFromChar()
+ nextLine = curLine+1
+ start = self._obj_.LineIndex(curLine)
+ end = self._obj_.LineIndex(nextLine)
+ if end==0: # must be last line.
+ end = start + self.end.GetLineLength(curLine)
+ self._obj_.SetSel(start,end)
+ self._obj_.Cut()
+ def _PrepareUserStateChange(self):
+ "Return selection, lineindex, etc info, so it can be restored"
+ self.SetRedraw(0)
+ return self.GetModify(), self.GetSel(), self.GetFirstVisibleLine()
+ def _EndUserStateChange(self, info):
+ scrollOff = info[2] - self.GetFirstVisibleLine()
+ if scrollOff:
+ self.LineScroll(scrollOff)
+ self.SetSel(info[1])
+ self.SetModify(info[0])
+ self.SetRedraw(1)
+ self.InvalidateRect()
+ self.UpdateWindow()
+
+ def _UpdateUIForState(self):
+ self.SetReadOnly(self.GetDocument()._IsReadOnly())
+
+ def SetAllLineColors(self, color = None):
+ if isRichText:
+ info = self._PrepareUserStateChange()
+ try:
+ if color is None: color = self.defCharFormat[4]
+ self.SetSel(0,-1)
+ self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
+ finally:
+ self._EndUserStateChange(info)
+
+ def SetLineColor(self, lineNo, color):
+ "lineNo is the 1 based line number to set. If color is None, default color is used."
+ if isRichText:
+ info = self._PrepareUserStateChange()
+ try:
+ if color is None: color = self.defCharFormat[4]
+ lineNo = lineNo-1
+ startIndex = self.LineIndex(lineNo)
+ if startIndex!=-1:
+ self.SetSel(startIndex, self.LineIndex(lineNo+1))
+ self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
+ finally:
+ self._EndUserStateChange(info)
+
+ def Indent(self):
+ """Insert an indent to move the cursor to the next tab position.
+
+ Honors the tab size and 'use tabs' settings. Assumes the cursor is already at the
+ position to be indented, and the selection is a single character (ie, not a block)
+ """
+ start, end = self._obj_.GetSel()
+ startLine = self._obj_.LineFromChar(start)
+ line = self._obj_.GetLine(startLine)
+ realCol = start - self._obj_.LineIndex(startLine)
+ # Calulate the next tab stop.
+ # Expand existing tabs.
+ curCol = 0
+ for ch in line[:realCol]:
+ if ch=='\t':
+ curCol = ((curCol / self.tabSize) + 1) * self.tabSize
+ else:
+ curCol = curCol + 1
+ nextColumn = ((curCol / self.indentSize) + 1) * self.indentSize
+# print "curCol is", curCol, "nextColumn is", nextColumn
+ ins = None
+ if self.bSmartTabs:
+ # Look for some context.
+ if realCol==0: # Start of the line - see if the line above can tell us
+ lookLine = startLine-1
+ while lookLine >= 0:
+ check = self._obj_.GetLine(lookLine)[0:1]
+ if check in ['\t', ' ']:
+ ins = check
+ break
+ lookLine = lookLine - 1
+ else: # See if the previous char can tell us
+ check = line[realCol-1]
+ if check in ['\t', ' ']:
+ ins = check
+
+ # Either smart tabs off, or not smart enough!
+ # Use the "old style" settings.
+ if ins is None:
+ if self.bUseTabs and nextColumn % self.tabSize==0:
+ ins = '\t'
+ else:
+ ins = ' '
+
+ if ins == ' ':
+ # Calc the number of spaces to take us to the next stop
+ ins = ins * (nextColumn - curCol)
+
+ self._obj_.ReplaceSel(ins)
+
+
+ def BlockDent(self, isIndent, startLine, endLine):
+ " Indent/Undent all lines specified "
+ if not self.GetDocument().CheckMakeDocumentWritable(): return 0
+ tabSize=self.tabSize # hard-code for now!
+ info = self._PrepareUserStateChange()
+ try:
+ for lineNo in range(startLine, endLine):
+ pos=self._obj_.LineIndex(lineNo)
+ self._obj_.SetSel(pos, pos)
+ if isIndent:
+ self.Indent()
+ else:
+ line = self._obj_.GetLine(lineNo)
+ try:
+ noToDel = 0
+ if line[0]=='\t':
+ noToDel = 1
+ elif line[0]==' ':
+ for noToDel in range(0,tabSize):
+ if line[noToDel]!=' ':
+ break
+ else:
+ noToDel=tabSize
+ if noToDel:
+ self._obj_.SetSel(pos, pos+noToDel)
+ self._obj_.Clear()
+ except IndexError:
+ pass
+ finally:
+ self._EndUserStateChange(info)
+ self.GetDocument().SetModifiedFlag(1) # Now dirty
+ self._obj_.SetSel(self.LineIndex(startLine), self.LineIndex(endLine))
+
+ def GotoLine(self, lineNo = None):
+ try:
+ if lineNo is None:
+ lineNo = string.atoi(raw_input("Enter Line Number"))
+ except (ValueError, KeyboardInterrupt):
+ return 0
+ self.GetLineCount() # Seems to be needed when file first opened???
+ charNo = self.LineIndex(lineNo-1)
+ self.SetSel(charNo)
+
+ def HookHandlers(self): # children can override, but should still call me!
+# self.HookAllKeyStrokes(self.OnKey)
+ self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
+ self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
+ self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
+ self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
+ self.HookKeyStroke(self.OnKeyCtrlY, 25) # ^Y
+ self.HookKeyStroke(self.OnKeyCtrlG, 7) # ^G
+ self.HookKeyStroke(self.OnKeyTab, 9) # TAB
+ self.HookKeyStroke(self.OnKeyEnter, 13) # Enter
+ self.HookCommand(self.OnCmdLocateFile, ID_LOCATE_FILE)
+ self.HookCommand(self.OnCmdGotoLine, ID_GOTO_LINE)
+ self.HookCommand(self.OnEditPaste, afxres.ID_EDIT_PASTE)
+ self.HookCommand(self.OnEditCut, afxres.ID_EDIT_CUT)
+
+ # Hook Handlers
+ def OnKillFocus(self,msg):
+ self._DeleteReloadIdleHandler()
+
+ def OnSetFocus(self,msg):
+ self.CheckExternalDocumentUpdated(self.CheckExternalDocumentUpdated,0)
+ self._AddReloadIdleHandler()
+
+ def OnRClick(self,params):
+ menu = win32ui.CreatePopupMenu()
+
+ # look for a module name
+ line=string.strip(self._obj_.GetLine())
+ flags=win32con.MF_STRING|win32con.MF_ENABLED
+ if patImport.match(line)==len(line):
+ menu.AppendMenu(flags, ID_LOCATE_FILE, "&Locate %s.py"%patImport.group('name'))
+ menu.AppendMenu(win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, win32ui.ID_EDIT_UNDO, '&Undo')
+ menu.AppendMenu(win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
+ menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
+ menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
+ menu.AppendMenu(flags, win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
+ menu.AppendMenu(flags, win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, ID_GOTO_LINE, '&Goto line...')
+ menu.TrackPopupMenu(params[5])
+ return 0
+
+ def OnCmdGotoLine(self, cmd, code):
+ self.GotoLine()
+ return 0
+
+ def OnCmdLocateFile(self, cmd, code):
+ modName = patImport.group('name')
+ if not modName:
+ return 0
+ import pywin.framework.scriptutils
+ fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
+ if fileName is None:
+ win32ui.SetStatusText("Can't locate module %s" % modName)
+ else:
+ win32ui.GetApp().OpenDocumentFile(fileName)
+ return 0
+
+ # Key handlers
+ def OnKeyEnter(self, key):
+ if not self.GetDocument().CheckMakeDocumentWritable(): return 0
+ curLine = self._obj_.GetLine()
+ self._obj_.ReplaceSel('\r\n') # insert the newline
+ # If the current line indicates the next should be indented,
+ # then copy the current indentation to this line.
+ res = patIndent.match(curLine,0)
+ if res>0 and string.strip(curLine):
+ curIndent = patIndent.group(1)
+ self._obj_.ReplaceSel(curIndent)
+ return 0 # dont pass on
+
+ def OnKeyCtrlY(self, key):
+ if not self.GetDocument().CheckMakeDocumentWritable(): return 0
+ self.CutCurLine()
+ return 0 # dont let him have it!
+ def OnKeyCtrlG(self, key):
+ self.GotoLine()
+ return 0 # dont let him have it!
+ def OnKeyTab(self, key):
+ if not self.GetDocument().CheckMakeDocumentWritable(): return 0
+ start, end = self._obj_.GetSel()
+ if start==end: # normal TAB key
+ self.Indent()
+ return 0 # we handled this.
+
+ # Otherwise it is a block indent/dedent.
+ if start>end:
+ start, end = end, start # swap them.
+ startLine = self._obj_.LineFromChar(start)
+ endLine = self._obj_.LineFromChar(end)
+
+ self.BlockDent(win32api.GetKeyState(win32con.VK_SHIFT)>=0, startLine, endLine)
+ return 0
+
+
+ def OnEditPaste(self, id, code):
+ # Return 1 if we can make the file editable.(or it already is!)
+ return self.GetDocument().CheckMakeDocumentWritable()
+
+ def OnEditCut(self, id, code):
+ # Return 1 if we can make the file editable.(or it already is!)
+ return self.GetDocument().CheckMakeDocumentWritable()
+
+ def OnKeyDown(self, msg):
+ key = msg[2]
+ if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
+ modList = MODIFYING_VK_KEYS_CTRL
+ elif win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
+ modList = MODIFYING_VK_KEYS_ALT
+ else:
+ modList = MODIFYING_VK_KEYS
+
+ if key in modList:
+ # Return 1 if we can make the file editable.(or it already is!)
+ return self.GetDocument().CheckMakeDocumentWritable()
+ return 1 # Pass it on OK
+
+# def OnKey(self, key):
+# return self.GetDocument().CheckMakeDocumentWritable()
+
+ #
+ # Support for checking the external file to see if it is changed.
+ # We set up an idle time handler only when the view has focus.
+ # This handler continually checks the file behind this document, but
+ # as it is in idle time, no one notices :-)
+ #
+ def _AddReloadIdleHandler(self):
+ win32ui.GetApp().AddIdleHandler(self.CheckExternalDocumentUpdated)
+
+ def _DeleteReloadIdleHandler(self):
+ if win32ui.GetApp().HaveIdleHandler(self.CheckExternalDocumentUpdated):
+ win32ui.GetApp().DeleteIdleHandler(self.CheckExternalDocumentUpdated)
+
+ def CheckExternalDocumentUpdated(self, handler, count):
+ if self._obj_ is None or self.bCheckingFile: return
+ self.bCheckingFile = 1
+ try:
+ self.GetDocument().CheckExternalDocumentUpdated()
+ except:
+ traceback.print_exc()
+ print "Idle handler failed!"
+ self._DeleteReloadIdleHandler()
+ self.bCheckingFile = 0
+ return 0 # No more idle handling required.
+
+from template import EditorTemplateBase
+class EditorTemplate(EditorTemplateBase):
+ def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
+ if makeDoc is None: makeDoc = EditorDocument
+ if makeView is None: makeView = EditorView
+ EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
+
+ def _CreateDocTemplate(self, resourceId):
+ return win32ui.CreateRichEditDocTemplate(resourceId)
+
+ def CreateWin32uiDocument(self):
+ return self.DoCreateRichEditDoc()
+
+def Create(fileName = None, title=None, template = None):
+ return editorTemplate.OpenDocumentFile(fileName)
+
+from pywin.framework.editor import GetDefaultEditorModuleName
+prefModule = GetDefaultEditorModuleName()
+# Initialize only if this is the "default" editor.
+if __name__==prefModule:
+ # For debugging purposes, when this module may be reloaded many times.
+ try:
+ win32ui.GetApp().RemoveDocTemplate(editorTemplate)
+ except (NameError, win32ui.error):
+ pass
+
+ editorTemplate = EditorTemplate()
+ win32ui.GetApp().AddDocTemplate(editorTemplate)
diff --git a/Pythonwin/pywin/framework/editor/frame.py b/Pythonwin/pywin/framework/editor/frame.py
new file mode 100644
index 0000000000..5265a02d92
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/frame.py
@@ -0,0 +1,29 @@
+# frame.py - The MDI frame window for an editor.
+import pywin.framework.window
+import win32ui
+import win32con
+import afxres
+
+import ModuleBrowser
+
+class EditorFrame(pywin.framework.window.MDIChildWnd):
+ def OnCreateClient(self, cp, context):
+ splitter = win32ui.CreateSplitter()
+ splitter.CreateStatic (self, 1, 2)
+ # Create the default view as specified by the template (ie, the editor view)
+ view = context.template.MakeView(context.doc)
+ # Create the browser view.
+ otherView = ModuleBrowser.BrowserView(context.doc)
+ # Note we must add the default view first, so that doc.GetFirstView() returns the editor view.
+ splitter.CreateView(view, 0, 1, (0,0)) # size ignored.
+ splitter.CreateView (otherView, 0, 0, (0, 0))
+ # Restrict the size of the browser splitter (and we can avoid filling
+ # it until it is shown)
+ splitter.SetColumnInfo(0, 10, 20)
+ # And the active view is our default view (so it gets initial focus)
+ self.SetActiveView(view)
+
+ def GetEditorView(self):
+ # In a multi-view (eg, splitter) environment, get
+ # an editor (ie, scintilla) view
+ return self.GetActiveDocument().GetFirstView()
\ No newline at end of file
diff --git a/Pythonwin/pywin/framework/editor/template.py b/Pythonwin/pywin/framework/editor/template.py
new file mode 100644
index 0000000000..e00f79511f
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/template.py
@@ -0,0 +1,48 @@
+import string
+import win32ui
+import win32api
+from pywin.mfc import docview
+import pywin.framework.window
+import os
+import frame
+
+ParentEditorTemplate=docview.DocTemplate
+class EditorTemplateBase(ParentEditorTemplate):
+ def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
+ if makeFrame is None: makeFrame = frame.EditorFrame
+ ParentEditorTemplate.__init__(self, res, makeDoc, makeFrame, makeView)
+
+ def _CreateDocTemplate(self, resourceId):
+ assert 0, "You must override this"
+ def CreateWin32uiDocument(self):
+ assert 0, "You must override this"
+ def MatchDocType(self, fileName, fileType):
+ doc = self.FindOpenDocument(fileName)
+ if doc: return doc
+ ext = string.lower(os.path.splitext(fileName)[1])
+ if ext =='.txt' or ext=='.py':
+ return win32ui.CDocTemplate_Confidence_yesAttemptNative
+ return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
+
+ def InitialUpdateFrame(self, frame, doc, makeVisible=1):
+ self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
+ doc._UpdateUIForState()
+
+ def GetPythonPropertyPages(self):
+ """Returns a list of property pages
+ """
+ import configui
+ return [configui.EditorPropertyPage()]
+
+ def OpenDocumentFile(self, filename, bMakeVisible = 1):
+ if filename is not None:
+ try:
+ path = os.path.split(filename)[0]
+# print "The editor is translating", `filename`,"to",
+ filename = win32api.FindFiles(filename)[0][8]
+ filename = os.path.join(path, filename)
+# print `filename`
+ except (win32api.error, IndexError), details:
+ pass
+# print "Couldnt get the full filename!", details
+ return self._obj_.OpenDocumentFile(filename, bMakeVisible)
diff --git a/Pythonwin/pywin/framework/editor/vss.py b/Pythonwin/pywin/framework/editor/vss.py
new file mode 100644
index 0000000000..03e68cfef6
--- /dev/null
+++ b/Pythonwin/pywin/framework/editor/vss.py
@@ -0,0 +1,92 @@
+# vss.py -- Source Control using Microsoft VSS.
+
+# Provides routines for checking files out of VSS.
+#
+# Uses an INI file very similar to how VB integrates with VSS - even
+# as far as using the same name.
+
+# The file must be named "Mssccprj.scc", and be in the format of
+# an INI file. This file may be in a parent directory, in which
+# case the project name will be built from what is specified in the
+# ini file, plus the path from the INI file to the file itself.
+#
+# The INI file should have a [Python] section, and a
+# Project=Project Name
+# and optionally
+# Database=??
+
+
+import win32ui, win32api, win32con, os, string, sys
+
+import traceback
+
+g_iniName = "Mssccprj.scc" # Use the same INI name as VB!
+
+g_sourceSafe = None
+
+def FindVssProjectInfo(fullfname):
+ """Looks up the file system for an INI file describing the project.
+
+ Looking up the tree is for ni style packages.
+
+ Returns (projectName, pathToFileName) where pathToFileName contains
+ the path from the ini file to the actual file.
+ """
+ path, fnameonly = os.path.split(fullfname)
+ origPath = path
+ project = ""
+ retPaths = [fnameonly]
+ while not project:
+ iniName = os.path.join(path, g_iniName)
+ database = win32api.GetProfileVal("Python","Database", "", iniName)
+ project = win32api.GetProfileVal("Python","Project", "", iniName)
+ if project:
+ break;
+ # No valid INI file in this directory - look up a level.
+ path, addpath = os.path.split(path)
+ if not addpath: # Root?
+ break
+ retPaths.insert(0, addpath)
+ if not project:
+ win32ui.MessageBox("%s\r\n\r\nThis directory is not configured for Python/VSS" % origPath)
+ return
+ return project, string.join(retPaths, "/")
+
+
+def CheckoutFile(fileName):
+ global g_sourceSafe
+ import pythoncom
+ ok = 0
+ # Assumes the fileName has a complete path,
+ # and that the INI file can be found in that path
+ # (or a parent path if a ni style package)
+ try:
+ import win32com.client, win32com.client.gencache
+ mod = win32com.client.gencache.EnsureModule('{783CD4E0-9D54-11CF-B8EE-00608CC9A71F}', 0, 5, 0)
+ if mod is None:
+ win32ui.MessageBox("VSS does not appear to be installed. The TypeInfo can not be created")
+ return ok
+
+ rc = FindVssProjectInfo(fileName)
+ if rc is None:
+ return
+ project, vssFname = rc
+ if g_sourceSafe is None:
+ g_sourceSafe=win32com.client.Dispatch("SourceSafe")
+ # SS seems a bit wierd. It defaults the arguments as empty strings, but
+ # then complains when they are used - so we pass "Missing"
+ g_sourceSafe.Open(pythoncom.Missing, pythoncom.Missing, pythoncom.Missing)
+ item = g_sourceSafe.VSSItem("$/%s/%s" % (project, vssFname))
+ item.Checkout(None, fileName)
+ ok = 1
+ except pythoncom.com_error, (hr, msg, exc, arg):
+ if exc:
+ msg = exc[2]
+ win32ui.MessageBox(msg, "Error checking out file")
+ except:
+ typ, val, tb = sys.exc_info()
+ traceback.print_exc()
+ win32ui.MessageBox("%s - %s" % (str(typ), str(val)),"Error checking out file")
+ return ok
+
+
diff --git a/Pythonwin/pywin/framework/help.py b/Pythonwin/pywin/framework/help.py
new file mode 100644
index 0000000000..5175c1585a
--- /dev/null
+++ b/Pythonwin/pywin/framework/help.py
@@ -0,0 +1,97 @@
+ # help.py - help utilities for PythonWin.
+import win32api
+import win32con
+import win32ui
+import string
+import sys
+import regutil
+import string, os
+
+def OpenHelpFile(fileName, helpCmd = None, helpArg = None):
+ "Open a help file, given a full path"
+ # default help arg.
+ win32ui.DoWaitCursor(1)
+ try:
+ ext = os.path.splitext(fileName)[1]
+ if string.lower(ext) == ".hlp":
+ if helpCmd is None: helpCmd = win32con.HELP_FINDER
+ win32api.WinHelp( win32ui.GetMainFrame().GetSafeHwnd(), fileName, helpCmd, helpArg)
+ else:
+ # Hope that the extension is registered, and we know what to do!
+ win32api.ShellExecute(0, "open", fileName, None, "", win32con.SW_SHOW)
+ return fileName
+ finally:
+ win32ui.DoWaitCursor(-1)
+
+def ListAllHelpFiles():
+ """Returns a list of (helpDesc, helpFname) for all registered help files
+ """
+ import regutil
+ retList = []
+ try:
+ key = win32api.RegOpenKey(regutil.GetRootKey(), regutil.BuildDefaultPythonKey() + "\\Help", 0, win32con.KEY_READ)
+ except win32api.error, (code, fn, details):
+ import winerror
+ if code!=winerror.ERROR_FILE_NOT_FOUND:
+ raise win32api.error, (code, fn, desc)
+ return retList
+ try:
+ keyNo = 0
+ while 1:
+ try:
+ helpDesc = win32api.RegEnumKey(key, keyNo)
+ helpFile = win32api.RegQueryValue(key, helpDesc)
+ retList.append(helpDesc, helpFile)
+ keyNo = keyNo + 1
+ except win32api.error, (code, fn, desc):
+ import winerror
+ if code!=winerror.ERROR_NO_MORE_ITEMS:
+ raise win32api.error, (code, fn, desc)
+ break
+ finally:
+ win32api.RegCloseKey(key)
+ return retList
+
+def SelectAndRunHelpFile():
+ from pywin.dialogs import list
+ helpFiles = ListAllHelpFiles()
+ index = list.SelectFromLists("Select Help file", helpFiles, ["Title"])
+ if index is not None:
+ OpenHelpFile(helpFiles[index][1])
+
+
+helpIDMap = None
+
+def SetHelpMenuOtherHelp(mainMenu):
+ """Modifies the main Help Menu to handle all registered help files.
+ mainMenu -- The main menu to modify - usually from docTemplate.GetSharedMenu()
+ """
+
+ # Load all help files from the registry.
+ if helpIDMap is None:
+ global helpIDMap
+ helpIDMap = {}
+ cmdID = win32ui.ID_HELP_OTHER
+ excludeList = ['Main Python Documentation', 'Pythonwin Reference']
+ firstList = ListAllHelpFiles()
+ helpDescs = []
+ for desc, fname in firstList:
+ if desc not in excludeList:
+ helpIDMap[cmdID] = (desc, fname)
+ win32ui.GetMainFrame().HookCommand(HandleHelpOtherCommand, cmdID)
+ cmdID = cmdID + 1
+
+ helpMenu = mainMenu.GetSubMenu(mainMenu.GetMenuItemCount()-1) # Help menu always last.
+ otherHelpMenuPos = 2 # cant search for ID, as sub-menu has no ID.
+ otherMenu = helpMenu.GetSubMenu(otherHelpMenuPos)
+ while otherMenu.GetMenuItemCount():
+ otherMenu.DeleteMenu(0, win32con.MF_BYPOSITION)
+
+ if helpIDMap:
+ for id, (desc, fname) in helpIDMap.items():
+ otherMenu.AppendMenu(win32con.MF_ENABLED|win32con.MF_STRING,id, desc)
+ else:
+ helpMenu.EnableMenuItem(otherHelpMenuPos, win32con.MF_BYPOSITION | win32con.MF_GRAYED)
+
+def HandleHelpOtherCommand(cmd, code):
+ OpenHelpFile(helpIDMap[cmd][1])
diff --git a/Pythonwin/pywin/framework/interact.py b/Pythonwin/pywin/framework/interact.py
new file mode 100644
index 0000000000..cbe4fa5331
--- /dev/null
+++ b/Pythonwin/pywin/framework/interact.py
@@ -0,0 +1,763 @@
+##################################################################
+#
+# Interactive Shell Window
+#
+
+import sys
+import code
+import string
+
+import win32ui
+import win32api
+import win32con
+import traceback
+import afxres
+import array
+import __main__
+
+import pywin.scintilla.formatter
+import pywin.scintilla.control
+import pywin.scintilla.IDLEenvironment
+import pywin.framework.app
+
+trace=pywin.scintilla.formatter.trace
+
+import winout
+
+import re
+# from IDLE.
+_is_block_opener = re.compile(r":\s*(#.*)?$").search
+_is_block_closer = re.compile(r"""
+ \s*
+ ( return
+ | break
+ | continue
+ | raise
+ | pass
+ )
+ \b
+""", re.VERBOSE).match
+
+tracebackHeader = "Traceback (innermost last):\n"
+
+sectionProfile = "Interactive Window"
+valueFormatTitle = "FormatTitle"
+valueFormatInput = "FormatInput"
+valueFormatOutput = "FormatOutput"
+valueFormatOutputError = "FormatOutputError"
+
+# These are defaults only. Values are read from the registry.
+formatTitle = (-536870897, 0, 220, 0, 16711680, 184, 34, 'Arial')
+formatInput = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
+formatOutput = (-402653169, 0, 200, 0, 8421376, 0, 49, 'Courier New')
+formatOutputError = (-402653169, 0, 200, 0, 255, 0, 49, 'Courier New')
+
+# couple of exceptions defined for this module
+excNoValidCommand = 'No Valid Command'
+excEmptyCommand = 'Empty Command'
+excContinueCommand = 'Continue Command'
+
+try:
+ sys.ps1
+except AttributeError:
+ sys.ps1 = '>>> '
+ sys.ps2 = '... '
+
+def LoadPreference(preference, default = ""):
+ return win32ui.GetProfileVal(sectionProfile, preference, default)
+
+def LoadFontPreferences():
+ global formatTitle, formatInput, formatOutput, formatOutputError
+ try:
+ fmt = win32ui.GetProfileVal( sectionProfile, valueFormatTitle, "" )
+ if len(fmt): formatTitle = eval(fmt)
+ fmt = win32ui.GetProfileVal( sectionProfile, valueFormatInput, "" )
+ if len(fmt): formatInput = eval(fmt)
+ fmt = win32ui.GetProfileVal( sectionProfile, valueFormatOutput, "" )
+ if len(fmt): formatOutput = eval(fmt)
+ fmt = win32ui.GetProfileVal( sectionProfile, valueFormatOutputError, "" )
+ if len(fmt): formatOutputError = eval(fmt)
+ except:
+ win32ui.MessageBox("The Font Preferences could not be loaded")
+
+def SavePreference( prefName, prefValue ):
+ win32ui.WriteProfileVal( sectionProfile, prefName, prefValue )
+
+def SaveFontPreferences():
+ win32ui.WriteProfileVal( sectionProfile, valueFormatTitle, str(formatTitle) )
+ win32ui.WriteProfileVal( sectionProfile, valueFormatInput, str(formatInput) )
+ win32ui.WriteProfileVal( sectionProfile, valueFormatOutput, str(formatOutput) )
+ win32ui.WriteProfileVal( sectionProfile, valueFormatOutputError, str(formatOutputError) )
+
+def GetPromptPrefix(line):
+ ps1=sys.ps1
+ if line[:len(ps1)]==ps1: return ps1
+ ps2=sys.ps2
+ if line[:len(ps2)]==ps2: return ps2
+
+#############################################################
+#
+# Colorizer related code.
+#
+#############################################################
+STYLE_INTERACTIVE_EOL = "Interactive EOL"
+STYLE_INTERACTIVE_OUTPUT = "Interactive Output"
+STYLE_INTERACTIVE_PROMPT = "Interactive Prompt"
+STYLE_INTERACTIVE_BANNER = "Interactive Banner"
+STYLE_INTERACTIVE_ERROR = "Interactive Error"
+STYLE_INTERACTIVE_ERROR_FINALLINE = "Interactive Error (final line)"
+
+INTERACTIVE_STYLES = [STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_OUTPUT, STYLE_INTERACTIVE_PROMPT, STYLE_INTERACTIVE_BANNER, STYLE_INTERACTIVE_ERROR, STYLE_INTERACTIVE_ERROR_FINALLINE]
+
+FormatterParent = pywin.scintilla.formatter.PythonSourceFormatter
+class InteractiveFormatter(FormatterParent):
+ def __init__(self, scintilla):
+ FormatterParent.__init__(self, scintilla)
+
+ def SetStyles(self):
+ FormatterParent.SetStyles(self)
+ Style = pywin.scintilla.formatter.Style
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_PROMPT ) )
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_PROMPT, formatInput ) )
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_OUTPUT, formatOutput) )
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_BANNER, formatTitle ) )
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_ERROR, formatOutputError ) )
+ self.RegisterStyle( Style(STYLE_INTERACTIVE_ERROR_FINALLINE, STYLE_INTERACTIVE_ERROR ) )
+
+ def ColorizeInteractiveCode(self, cdoc, styleStart, stylePyStart):
+ lengthDoc = len(cdoc)
+ if lengthDoc == 0: return
+ state = styleStart
+ chNext = cdoc[0]
+ startSeg = 0
+ i = 0
+ lastState=state # debug only
+ while i < lengthDoc:
+ ch = chNext
+ chNext = cdoc[i+1:i+2]
+
+ if state == STYLE_INTERACTIVE_EOL:
+ if ch not in '\r\n':
+ self.ColorSeg(startSeg, i-1, state)
+ startSeg = i
+ if ch in [sys.ps1[0], sys.ps2[0]]:
+ state = STYLE_INTERACTIVE_PROMPT
+ elif cdoc[i:i+len(tracebackHeader)]==tracebackHeader:
+ state = STYLE_INTERACTIVE_ERROR
+ else:
+ state = STYLE_INTERACTIVE_OUTPUT
+ elif state == STYLE_INTERACTIVE_PROMPT:
+ if ch not in sys.ps1 + sys.ps2 + " ":
+ self.ColorSeg(startSeg, i-1, state)
+ startSeg = i
+ state = stylePyStart # Start coloring Python code.
+ elif state in [STYLE_INTERACTIVE_OUTPUT]:
+ if ch in '\r\n':
+ self.ColorSeg(startSeg, i-1, state)
+ startSeg = i
+ state = STYLE_INTERACTIVE_EOL
+ elif state == STYLE_INTERACTIVE_ERROR:
+ if ch in '\r\n' and chNext and chNext not in string.whitespace:
+ # Everything including me
+ self.ColorSeg(startSeg, i, state)
+ startSeg = i+1
+ state = STYLE_INTERACTIVE_ERROR_FINALLINE
+ elif state == STYLE_INTERACTIVE_ERROR_FINALLINE:
+ if ch in '\r\n':
+ self.ColorSeg(startSeg, i-1, state)
+ startSeg = i
+ state = STYLE_INTERACTIVE_EOL
+ elif state == STYLE_INTERACTIVE_BANNER:
+ if ch in '\r\n' and chNext and chNext in ">[":
+ # Everything including me
+ self.ColorSeg(startSeg, i-1, state)
+ startSeg = i
+ state = STYLE_INTERACTIVE_EOL
+ else:
+ # It is a PythonColorizer state - seek past the end of the line
+ # and ask the Python colorizer to color that.
+ end = startSeg
+ while end < lengthDoc and cdoc[end] not in '\r\n':
+ end = end + 1
+ self.ColorizePythonCode( cdoc[:end], startSeg, state)
+ stylePyStart = self.GetStringStyle(end-1)
+ if stylePyStart is None:
+ stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
+ else:
+ stylePyStart = stylePyStart.name
+ startSeg =end
+ i = end - 1 # ready for increment.
+ chNext = cdoc[end:end+1]
+ state = STYLE_INTERACTIVE_EOL
+ if lastState != state:
+ lastState = state
+ i = i + 1
+ # and the rest
+ if startSeg 1:
+ # Likely we are being asked to color from the start of the line.
+ # The character before should be '\n', and formatted EOL
+ # The character before is the continued Python style
+ # (or an interactive style). If the Python style is a string style, then we continue
+ # the style, otherwise we reset to the default style to account for the '\n' which
+ # we hide from the Python formatter.
+ look = start -1
+ while look and self.scintilla.SCIGetCharAt(look) in '\n\r':
+ look = look - 1
+ if look and look < start-1:
+ strstyle = self.GetStringStyle(look)
+ if strstyle is not None:
+ stylePyStart = strstyle.name
+ if stylePyStart is None: stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
+
+ if start > 0:
+ stylenum = self.scintilla.SCIGetStyleAt(start - 1)
+ styleStart = self.GetStyleByNum(stylenum).name
+ else:
+ styleStart = STYLE_INTERACTIVE_BANNER
+ self.scintilla.SCIStartStyling(start, 31)
+ self.style_buffer = array.array("c", chr(0)*len(stringVal))
+ self.ColorizeInteractiveCode(stringVal, styleStart, stylePyStart)
+ self.scintilla.SCISetStylingEx(self.style_buffer)
+ self.style_buffer = None
+
+###############################################################
+#
+# This class handles the Python interactive interpreter.
+#
+# It uses a basic EditWindow, and does all the magic.
+# This is triggered by the enter key hander attached by the
+# start-up code. It determines if a command is to be executed
+# or continued (ie, emit "... ") by snooping around the current
+# line, looking for the prompts
+#
+class PythonwinInteractiveInterpreter(code.InteractiveInterpreter):
+ def __init__(self, locals = None, globals = None):
+ if locals is None: locals = __main__.__dict__
+ if globals is None: globals = locals
+ self.globals = globals
+ code.InteractiveInterpreter.__init__(self, locals)
+ def showsyntaxerror(self, filename=None):
+ sys.stderr.write(tracebackHeader) # So the color syntaxer recognises it.
+ code.InteractiveInterpreter.showsyntaxerror(self, filename)
+ def runcode(self, code):
+ try:
+ exec code in self.globals, self.locals
+ except SystemExit:
+ raise
+ except:
+ self.showtraceback()
+
+class InteractiveCore:
+ def __init__(self, banner = None):
+ self.banner = banner
+ def Init(self):
+ self.oldStdOut = self.oldStdErr = None
+ LoadFontPreferences()
+
+# self.SetWordWrap(win32ui.CRichEditView_WrapNone)
+ self.interp = PythonwinInteractiveInterpreter()
+
+ self.OutputGrab() # Release at cleanup.
+
+ if self.GetTextLength()==0:
+ if self.banner is None:
+ sys.stderr.write("PythonWin %s on %s\n%s\n" % (sys.version, sys.platform, sys.copyright) )
+ sys.stderr.write("Portions %s\n" % (win32ui.copyright) )
+ else:
+ sys.stderr.write(banner)
+ self.AppendToPrompt([])
+
+ def SetContext(self, globals, locals, name = "Dbg"):
+ oldPrompt = sys.ps1
+ if globals is None:
+ # Reset
+ sys.ps1 = ">>> "
+ sys.ps2 = "... "
+ locals = globals = __main__.__dict__
+ else:
+ sys.ps1 = "[%s]>>> " % name
+ sys.ps2 = "[%s]... " % name
+ self.interp.locals = locals
+ self.interp.globals = globals
+ self.AppendToPrompt([], oldPrompt)
+
+ def DoGetLine(self, line=-1):
+ if line==-1: line = self.LineFromChar()
+ line = self.GetLine(line)
+ while line and line[-1] in ['\r', '\n']:
+ line = line[:-1]
+ return line
+ def AppendToPrompt(self,bufLines, oldPrompt = None):
+ " Take a command and stick it at the end of the buffer (with python prompts inserted if required)."
+ self.flush()
+ lastLineNo = self.GetLineCount()-1
+ line = self.DoGetLine(lastLineNo)
+ if oldPrompt and line==oldPrompt:
+ self.SetSel(self.GetTextLength()-len(oldPrompt), self.GetTextLength())
+ self.ReplaceSel(sys.ps1)
+ elif (line!=str(sys.ps1)):
+ if len(line)!=0: self.write('\n')
+ self.write(sys.ps1)
+ self.flush()
+ self.idle.text.mark_set("iomark", "end-1c")
+# print "set to" + self.text.index("iomark")
+ if not bufLines:
+ return
+ terms = (["\n" + sys.ps2] * (len(bufLines)-1)) + ['']
+ for bufLine, term in map(None, bufLines, terms):
+ if string.strip(bufLine):
+ self.write( bufLine + term )
+ self.flush()
+
+ def _GetSubConfigNames(self):
+ return ["interactive"] # Allow [Keys:Interactive] sections to be specific
+
+ def HookHandlers(self):
+ # Hook menu command (executed when a menu item with that ID is selected from a menu/toolbar
+ self.HookCommand(self.OnSelectBlock, win32ui.ID_EDIT_SELECT_BLOCK)
+ mod = pywin.scintilla.IDLEenvironment.GetIDLEModule("IdleHistory")
+ self.history = mod.History(self.idle.text, "\n" + sys.ps2)
+ # hack for now for event handling.
+
+ # GetBlockBoundary takes a line number, and will return the
+ # start and and line numbers of the block, and a flag indicating if the
+ # block is a Python code block.
+ # If the line specified has a Python prompt, then the lines are parsed
+ # backwards and forwards, and the flag is true.
+ # If the line does not start with a prompt, the block is searched forward
+ # and backward until a prompt _is_ found, and all lines in between without
+ # prompts are returned, and the flag is false.
+ def GetBlockBoundary( self, lineNo ):
+ line = self.DoGetLine(lineNo)
+ maxLineNo = self.GetLineCount()-1
+ prefix = GetPromptPrefix(line)
+ if prefix is None: # Non code block
+ flag = 0
+ startLineNo = lineNo
+ while startLineNo>0:
+ if GetPromptPrefix(self.DoGetLine(startLineNo-1)) is not None:
+ break # there _is_ a prompt
+ startLineNo = startLineNo-1
+ endLineNo = lineNo
+ while endLineNo0 and prefix!=str(sys.ps1):
+ prefix = GetPromptPrefix(self.DoGetLine(startLineNo-1))
+ if prefix is None:
+ break; # there is no prompt.
+ startLineNo = startLineNo - 1
+ endLineNo = lineNo
+ while endLineNo= start:
+ thisLine = self.DoGetLine(end)
+ promptLen = len(GetPromptPrefix(thisLine))
+ retList = [thisLine[promptLen:]] + retList
+ end = end-1
+ return retList
+
+ def OutputGrab(self):
+# import win32traceutil; return
+ self.oldStdOut = sys.stdout
+ self.oldStdErr = sys.stderr
+ sys.stdout=self
+ sys.stderr=self
+ self.flush()
+
+ def OutputRelease(self):
+ # a command may have overwritten these - only restore if not.
+ if self.oldStdOut is not None:
+ if sys.stdout == self:
+ sys.stdout=self.oldStdOut
+ if self.oldStdErr is not None:
+ if sys.stderr == self:
+ sys.stderr=self.oldStdErr
+ self.oldStdOut = None
+ self.oldStdErr = None
+ self.flush()
+
+ ###################################
+ #
+ # Message/Command/Key Hooks.
+ #
+ # Enter key handler
+ #
+ def ProcessEnterEvent(self, event ):
+ return self._ProcessEnterEvent(event)
+
+ def ProcessEnterEvent(self, event ):
+ self.SCICancel()
+ # First, check for an error message
+ haveGrabbedOutput = 0
+ if self.HandleSpecialLine(): return 0
+
+ lineNo = self.LineFromChar()
+ start, end, isCode = self.GetBlockBoundary(lineNo)
+ # If we are not in a code block just go to the prompt (or create a new one)
+ if not isCode:
+ self.AppendToPrompt([])
+# lastLine = self.DoGetLine(self.GetLineCount()-1)
+# if string.find(lastLine, sys.ps1)!=0 and string.find(lastLine, sys.ps2)!=0:
+# self.write("\n%s" % sys.ps1)
+# self.SetSel(-1)
+ win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
+ return
+
+ lines = self.ExtractCommand((start,end))
+
+ # If we are in a code-block, but it isnt at the end of the buffer
+ # then copy it to the end ready for editing and subsequent execution
+ if end!=self.GetLineCount()-1:
+ win32ui.SetStatusText('Press ENTER to execute command')
+ self.AppendToPrompt(lines)
+ self.SetSel(-2)
+ return
+
+ # If SHIFT held down, we want new code here and now!
+ bNeedIndent = win32api.GetKeyState(win32con.VK_SHIFT)<0 or win32api.GetKeyState(win32con.VK_CONTROL)<0
+ if bNeedIndent:
+ self.ReplaceSel("\n")
+ else:
+ self.SetSel(-2)
+ self.ReplaceSel("\n")
+ source = string.join(lines, '\n')
+ while source and source[-1] in '\t ':
+ source = source[:-1]
+ self.OutputGrab() # grab the output for the command exec.
+ try:
+ if self.interp.runsource(source, ""): # Need more input!
+ bNeedIndent = 1
+ else:
+ # If the last line isnt empty, append a newline
+# self.flush()
+# lastLine = self.DoGetLine(self.GetLineCount()-1)
+# if lastLine: self.write("\n")
+# self.write(sys.ps1)
+ self.history.history_store(source)
+ self.AppendToPrompt([])
+ win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
+# win32ui.SetStatusText('Successfully executed statement')
+ finally:
+ self.OutputRelease()
+ if bNeedIndent:
+ win32ui.SetStatusText('Ready to continue the command')
+ # Now attempt correct indentation (should use IDLE?)
+ curLine = self.DoGetLine(lineNo)[len(sys.ps2):]
+ pos = 0
+ indent=''
+ while len(curLine)>pos and curLine[pos] in string.whitespace:
+ indent = indent + curLine[pos]
+ pos = pos + 1
+ if _is_block_opener(curLine):
+ indent = indent + '\t'
+ elif _is_block_closer(curLine):
+ indent = indent[:-1]
+ # use ReplaceSel to ensure it goes at the cursor rather than end of buffer.
+ self.ReplaceSel(sys.ps2+indent)
+ return 0
+
+ # ESC key handler
+ def ProcessEscEvent(self, event):
+ # Implement a cancel.
+ if self.SCIAutoCActive() or self.SCICallTipActive():
+ self.SCICancel()
+ else:
+ win32ui.SetStatusText('Cancelled.')
+ self.AppendToPrompt(('',))
+ return 0
+
+ def OnSelectBlock(self,command, code):
+ lineNo = self.LineFromChar()
+ start, end, isCode = self.GetBlockBoundary(lineNo)
+ startIndex = self.LineIndex(start)
+ endIndex = self.LineIndex(end+1)-2 # skip \r + \n
+ if endIndex<0: # must be beyond end of buffer
+ endIndex = -2 # self.Length()
+ self.SetSel(startIndex,endIndex)
+
+ def GetRightMenuItems(self):
+ # Just override parents
+ ret = []
+ flags = 0
+ ret.append(flags, win32ui.ID_EDIT_UNDO, '&Undo')
+ ret.append(win32con.MF_SEPARATOR);
+ ret.append(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
+ ret.append(flags, win32ui.ID_EDIT_COPY, '&Copy')
+ ret.append(flags, win32ui.ID_EDIT_PASTE, '&Paste')
+ ret.append(win32con.MF_SEPARATOR)
+ ret.append(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
+ ret.append(flags, win32ui.ID_EDIT_SELECT_BLOCK, 'Select &block')
+ ret.append(flags, win32ui.ID_VIEW_WHITESPACE, "View &Whitespace")
+ return ret
+
+ def MDINextEvent(self, event):
+ win32ui.GetMainFrame().MDINext(0)
+ def MDIPrevEvent(self, event):
+ win32ui.GetMainFrame().MDINext(0)
+
+ def WindowBackEvent(self, event):
+ parent = self.GetParentFrame()
+ if parent == win32ui.GetMainFrame():
+ # It it docked.
+ wnd, isactive = parent.MDIGetActive()
+ wnd.SetFocus()
+ else:
+ # Normal Window
+ try:
+ lastActive = self.GetParentFrame().lastActive
+ # If the window is invalid, reset it.
+ if lastActive is not None and (lastActive._obj_ is None or lastActive.GetSafeHwnd()==0):
+ lastActive = self.GetParentFrame().lastActive = None
+ win32ui.SetStatusText("The last active Window has been closed.")
+ except AttributeError:
+ print "Can't find the last active window!"
+ lastActive = None
+ if lastActive is not None:
+ lastActive.MDIActivate()
+
+class InteractiveView(InteractiveCore, winout.WindowOutputView):
+ def __init__(self, doc):
+ InteractiveCore.__init__(self)
+ winout.WindowOutputView.__init__(self, doc)
+ def _MakeColorizer(self):
+ return InteractiveFormatter(self)
+ def OnInitialUpdate(self):
+ winout.WindowOutputView.OnInitialUpdate(self)
+ self.Init()
+# InteractiveCore.OnInitialUpdate(self)
+ def HookHandlers(self):
+ winout.WindowOutputView.HookHandlers(self)
+ InteractiveCore.HookHandlers(self)
+# self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE)
+## def FillKeyMap(self): # Scintilla only
+## winout.WindowOutputView.FillKeyMap(self)
+## InteractiveCore.FillKeyMap(self)
+
+class CInteractivePython(winout.WindowOutput):
+ def __init__(self, makeDoc = None, makeFrame = None):
+ winout.WindowOutput.__init__(self, sectionProfile, sectionProfile, \
+ winout.flags.WQ_NONE, 1, None, makeDoc, makeFrame, InteractiveView )
+ self.Create()
+ self.IsFinalDestroy = 0
+
+ def OnViewDestroy(self, view):
+ if self.IsFinalDestroy:
+ view.OutputRelease()
+ winout.WindowOutput.OnViewDestroy(self, view)
+
+ def Close(self):
+ self.IsFinalDestroy = 1
+ winout.WindowOutput.Close(self)
+
+class InteractiveFrame(winout.WindowOutputFrame):
+ def __init__(self):
+ self.lastActive = None
+ winout.WindowOutputFrame.__init__(self)
+
+ def OnMDIActivate(self, bActive, wndActive, wndDeactive):
+ if bActive:
+ self.lastActive = wndDeactive
+
+######################################################################
+#
+# Dockable Window Support
+#
+######################################################################
+ID_DOCKED_INTERACTIVE_CONTROLBAR = 0xe802
+
+DockedInteractiveViewParent = InteractiveView
+class DockedInteractiveView(DockedInteractiveViewParent):
+ def HookHandlers(self):
+ DockedInteractiveViewParent.HookHandlers(self)
+ self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
+ self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
+ def OnSetFocus(self, msg):
+ self.GetParentFrame().SetActiveView(self)
+ return 1
+ def OnKillFocus(self, msg):
+ # If we are losing focus to another in this app, reset the main frame's active view.
+ hwnd = wparam = msg[2]
+ try:
+ wnd = win32ui.CreateWindowFromHandle(hwnd)
+ reset = wnd.GetTopLevelFrame()==self.GetTopLevelFrame()
+ except win32ui.error:
+ reset = 0 # Not my window
+ if reset: self.GetParentFrame().SetActiveView(None)
+ return 1
+ def OnDestroy(self, msg):
+ newSize = self.GetWindowPlacement()[4]
+ pywin.framework.app.SaveWindowSize("Interactive Window", newSize, "docked")
+ return DockedInteractiveViewParent.OnDestroy(self, msg)
+
+class CDockedInteractivePython(CInteractivePython):
+ def __init__(self, dockbar):
+ self.bFirstCreated = 0
+ self.dockbar = dockbar
+ CInteractivePython.__init__(self)
+ def NeedRecreateWindow(self):
+ if self.bCreating: return 0
+ try:
+ cb = win32ui.GetMainFrame().GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
+ return not cb.IsWindowVisible()
+ except win32ui.error:
+ return 0 # We are dieing!
+ def RecreateWindow(self):
+ dockbar = win32ui.GetMainFrame().GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
+ win32ui.GetMainFrame().ShowControlBar(dockbar, 1, 1)
+
+ def Create(self):
+ self.bCreating = 1
+ doc = InteractiveDocument(None, self.DoCreateDoc())
+ view = DockedInteractiveView(doc)
+ defRect = pywin.framework.app.LoadWindowSize("Interactive Window", "docked")
+ if defRect[2]-defRect[0]==0:
+ defRect = 0, 0, 500, 200
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER
+ id = 1050 # win32ui.AFX_IDW_PANE_FIRST
+ view.CreateWindow(self.dockbar, id, style, defRect)
+ view.OnInitialUpdate()
+ self.bFirstCreated = 1
+
+ self.currentView = doc.GetFirstView()
+ self.bCreating = 0
+ if self.title: doc.SetTitle(self.title)
+
+# The factory we pass to the dockable window support.
+def InteractiveViewCreator(parent):
+ global edit
+ edit = CDockedInteractivePython(parent)
+ return edit.currentView
+
+def CreateDockedInteractiveWindow():
+ # Later, the DockingBar should be capable of hosting multiple
+ # children.
+ from pywin.docking.DockingBar import DockingBar
+ bar = DockingBar()
+ creator = InteractiveViewCreator
+ bar.CreateWindow(win32ui.GetMainFrame(), creator, "Interactive Window", ID_DOCKED_INTERACTIVE_CONTROLBAR)
+ bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
+ bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
+
+######################################################################
+#
+# The public interface to this module.
+#
+######################################################################
+# No extra functionality now, but maybe later, so
+# publicize these names.
+InteractiveDocument = winout.WindowOutputDocument
+
+# We remember our one and only interactive window in the "edit" variable.
+edit = None
+
+def CreateInteractiveWindowUserPreference(makeDoc = None, makeFrame = None):
+ """Create some sort of interactive window if the user's preference say we should.
+ """
+ bCreate = LoadPreference("Show at startup", 1)
+ if bCreate:
+ CreateInteractiveWindow(makeDoc, makeFrame)
+
+def CreateInteractiveWindow(makeDoc = None, makeFrame = None):
+ """Create a standard or docked interactive window unconditionally
+ """
+ assert edit is None, "Creating second interactive window!"
+ bDocking = LoadPreference("Docking", 0)
+ if bDocking:
+ CreateDockedInteractiveWindow()
+ else:
+ CreateMDIInteractiveWindow(makeDoc, makeFrame)
+ assert edit is not None, "Created interactive window, but did not set the global!"
+ edit.currentView.SetFocus()
+
+def CreateMDIInteractiveWindow(makeDoc = None, makeFrame = None):
+ """Create a standard (non-docked) interactive window unconditionally
+ """
+ global edit
+ if makeDoc is None: makeDoc = InteractiveDocument
+ if makeFrame is None: makeFrame = InteractiveFrame
+ edit = CInteractivePython(makeDoc=makeDoc,makeFrame=makeFrame)
+
+def DestroyInteractiveWindow():
+ """ Destroy the interactive window.
+ This is different to Closing the window,
+ which may automatically re-appear. Once destroyed, it can never be recreated,
+ and a complete new instance must be created (which the various other helper
+ functions will then do after making this call
+ """
+ global edit
+ if edit is not None and edit.currentView is not None:
+ if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
+ # It is docked - do nothing now (this is only called at shutdown!)
+ pass
+ else:
+ # It is a standard window - call Close on the container.
+ edit.Close()
+ edit = None
+
+def CloseInteractiveWindow():
+ """Close the interactive window, allowing it to be re-created on demand.
+ """
+ global edit
+ if edit is not None and edit.currentView is not None:
+ if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
+ # It is docked, just hide the dock bar.
+ frame = win32ui.GetMainFrame()
+ cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
+ frame.ShowControlBar(cb, 0, 1)
+ else:
+ # It is a standard window - destroy the frame/view, allowing the object itself to remain.
+ edit.currentView.GetParentFrame().DestroyWindow()
+
+def ToggleInteractiveWindow():
+ """If the interactive window is visible, hide it, otherwise show it.
+ """
+ if edit is None:
+ CreateInteractiveWindow()
+ else:
+ if edit.NeedRecreateWindow():
+ edit.RecreateWindow()
+ else:
+ # Close it, allowing a reopen.
+ CloseInteractiveWindow()
+
+def ShowInteractiveWindow():
+ """Shows (or creates if necessary) an interactive window"""
+ if edit is None:
+ CreateInteractiveWindow()
+ else:
+ if edit.NeedRecreateWindow():
+ edit.RecreateWindow()
+ else:
+ parent = edit.currentView.GetParentFrame()
+ if parent == win32ui.GetMainFrame(): # It is docked.
+ edit.currentView.SetFocus()
+ else: # It is a "normal" window
+ edit.currentView.GetParentFrame().AutoRestore()
+ win32ui.GetMainFrame().MDIActivate(edit.currentView.GetParentFrame())
+
+def IsInteractiveWindowVisible():
+ return edit is not None and not edit.NeedRecreateWindow()
diff --git a/Pythonwin/pywin/framework/intpyapp.py b/Pythonwin/pywin/framework/intpyapp.py
new file mode 100644
index 0000000000..fb20ffab31
--- /dev/null
+++ b/Pythonwin/pywin/framework/intpyapp.py
@@ -0,0 +1,421 @@
+# intpyapp.py - Interactive Python application class
+#
+import win32con
+import win32api
+import win32ui
+import __main__
+import sys
+import string
+import app
+import traceback
+from pywin.mfc import window, afxres, dialog
+import commctrl
+import dbgcommands
+
+lastLocateFileName = ".py" # used in the "File/Locate" dialog...
+
+class MainFrame(app.MainFrame):
+ def OnCreate(self, createStruct):
+ if app.MainFrame.OnCreate(self, createStruct)==-1:
+ return -1
+ style = win32con.WS_CHILD | afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
+
+ self.EnableDocking(afxres.CBRS_ALIGN_ANY)
+
+ tb = win32ui.CreateToolBar (self, style | win32con.WS_VISIBLE)
+ tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
+ tb.LoadToolBar(win32ui.IDR_MAINFRAME)
+ tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ tb.SetWindowText("Standard")
+
+ tbd = win32ui.CreateToolBar (self, style, win32ui.ID_VIEW_TOOLBAR_DBG)
+ tbd.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
+ tbd.LoadToolBar(win32ui.IDR_DEBUGGER)
+ tbd.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ tbd.SetWindowText("Debugger")
+
+ self.DockControlBar(tb)
+ self.DockControlBar(tbd)
+ # And a "Tools" menu on the main frame.
+ menu = self.GetMenu()
+ import toolmenu
+ toolmenu.SetToolsMenu(menu, 2)
+ # And fix the "Help" menu on the main frame
+ from pywin.framework import help
+ help.SetHelpMenuOtherHelp(menu)
+
+ def OnClose(self):
+ try:
+ import pywin.debugger
+ if pywin.debugger.currentDebugger is not None and pywin.debugger.currentDebugger.pumping:
+ try:
+ pywin.debugger.currentDebugger.close(1)
+ except:
+ import traceback
+ traceback.print_exc()
+ return
+ except win32ui.error:
+ pass
+ self.SaveBarState("ToolbarDefault")
+ self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save.
+ return self._obj_.OnClose()
+ def OnCommand(self, wparam, lparam):
+ # By default, the current MDI child frame will process WM_COMMAND
+ # messages before any docked control bars - even if the control bar
+ # has focus. This is a problem for the interactive window when docked.
+ # Therefore, we detect the situation of a view having the main frame
+ # as its parent, and assume it must be a docked view (which it will in an MDI app)
+ try:
+ v = self.GetActiveView() # Raise an exception if none - good - then we want default handling
+ # Main frame _does_ have a current view (ie, a docking view) - see if it wants it.
+ if v.OnCommand(wparam, lparam):
+ return 1
+ except (win32ui.error, AttributeError):
+ pass
+ return self._obj_.OnCommand(wparam, lparam)
+
+class InteractivePythonApp(app.CApp):
+ # This works if necessary - just we dont need to override the Run method.
+# def Run(self):
+# return self._obj_.Run()
+
+ def HookCommands(self):
+ app.CApp.HookCommands(self)
+ dbgcommands.DebuggerCommandHandler().HookCommands()
+ self.HookCommand(self.OnViewBrowse,win32ui.ID_VIEW_BROWSE)
+ self.HookCommand(self.OnFileImport,win32ui.ID_FILE_IMPORT)
+ self.HookCommand(self.OnFileCheck,win32ui.ID_FILE_CHECK)
+ self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK)
+ self.HookCommand(self.OnFileRun,win32ui.ID_FILE_RUN)
+ self.HookCommand(self.OnFileLocate,win32ui.ID_FILE_LOCATE)
+ self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
+ self.HookCommandUpdate(self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
+ self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS)
+ self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX)
+ self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL)
+ self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
+ self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
+
+ def CreateMainFrame(self):
+ return MainFrame()
+
+ def MakeExistingDDEConnection(self):
+ # Use DDE to connect to an existing instance
+ # Return None if no existing instance
+ try:
+ import intpydde
+ except ImportError:
+ # No dde support!
+ return None
+ conv = intpydde.CreateConversation(self.ddeServer)
+ try:
+ conv.ConnectTo("Pythonwin", "System")
+ return conv
+ except intpydde.error:
+ return None
+
+ def InitDDE(self):
+ # Do all the magic DDE handling.
+ # Returns TRUE if we have pumped the arguments to our
+ # remote DDE app, and we should terminate.
+ try:
+ import intpydde
+ except ImportError:
+ self.ddeServer = None
+ intpydde = None
+ if intpydde is not None:
+ self.ddeServer = intpydde.DDEServer(self)
+ self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS )
+ try:
+ # If there is an existing instance, pump the arguments to it.
+ connection = self.MakeExistingDDEConnection()
+ if connection is not None:
+ if self.ProcessArgs(sys.argv, connection) is None:
+ return 1
+ except:
+ win32ui.MessageBox("There was an error in the DDE conversation with Pythonwin")
+ traceback.print_exc()
+
+ def InitInstance(self):
+ # Allow "/nodde" to optimize this!
+ if "/nodde" not in sys.argv:
+ if self.InitDDE():
+ return 1 # A remote DDE client is doing it for us!
+ else:
+ self.ddeServer = None
+
+ win32ui.SetRegistryKey('Python') # MFC automatically puts the main frame caption on!
+ app.CApp.InitInstance(self)
+
+ # Create the taskbar icon
+ win32ui.CreateDebuggerThread()
+
+ # Allow Pythonwin to host OCX controls.
+ win32ui.EnableControlContainer()
+
+ # Display the interactive window if the user wants it.
+ import interact
+ interact.CreateInteractiveWindowUserPreference()
+
+ # Load the modules we use internally.
+ self.LoadSystemModules()
+
+ # Load additional module the user may want.
+ self.LoadUserModules()
+
+ # Load the ToolBar state near the end of the init process, as
+ # there may be Toolbar IDs created by the user or other modules.
+ # By now all these modules should be loaded, so all the toolbar IDs loaded.
+ try:
+ self.frame.LoadBarState("ToolbarDefault")
+ except win32ui.error:
+ # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the
+ # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this
+ # but I need to investigate more how to prevent it (AFAIK, ensuring all the
+ # toolbars are created by now _should_ stop it!)
+ pass
+
+ # Finally process the command line arguments.
+ self.ProcessArgs(sys.argv)
+
+ def ExitInstance(self):
+ win32ui.DestroyDebuggerThread()
+ try:
+ import interact
+ interact.DestroyInteractiveWindow()
+ except:
+ pass
+ if self.ddeServer is not None:
+ self.ddeServer.Shutdown()
+ self.ddeServer = None
+ return app.CApp.ExitInstance(self)
+
+ def Activate(self):
+ # Bring to the foreground. Mainly used when another app starts up, it asks
+ # this one to activate itself, then it terminates.
+ frame = win32ui.GetMainFrame()
+ frame.SetForegroundWindow()
+ if frame.GetWindowPlacement()[1]==win32con.SW_SHOWMINIMIZED:
+ frame.ShowWindow(win32con.SW_RESTORE)
+
+ def ProcessArgs(self, args, dde = None):
+ # If we are going to talk to a remote app via DDE, then
+ # activate it!
+ if dde is not None: dde.Exec("self.Activate()")
+ if len(args) and args[0]=='/nodde': del args[0] # already handled.
+ if len(args)<1 or not args[0]: # argv[0]=='' when started without args, just like Python.exe!
+ return
+ try:
+ if args[0] and args[0][0]!='/':
+ argStart = 0
+ argType = string.lower(win32ui.GetProfileVal("Python","Default Arg Type","/edit"))
+ else:
+ argStart = 1
+ argType = args[0]
+ if argStart >= len(args):
+ raise TypeError, "The command line requires an additional arg."
+ if argType=="/edit":
+ # Load up the default application.
+ if dde:
+ fname = win32api.GetFullPathName(args[argStart])
+ dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (`fname`))
+ else:
+ win32ui.GetApp().OpenDocumentFile(args[argStart])
+ elif argType=="/rundlg":
+ if dde:
+ dde.Exec("import scriptutils;scriptutils.RunScript('%s', '%s', 1)" % (args[argStart], string.join(args[argStart+1:])))
+ else:
+ import scriptutils
+ scriptutils.RunScript(args[argStart], string.join(args[argStart+1:]))
+ elif argType=="/run":
+ if dde:
+ dde.Exec("import scriptutils;scriptutils.RunScript('%s', '%s', 0)" % (args[argStart], string.join(args[argStart+1:])))
+ else:
+ import scriptutils
+ scriptutils.RunScript(args[argStart], string.join(args[argStart+1:]), 0)
+ elif argType=="/app":
+ raise RuntimeError, "/app only supported for new instances of Pythonwin.exe"
+ elif argType=='/new': # Allow a new instance of Pythonwin
+ return 1
+ elif argType=='/dde': # Send arbitary command
+ if dde is not None:
+ dde.Exec(args[argStart])
+ else:
+ win32ui.MessageBox("The /dde command can only be used\r\nwhen Pythonwin is already running")
+ else:
+ raise TypeError, "Command line arguments not recognised"
+ except:
+ typ, val, tb = sys.exc_info()
+ print "There was an error processing the command line args"
+ traceback.print_exception(typ, val, tb, None, sys.stdout)
+ win32ui.OutputDebug("There was a problem with the command line args - %s: %s" % (`typ`,`val`))
+
+
+ def LoadSystemModules(self):
+ self.DoLoadModules("editor")
+
+ def LoadUserModules(self, moduleNames = None):
+ # Load the users modules.
+ if moduleNames is None:
+ default = "sgrepmdi"
+ moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default)
+ self.DoLoadModules(moduleNames)
+
+ def DoLoadModules(self, moduleNames): # ", sep string of module names.
+ if not moduleNames: return
+ modules = string.splitfields(moduleNames,",")
+ for module in modules:
+ try:
+ exec "import "+module
+ except: # Catch em all, else the app itself dies! 'ImportError:
+ traceback.print_exc()
+ msg = 'Startup import of user module "%s" failed' % module
+ print msg
+ win32ui.MessageBox(msg)
+
+ #
+ # DDE Callback
+ #
+ def OnDDECommand(self, command):
+# print "DDE Executing", `command`
+ try:
+ exec command + "\n"
+ except:
+ print "ERROR executing DDE command: ", command
+ traceback.print_exc()
+ raise
+
+ #
+ # General handlers
+ #
+ def OnViewBrowse( self, id, code ):
+ " Called when ViewBrowse message is received "
+ from pywin.mfc import dialog
+ from pywin.tools import browser
+ obName = dialog.GetSimpleInput('Object', '__builtins__', 'Browse Python Object')
+ if obName is None:
+ return
+ try:
+ browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__))
+ except NameError:
+ win32ui.MessageBox('This is no object with this name')
+ except AttributeError:
+ win32ui.MessageBox('The object has no attribute of that name')
+ except:
+ win32ui.MessageBox('This object can not be browsed')
+
+ def OnFileImport( self, id, code ):
+ " Called when a FileImport message is received. Import the current or specified file"
+ import scriptutils
+ scriptutils.ImportFile()
+
+ def OnFileCheck( self, id, code ):
+ " Called when a FileCheck message is received. Check the current file."
+ import scriptutils
+ scriptutils.CheckFile()
+
+ def OnUpdateFileCheck(self, cmdui):
+ import scriptutils
+ cmdui.Enable( scriptutils.GetActiveFileName(0) is not None )
+
+ def OnFileRun( self, id, code ):
+ " Called when a FileRun message is received. "
+ import scriptutils
+ showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0
+ scriptutils.RunScript(None, None, showDlg)
+
+ def OnFileLocate( self, id, code ):
+ from pywin.mfc import dialog
+ import scriptutils
+ import os
+ global lastLocateFileName # save the new version away for next time...
+
+ # Loop until a good name, or cancel
+ while 1:
+ name = dialog.GetSimpleInput('File name', lastLocateFileName, 'Locate Python File')
+ if name is None: # Cancelled.
+ break
+ lastLocateFileName = name
+ # if ".py" supplied, rip it off!
+ if string.lower(lastLocateFileName[-3:])=='.py':
+ lastLocateFileName = lastLocateFileName[:-3]
+ lastLocateFileName = string.translate(lastLocateFileName, string.maketrans(".","\\"))
+ newName = scriptutils.LocatePythonFile(lastLocateFileName)
+ if newName is None:
+ win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName)
+ else:
+ win32ui.GetApp().OpenDocumentFile(newName)
+ break
+
+ # Display all the "options" proprety pages we can find
+ def OnViewOptions(self, id, code):
+ win32ui.InitRichEdit()
+ sheet = dialog.PropertySheet("Pythonwin Options")
+ # Add property pages we know about that need manual work.
+ from pywin.dialogs import ideoptions
+ sheet.AddPage( ideoptions.OptionsPropPage() )
+
+ import toolmenu
+ sheet.AddPage( toolmenu.ToolMenuPropPage() )
+
+ # Get other dynamic pages from templates.
+ pages = []
+ for template in self.GetDocTemplateList():
+ try:
+ # Dont actually call the function with the exception handler.
+ getter = template.GetPythonPropertyPages
+ except AttributeError:
+ # Template does not provide property pages!
+ continue
+ pages = pages + getter()
+
+ # Debugger template goes at the end
+ try:
+ from pywin.debugger import configui
+ except ImportError:
+ configui = None
+ if configui is not None: pages.append(configui.DebuggerOptionsPropPage())
+ # Now simply add the pages, and display the dialog.
+ for page in pages:
+ sheet.AddPage(page)
+
+ sheet.DoModal()
+
+ def OnInteractiveWindow(self, id, code):
+ # toggle the existing state.
+ import interact
+ interact.ToggleInteractiveWindow()
+
+ def OnUpdateInteractiveWindow(self, cmdui):
+ try:
+ interact=sys.modules['pywin.framework.interact']
+ state = interact.IsInteractiveWindowVisible()
+ except KeyError: # Interactive module hasnt ever been imported.
+ state = 0
+ cmdui.Enable()
+ cmdui.SetCheck(state)
+
+ def OnFileSaveAll(self, id, code):
+ # Only attempt to save editor documents.
+ from pywin.framework.editor import editorTemplate
+ docs = filter(lambda doc: doc.IsModified() and doc.GetPathName(), editorTemplate.GetDocumentList())
+ map(lambda doc: doc.OnSaveDocument(doc.GetPathName()), docs)
+ win32ui.SetStatusText("%d documents saved" % len(docs), 1)
+
+ def OnViewToolbarDbg(self, id, code):
+ if code==0:
+ return not win32ui.GetMainFrame().OnBarCheck(id)
+
+ def OnUpdateViewToolbarDbg(self, cmdui):
+ win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
+ cmdui.Enable(1)
+
+ def OnHelpIndex( self, id, code ):
+ import help
+ help.SelectAndRunHelpFile()
+
+# As per the comments in app.py, this use is depreciated.
+# app.AppBuilder = InteractivePythonApp
+
+# Now all we do is create the application
+thisApp = InteractivePythonApp()
diff --git a/Pythonwin/pywin/framework/intpydde.py b/Pythonwin/pywin/framework/intpydde.py
new file mode 100644
index 0000000000..2ca6f75354
--- /dev/null
+++ b/Pythonwin/pywin/framework/intpydde.py
@@ -0,0 +1,56 @@
+# DDE support for Pythonwin
+#
+# Seems to work fine (in the context that IE4 seems to have broken
+# DDE on _all_ NT4 machines I have tried, but only when a "Command Prompt" window
+# is open. Strange, but true. If you have problems with this, close all Command Prompts!
+
+
+import win32ui
+import win32api, win32con
+from pywin.mfc import object
+from dde import *
+import traceback
+import string
+
+class DDESystemTopic(object.Object):
+ def __init__(self, app):
+ self.app = app
+ object.Object.__init__(self, CreateServerSystemTopic())
+ def Exec(self, data):
+ try:
+# print "Executing", cmd
+ self.app.OnDDECommand(data)
+ except:
+ # The DDE Execution failed.
+ print "Error executing DDE command."
+ traceback.print_exc()
+ return 0
+
+class DDEServer(object.Object):
+ def __init__(self, app):
+ self.app = app
+ object.Object.__init__(self, CreateServer())
+ self.topic = self.item = None
+
+ def CreateSystemTopic(self):
+ return DDESystemTopic(self.app)
+
+ def Shutdown(self):
+ self._obj_.Shutdown()
+ self._obj_.Destroy()
+ if self.topic is not None:
+ self.topic.Destroy()
+ self.topic = None
+ if self.item is not None:
+ self.item.Destroy()
+ self.item = None
+
+ def OnCreate(self):
+ return 1
+
+ def Status(self, msg):
+ try:
+ win32ui.SetStatusText(msg)
+ except win32ui.error:
+ print msg
+
diff --git a/Pythonwin/pywin/framework/scriptutils.py b/Pythonwin/pywin/framework/scriptutils.py
new file mode 100644
index 0000000000..be8b348277
--- /dev/null
+++ b/Pythonwin/pywin/framework/scriptutils.py
@@ -0,0 +1,578 @@
+"""
+Various utilities for running/importing a script
+"""
+import app
+import sys
+import win32ui
+import win32api
+import win32con
+import __main__
+from pywin.mfc import dialog
+import os
+import string
+import traceback
+import linecache
+
+from cmdline import ParseArgs
+
+RS_DEBUGGER_NONE=0 # Dont run under the debugger.
+RS_DEBUGGER_STEP=1 # Start stepping under the debugger
+RS_DEBUGGER_GO=2 # Just run under the debugger, stopping only at break-points.
+RS_DEBUGGER_PM=3 # Dont run under debugger, but do post-mortem analysis on exception.
+
+debugging_options = string.split("""No debugging
+Step-through in the debugger
+Run in the debugger
+Post-Mortem of unhandled exceptions""", "\n")
+
+# A dialog box for the "Run Script" command.
+class DlgRunScript(dialog.Dialog):
+ "A class for the 'run script' dialog"
+ def __init__(self, bHaveDebugger):
+ dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT )
+ self.AddDDX(win32ui.IDC_EDIT1, "script")
+ self.AddDDX(win32ui.IDC_EDIT2, "args")
+ self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i")
+ self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2)
+ self.bHaveDebugger = bHaveDebugger
+ def OnInitDialog(self):
+ rc = dialog.Dialog.OnInitDialog(self)
+ cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
+ for o in debugging_options:
+ cbo.AddString(o)
+ cbo.SetCurSel(self['debuggingType'])
+ if not self.bHaveDebugger:
+ cbo.EnableWindow(0)
+
+ def OnBrowse(self, id, cmd):
+ openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
+ dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py)|*.py||", self)
+ dlg.SetOFNTitle("Run Script")
+ if dlg.DoModal()!=win32con.IDOK:
+ return 0
+ self['script'] = dlg.GetPathName()
+ self.UpdateData(0)
+ return 0
+
+def GetDebugger():
+ """Get the default Python debugger. Returns the debugger, or None.
+
+ It is assumed the debugger has a standard "pdb" defined interface.
+ Currently always returns the 'pywin.debugger' debugger, or None
+ (pdb is _not_ returned as it is not effective in this GUI environment)
+ """
+ try:
+ import pywin.debugger
+ return pywin.debugger
+ except ImportError:
+ return None
+
+def IsOnPythonPath(path):
+ "Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec."
+ # must check that the command line arg's path is in sys.path
+ for syspath in sys.path:
+ try:
+ # Python 1.5 and later allows an empty sys.path entry.
+ if syspath and win32ui.FullPath(syspath)==path:
+ return 1
+ except win32ui.error, details:
+ print "Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details)
+ return 0
+
+def GetPackageModuleName(fileName):
+ """Given a filename, return (module name, new path).
+ eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path.
+ If no package found, will return ("my", "c:\a\b\c")
+ """
+ path, fname = os.path.split(fileName)
+ path=origPath=win32ui.FullPath(path)
+ fname = os.path.splitext(fname)[0]
+ modBits = []
+ newPathReturn = None
+ if not IsOnPythonPath(path):
+ # Module not directly on the search path - see if under a package.
+ while len(path)>3: # ie 'C:\'
+ path, modBit = os.path.split(path)
+ modBits.append(modBit)
+ # If on path, _and_ existing package of that name loaded.
+ if IsOnPythonPath(path) and sys.modules.has_key(modBit) and \
+ ( os.path.exists(os.path.join(path, '__init__.py')) or \
+ os.path.exists(os.path.join(path, '__init__.pyc')) or \
+ os.path.exists(os.path.join(path, '__init__.pyo')) \
+ ):
+ modBits.reverse()
+ return string.join(modBits, ".") + "." + fname, newPathReturn
+ # Not found - look a level higher
+ else:
+ newPathReturn = origPath
+
+ return fname, newPathReturn
+
+def GetActiveView():
+ """Gets the edit control (eg, EditView) with the focus, or None
+ """
+ try:
+ childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive()
+ return childFrame.GetActiveView()
+ except win32ui.error:
+ return None
+
+def GetActiveEditControl():
+ view = GetActiveView()
+ if view is None: return None
+ if hasattr(view, "SCIAddText"): # Is it a scintilla control?
+ return view
+ try:
+ return view.GetRichEditCtrl()
+ except AttributeError:
+ pass
+ try:
+ return view.GetEditCtrl()
+ except AttributeError:
+ pass
+
+def GetActiveEditorDocument():
+ """Returns the active editor document, or None if no
+ active document or its not an editor document.
+ """
+ view = GetActiveView()
+ if view is None: return None
+ doc = view.GetDocument()
+ if hasattr(doc, "MarkerAdd"): # Is it an Editor document?
+ return doc
+ return None
+
+def GetActiveFileName(bAutoSave = 1):
+ """Gets the file name for the active frame, saving it if necessary.
+
+ Returns None if it cant be found, or raises KeyboardInterrupt.
+ """
+ pathName = None
+ active = GetActiveView()
+ if active is None:
+ return None
+ try:
+ doc = active.GetDocument()
+ pathName = doc.GetPathName()
+
+ if bAutoSave and \
+ (len(pathName)>0 or \
+ doc.GetTitle()[:8]=="Untitled" or \
+ doc.GetTitle()[:6]=="Script"): # if not a special purpose window
+ if doc.IsModified():
+ try:
+ doc.OnSaveDocument(pathName)
+ pathName = doc.GetPathName()
+
+ # clear the linecache buffer
+ linecache.clearcache()
+
+ except win32ui.error:
+ raise KeyboardInterrupt
+
+ except (win32ui.error, AttributeError):
+ pass
+ if not pathName:
+ return None
+ return pathName
+
+lastScript = ''
+lastArgs = ''
+lastDebuggingType = RS_DEBUGGER_NONE
+
+def RunScript(defName=None, defArgs=None, bShowDialog = 1, debuggingType=None):
+ global lastScript, lastArgs, lastDebuggingType
+
+ # Get the debugger - may be None!
+ debugger = GetDebugger()
+
+ if defName is None:
+ try:
+ pathName = GetActiveFileName()
+ except KeyboardInterrupt:
+ return # User cancelled save.
+ else:
+ pathName = defName
+ if not pathName:
+ pathName = lastScript
+ if defArgs is None:
+ args = ''
+ if pathName==lastScript:
+ args = lastArgs
+ else:
+ args = defArgs
+ if debuggingType is None: debuggingType = lastDebuggingType
+
+ if not pathName or bShowDialog:
+ dlg = DlgRunScript(debugger is not None)
+ dlg['script'] = pathName
+ dlg['args'] = args
+ dlg['debuggingType'] = debuggingType
+ if dlg.DoModal() != win32con.IDOK:
+ return
+ script=dlg['script']
+ args=dlg['args']
+ debuggingType = dlg['debuggingType']
+ if not script: return
+ if debuggingType == RS_DEBUGGER_GO and debugger is not None:
+ # This may surprise users - they select "Run under debugger", but
+ # it appears not to! Only warn when they pick from the dialog!
+ # First - ensure the debugger is activated to pickup any break-points
+ # set in the editor.
+ try:
+ # Create the debugger, but _dont_ init the debugger GUI.
+ rd = debugger._GetCurrentDebugger()
+ except AttributeError:
+ rd = None
+ if rd is not None and len(rd.breaks)==0:
+ msg = "There are no active break-points.\r\n\r\nSelecting this debug option without any\r\nbreak-points is unlikely to have the desired effect\r\nas the debugger is unlikely to be invoked..\r\n\r\nWould you like to step-through in the debugger instead?"
+ rc = win32ui.MessageBox(msg, win32ui.LoadString(win32ui.IDR_DEBUGGER), win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION)
+ if rc == win32con.IDCANCEL:
+ return
+ if rc == win32con.IDYES:
+ debuggingType = RS_DEBUGGER_STEP
+
+ lastDebuggingType = debuggingType
+ lastScript = script
+ lastArgs = args
+ else:
+ script = pathName
+
+ # try and open the script.
+ if len(os.path.splitext(script)[1])==0: # check if no extension supplied, and give one.
+ script = script + '.py'
+ # If no path specified, try and locate the file
+ path, fnameonly = os.path.split(script)
+ if len(path)==0:
+ try:
+ os.stat(fnameonly) # See if it is OK as is...
+ script = fnameonly
+ except os.error:
+ fullScript = app.LocatePythonFile(script)
+ if fullScript is None:
+ win32ui.MessageBox("The file '%s' can not be located" % script )
+ return
+ script = fullScript
+ else:
+ path = win32ui.FullPath(path)
+ if not IsOnPythonPath(path): sys.path.append(path)
+
+ try:
+ f = open(script)
+ except IOError, (code, msg):
+ win32ui.MessageBox("The file could not be opened - %s (%d)" % (msg, code))
+ return
+
+ # Remember and hack sys.argv for the script.
+ oldArgv = sys.argv
+ sys.argv = ParseArgs(args)
+ sys.argv.insert(0, script)
+ bWorked = 0
+ win32ui.DoWaitCursor(1)
+ base = os.path.split(script)[1]
+ # Allow windows to repaint before starting.
+ win32ui.PumpWaitingMessages()
+ win32ui.SetStatusText('Running script %s...' % base,1 )
+ exitCode = 0
+ # We set the interactive window to write output as it occurs
+ oldFlag = None
+ try:
+ oldFlag = sys.stdout.template.writeQueueing
+ # sys.stdout may change on us! So we remember the object
+ # that we actually modified so we can reset it correctly.
+ oldFlagObject = sys.stdout.template
+ oldFlagObject.writeQueueing = 0
+ except (NameError, AttributeError):
+ # sys.stdout is not an interactive window - we dont care.
+ pass
+ # Check the debugger flags
+ if debugger is None and (debuggingType != RS_DEBUGGER_NONE):
+ win32ui.MessageBox("No debugger is installed. Debugging options have been ignored!")
+ debuggingType = RS_DEBUGGER_NONE
+
+ # Get a code object - ignore the debugger for this, as it is probably a syntax error
+ # at this point
+ try:
+ codeObject = compile(f.read()+"\n", script, "exec")
+ except:
+ # Almost certainly a syntax error!
+ _HandlePythonFailure("run script", script)
+ # No code object which to run/debug.
+ return
+ try:
+ if debuggingType == RS_DEBUGGER_STEP:
+ debugger.run(codeObject, __main__.__dict__, start_stepping=1)
+ elif debuggingType == RS_DEBUGGER_GO:
+ debugger.run(codeObject, __main__.__dict__, start_stepping=0)
+ else:
+ # Post mortem or no debugging
+ exec codeObject in __main__.__dict__
+ bWorked = 1
+ except SystemExit, code:
+ exitCode = code
+ bWorked = 1
+ except KeyboardInterrupt:
+ print "Interrupted!"
+ # Consider this successful, as we dont want the debugger.
+ bWorked = 1
+ except SyntaxError:
+ # We dont want to break into the debugger for a syntax error!
+ # (although there shouldnt really be any here, as we work with code objects!)
+ traceback.print_exc()
+ except:
+ # Reset queueing before exception for nice clean printing.
+ if oldFlag is not None:
+ oldFlagObject.writeQueueing = oldFlag
+ oldFlag = oldFlagObject = None
+ traceback.print_exc()
+ if debuggingType == RS_DEBUGGER_PM:
+ debugger.pm()
+ sys.argv = oldArgv
+ f.close()
+ if oldFlag is not None:
+ oldFlagObject.writeQueueing = oldFlag
+ oldFlag = oldFlagObject = None
+ if bWorked:
+ win32ui.SetStatusText("Script '%s' returned exit code %s" %(script, exitCode))
+ else:
+ win32ui.SetStatusText('Exception raised while running script %s' % base)
+ try:
+ sys.stdout.flush()
+ except:
+ pass
+
+ win32ui.DoWaitCursor(0)
+
+def ImportFile():
+ """ This code looks for the current window, and determines if it can be imported. If not,
+ it will prompt for a file name, and allow it to be imported. """
+ try:
+ pathName = GetActiveFileName()
+ except KeyboardInterrupt:
+ pathName = None
+
+ if pathName is not None:
+ if string.lower(os.path.splitext(pathName)[1]) <> ".py":
+ pathName = None
+
+ if pathName is None:
+ openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
+ dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py)|*.py||")
+ dlg.SetOFNTitle("Import Script")
+ if dlg.DoModal()!=win32con.IDOK:
+ return 0
+
+ pathName = dlg.GetPathName()
+
+ # If already imported, dont look for package
+ path, modName = os.path.split(pathName)
+ modName, modExt = os.path.splitext(modName)
+ newPath = None
+ for key, mod in sys.modules.items():
+ if hasattr(mod, '__file__'):
+ fname = mod.__file__
+ base, ext = os.path.splitext(fname)
+ if string.lower(ext) in ['.pyo', '.pyc']:
+ ext = '.py'
+ fname = base + ext
+ if win32ui.ComparePath(fname, pathName):
+ modName = key
+ break
+ else: # for not broken
+ modName, newPath = GetPackageModuleName(pathName)
+ if newPath: sys.path.append(newPath)
+
+ if sys.modules.has_key(modName):
+ bNeedReload = 1
+ what = "reload"
+ else:
+ what = "import"
+ bNeedReload = 0
+
+ win32ui.SetStatusText(string.capitalize(what)+'ing module...',1)
+ win32ui.DoWaitCursor(1)
+# win32ui.GetMainFrame().BeginWaitCursor()
+ try:
+ # always do an import, as it is cheap is already loaded. This ensures
+ # it is in our name space.
+ codeObj = compile('import '+modName,'','exec')
+ exec codeObj in __main__.__dict__
+ if bNeedReload:
+ reload(sys.modules[modName])
+# codeObj = compile('reload('+modName+')','','eval')
+# exec codeObj in __main__.__dict__
+ win32ui.SetStatusText('Successfully ' + what + "ed module '"+modName+"'")
+ except:
+ _HandlePythonFailure(what)
+ win32ui.DoWaitCursor(0)
+
+def CheckFile():
+ """ This code looks for the current window, and gets Python to check it
+ without actually executing any code (ie, by compiling only)
+ """
+ try:
+ pathName = GetActiveFileName()
+ except KeyboardInterrupt:
+ return
+
+ what = "check"
+ win32ui.SetStatusText(string.capitalize(what)+'ing module...',1)
+ win32ui.DoWaitCursor(1)
+ try:
+ f = open(pathName)
+ except IOError, details:
+ print "Cant open file '%s' - %s" % (pathName, details)
+ return
+ try:
+ code = f.read() + "\n"
+ finally:
+ f.close()
+ try:
+ codeObj = compile(code, pathName,'exec')
+ if RunTabNanny(pathName):
+ win32ui.SetStatusText("Python and the TabNanny successfully checked the file '"+os.path.basename(pathName)+"'")
+ except SyntaxError:
+ _HandlePythonFailure(what, pathName)
+ except:
+ traceback.print_exc()
+ _HandlePythonFailure(what)
+ win32ui.DoWaitCursor(0)
+
+def RunTabNanny(filename):
+ import cStringIO
+ tabnanny = FindPythonTool("tabnanny.py")
+ if tabnanny is None:
+ win32ui.MessageBox("The TabNanny is not around, so the children can run amok!" )
+ return
+
+ # We "import" the tab nanny, so we run faster next time:
+ tabnannyhome, tabnannybase = os.path.split(tabnanny)
+ tabnannybase = os.path.splitext(tabnannybase)[0]
+ # Put tab nanny at the top of the path.
+ sys.path.insert(0, tabnannyhome)
+ try:
+ tn = __import__(tabnannybase)
+ # Capture the tab-nanny output
+ newout = cStringIO.StringIO()
+ old_out = sys.stderr, sys.stdout
+ sys.stderr = sys.stdout = newout
+ try:
+ tn.check(filename)
+ finally:
+ # Restore output
+ sys.stderr, sys.stdout = old_out
+ data = newout.getvalue()
+ if data:
+ try:
+ lineno = string.split(data)[1]
+ lineno = int(lineno)
+ _JumpToPosition(filename, lineno)
+ try: # Try and display whitespace
+ GetActiveEditControl().SCISetViewWS(1)
+ except:
+ pass
+ win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno)
+ except (IndexError, TypeError, ValueError):
+ print "The tab nanny complained, but I cant see where!"
+ print data
+
+ return 0
+ else:
+ return 1
+
+ finally:
+ # remove the tab-nanny from the path
+ del sys.path[0]
+
+def _JumpToPosition(fileName, lineno, col = 1):
+ JumpToDocument(fileName, lineno, col)
+
+def JumpToDocument(fileName, lineno=0, col = 1, nChars = 0, bScrollToTop = 0):
+ # Jump to the position in a file.
+ # If lineno is <= 0, dont move the position - just open/restore.
+ # if nChars > 0, select that many characters.
+ # if bScrollToTop, the specified line will be moved to the top of the window
+ # (eg, bScrollToTop should be false when jumping to an error line to retain the
+ # context, but true when jumping to a method defn, where we want the full body.
+ doc = win32ui.GetApp().OpenDocumentFile(fileName)
+ if doc is None: return 0
+ frame = doc.GetFirstView().GetParentFrame()
+ try:
+ view = frame.GetEditorView()
+ if frame.GetActiveView() != view:
+ frame.SetActiveView(view)
+ frame.AutoRestore()
+ except AttributeError: # Not an editor frame??
+ view = doc.GetFirstView()
+ if lineno > 0:
+ charNo = view.LineIndex(lineno-1)
+ start = charNo + col - 1
+ size = view.GetTextLength()
+ view.SetSel(min(start, size), min(start + nChars, size))
+ if bScrollToTop:
+ curTop = view.GetFirstVisibleLine()
+ nScroll = (lineno-1) - curTop
+ view.LineScroll(nScroll, 0)
+ view.SetFocus()
+ return 1
+
+def _HandlePythonFailure(what, syntaxErrorPathName = None):
+ typ, details, tb = sys.exc_info()
+ if typ == SyntaxError:
+ try:
+ msg, (fileName, line, col, text) = details
+ if (not fileName or fileName =="") and syntaxErrorPathName:
+ fileName = syntaxErrorPathName
+ _JumpToPosition(fileName, line, col)
+ except (TypeError, ValueError):
+ msg = str(details)
+ win32ui.SetStatusText('Failed to ' + what + ' - syntax error - %s' % msg)
+ else:
+ traceback.print_exc()
+ win32ui.SetStatusText('Failed to ' + what + ' - ' + str(details) )
+
+# Find a Python "tool" - ie, a file in the Python Tools/Scripts directory.
+def FindPythonTool(filename):
+ try:
+ path = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver))
+ except win32api.error:
+ print "WARNING - The Python registry does not have an 'InstallPath' setting"
+ print " The file '%s' can not be located" % (filename)
+ return None
+ fname = os.path.join(path, "Tools\\Scripts\\%s" % filename)
+ try:
+ os.stat(fname)
+ return fname
+ except os.error:
+ print "WARNING - The Python registry's 'InstallPath' setting does not appear valid"
+ print " The file '%s' can not be located in path '%s'" % (filename, path)
+ return None
+
+def LocatePythonFile( fileName, bBrowseIfDir = 1 ):
+ " Given a file name, return a fully qualified file name, or None "
+ # first look for the exact file as specified
+ if not os.path.isfile(fileName):
+ # Go looking!
+ baseName = fileName
+ for path in sys.path:
+ fileName = os.path.join(path, baseName)
+ if os.path.isdir(fileName):
+ if bBrowseIfDir:
+ old=os.getcwd()
+ os.chdir(fileName)
+ d=win32ui.CreateFileDialog(1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*")
+ rc=d.DoModal()
+ os.chdir(old)
+ if rc==win32con.IDOK:
+ fileName = d.GetPathName()
+ break
+ else:
+ return None
+ else:
+ fileName = fileName + ".py"
+ if os.path.isfile(fileName):
+ break # Found it!
+
+ else: # for not broken out of
+ return None
+ return win32ui.FullPath(fileName)
diff --git a/Pythonwin/pywin/framework/sgrepmdi.py b/Pythonwin/pywin/framework/sgrepmdi.py
new file mode 100644
index 0000000000..dc4dee3717
--- /dev/null
+++ b/Pythonwin/pywin/framework/sgrepmdi.py
@@ -0,0 +1,521 @@
+#SGrepMDI is by Gordon McMillan (gmcm@hypernet.com)
+#It does basically what Find In Files does in MSVC with a couple enhancements.
+# - It saves any directories in the app's ini file (if you want to get rid
+# of them you'll have to edit the file)
+# - "Directories" can be directories,
+# - semicolon separated lists of "directories",
+# - environment variables that evaluate to "directories",
+# - registry path names that evaluate to "directories",
+# - all of which is recursive, so you can mix them all up.
+# - It is MDI, so you can 'nest' greps and return to earlier ones,
+# (ie, have multiple results open at the same time)
+# - Like FIF, double clicking a line opens an editor and takes you to the line.
+# - You can highlight text, right click and start a new grep with the selected
+# text as search pattern and same directories etc as before.
+# - You can save grep parameters (so you don't lose your hardearned pattern)
+# from File|Save
+# - You can save grep results by right clicking in the result window.
+# Hats off to Mark Hammond for providing an environment where I could cobble
+# something like this together in a couple evenings!
+
+import win32ui
+import win32api
+from pywin.mfc import docview, dialog, window
+import win32con
+import string
+import regex
+import regex_syntax
+import glob
+import os
+import stat
+import glob
+import scriptutils
+
+def getsubdirs(d):
+ dlist = []
+ flist = glob.glob(d+'\\*')
+ for f in flist:
+ if os.path.isdir(f):
+ dlist.append(f)
+ dlist = dlist + getsubdirs(f)
+ return dlist
+
+class dirpath:
+ def __init__(self, str, recurse=0):
+ dp = string.split(str, ';')
+ dirs = {}
+ for d in dp:
+ if os.path.isdir(d):
+ d = string.lower(d)
+ if not dirs.has_key(d):
+ dirs[d] = None
+ if recurse:
+ subdirs = getsubdirs(d)
+ for sd in subdirs:
+ sd = string.lower(sd)
+ if not dirs.has_key(sd):
+ dirs[sd] = None
+ elif os.path.isfile(d):
+ pass
+ else:
+ x = None
+ if os.environ.has_key(d):
+ x = dirpath(os.environ[d])
+ elif d[:5] == 'HKEY_':
+ keystr = string.split(d,'\\')
+ try:
+ root = eval('win32con.'+keystr[0])
+ except:
+ win32ui.MessageBox("Can't interpret registry key name '%s'" % keystr[0])
+ try:
+ subkey = string.join(keystr[1:], '\\')
+ val = win32api.RegQueryValue(root, subkey)
+ if val:
+ x = dirpath(val)
+ else:
+ win32ui.MessageBox("Registry path '%s' did not return a path entry" % d)
+ except:
+ win32ui.MessageBox("Can't interpret registry key value: %s" % keystr[1:])
+ else:
+ win32ui.MessageBox("Directory '%s' not found" % d)
+ if x:
+ for xd in x:
+ if not dirs.has_key(xd):
+ dirs[xd] = None
+ if recurse:
+ subdirs = getsubdirs(xd)
+ for sd in subdirs:
+ sd = string.lower(sd)
+ if not dirs.has_key(sd):
+ dirs[sd] = None
+ self.dirs = []
+ for d in dirs.keys():
+ self.dirs.append(d)
+
+ def __getitem__(self, key):
+ return self.dirs[key]
+ def __len__(self):
+ return len(self.dirs)
+ def __setitem__(self, key, value):
+ self.dirs[key] = value
+ def __delitem__(self, key):
+ del self.dirs[key]
+ def __getslice__(self, lo, hi):
+ return self.dirs[lo:hi]
+ def __setslice__(self, lo, hi, seq):
+ self.dirs[lo:hi] = seq
+ def __delslice__(self, lo, hi):
+ del self.dirs[lo:hi]
+ def __add__(self, other):
+ if type(other) == type(self) or type(other) == type([]):
+ return self.dirs + other.dirs
+ def __radd__(self, other):
+ if type(other) == type(self) or type(other) == type([]):
+ return other.dirs + self.dirs
+
+# Group(1) is the filename, group(2) is the lineno.
+#regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
+
+regexGrepResult=regex.compile("^\\([a-zA-Z]:[^(]*\\)(\\([0-9]+\\))")
+
+#these are the atom numbers defined by Windows for basic dialog controls
+
+BUTTON = 0x80
+EDIT = 0x81
+STATIC = 0x82
+LISTBOX = 0x83
+SCROLLBAR = 0x84
+COMBOBOX = 0x85
+
+class GrepTemplate(docview.RichEditDocTemplate):
+ def __init__(self):
+ docview.RichEditDocTemplate.__init__(self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView)
+ self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n")
+ win32ui.GetApp().AddDocTemplate(self)
+ self.docparams = None
+
+ def MatchDocType(self, fileName, fileType):
+ doc = self.FindOpenDocument(fileName)
+ if doc: return doc
+ ext = string.lower(os.path.splitext(fileName)[1])
+ if ext =='.grep':
+ return win32ui.CDocTemplate_Confidence_yesAttemptNative
+ return win32ui.CDocTemplate_Confidence_noAttempt
+
+ def setParams(self, params):
+ self.docparams = params
+
+ def readParams(self):
+ tmp = self.docparams
+ self.docparams = None
+ return tmp
+
+class GrepFrame(window.MDIChildWnd):
+ # The template and doc params will one day be removed.
+ def __init__(self, wnd = None):
+ window.MDIChildWnd.__init__(self, wnd)
+
+class GrepDocument(docview.RichEditDoc):
+ def __init__(self, template):
+ docview.RichEditDoc.__init__(self, template)
+ self.dirpattern = ''
+ self.filpattern = ''
+ self.greppattern = ''
+ self.casesensitive = 1
+ self.recurse = 1
+ self.verbose = 0
+
+ def OnOpenDocument(self, fnm):
+ #this bizarre stuff with params is so right clicking in a result window
+ #and starting a new grep can communicate the default parameters to the
+ #new grep.
+ try:
+ params = open(fnm,'r').read()
+ except:
+ params = None
+ self.setInitParams(params)
+ return self.OnNewDocument()
+
+ def OnCloseDocument(self):
+ try:
+ win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
+ except:
+ pass
+ return self._obj_.OnCloseDocument()
+
+ def saveInitParams(self):
+ # Only save the flags, not the text boxes.
+ paramstr = "\t%s\t\t%d\t%d" % (self.filpattern, self.casesensitive, self.recurse)
+ win32ui.WriteProfileVal("Grep", "Params", paramstr)
+
+ def setInitParams(self, paramstr):
+ if paramstr is None:
+ paramstr = win32ui.GetProfileVal("Grep", "Params", '\t\t\t1\t0\t0')
+ params = string.split(paramstr, '\t')
+ if len(params) < 3:
+ params = params + ['']*(3-len(params))
+ if len(params) < 6:
+ params = params + [0]*(6-len(params))
+ self.dirpattern = params[0]
+ self.filpattern = params[1]
+ self.greppattern = params[2]
+ self.casesensitive = int(params[3])
+ self.recurse = int(params[4])
+ self.verbose = int(params[5])
+ # setup some reasonable defaults.
+ if not self.dirpattern:
+ self.dirpattern = os.getcwd()
+ if not self.filpattern:
+ self.filpattern = "*.py"
+
+ def OnNewDocument(self):
+ if self.dirpattern == '':
+ self.setInitParams(greptemplate.readParams())
+ d = GrepDialog(self.dirpattern, self.filpattern, self.greppattern, self.casesensitive, self.recurse, self.verbose)
+ if d.DoModal() == win32con.IDOK:
+ self.dirpattern = d['dirpattern']
+ self.filpattern = d['filpattern']
+ self.greppattern = d['greppattern']
+ self.casesensitive = d['casesensitive']
+ self.recurse = d['recursive']
+ self.verbose = d['verbose']
+ self.doSearch()
+ self.saveInitParams()
+ return 1
+ return 0 # cancelled - return zero to stop frame creation.
+
+ def doSearch(self):
+ self.dp = dirpath(self.dirpattern, self.recurse)
+ self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern))
+ #self.text = []
+ self.GetFirstView().Append('#Search '+self.dirpattern+'\n')
+ if self.verbose:
+ self.GetFirstView().Append('# ='+`self.dp.dirs`+'\n')
+ self.GetFirstView().Append('# Files '+self.filpattern+'\n')
+ self.GetFirstView().Append('# For '+self.greppattern+'\n')
+ self.fplist = string.split(self.filpattern,';')
+ regex.set_syntax(regex_syntax.RE_SYNTAX_GREP)
+ if self.casesensitive:
+ self.pat = regex.compile(self.greppattern)
+ else:
+ self.pat = regex.compile(self.greppattern, regex.casefold)
+ win32ui.SetStatusText("Searching. Please wait...", 0)
+ self.dpndx = self.fpndx = 0
+ self.fndx = -1
+ if not self.dp:
+ self.GetFirstView().Append("# ERROR: '%s' does not resolve to any search locations" % self.dirpattern)
+ self.SetModifiedFlag(0)
+ else:
+ self.flist = glob.glob(self.dp[0]+'\\'+self.fplist[0])
+ win32ui.GetApp().AddIdleHandler(self.SearchFile)
+
+ def SearchFile(self, handler, count):
+ self.fndx = self.fndx + 1
+ if self.fndx < len(self.flist):
+ f = self.flist[self.fndx]
+ if self.verbose:
+ self.GetFirstView().Append('# ..'+f+'\n')
+ win32ui.SetStatusText("Searching "+f, 0)
+ lines = open(f, 'r').readlines()
+ for i in range(len(lines)):
+ line = lines[i]
+ if self.pat.search(line) >= 0:
+ self.GetFirstView().Append(f+'('+`i+1` + ') '+line)
+ else:
+ self.fndx = -1
+ self.fpndx = self.fpndx + 1
+ if self.fpndx < len(self.fplist):
+ self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
+ else:
+ self.fpndx = 0
+ self.dpndx = self.dpndx + 1
+ if self.dpndx < len(self.dp):
+ self.flist = glob.glob(self.dp[self.dpndx] + '\\' + self.fplist[self.fpndx])
+ else:
+ win32ui.SetStatusText("Search complete.", 0)
+ self.SetModifiedFlag(0) # default to not modified.
+ try:
+ win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
+ except:
+ pass
+ return 0
+ return 1
+
+ def GetParams(self):
+ return self.dirpattern+'\t'+self.filpattern+'\t'+self.greppattern+'\t'+`self.casesensitive`+'\t'+`self.recurse`+'\t'+`self.verbose`
+
+ def OnSaveDocument(self, filename):
+# print 'OnSaveDocument() filename=',filename
+ savefile = open(filename,"wb")
+ txt = self.GetParams()+'\n'
+# print 'writing',txt
+ savefile.write(txt)
+ savefile.close()
+ self.SetModifiedFlag(0)
+ return 1
+
+ID_OPEN_FILE = 0xe400
+ID_GREP = 0xe401
+ID_SAVERESULTS = 0x402
+ID_TRYAGAIN = 0x403
+
+class GrepView(docview.RichEditView):
+ def __init__(self, doc):
+ docview.RichEditView.__init__(self, doc)
+ self.SetWordWrap(win32ui.CRichEditView_WrapNone)
+ self.HookHandlers()
+
+ def OnInitialUpdate(self):
+ rc = self._obj_.OnInitialUpdate()
+ return rc
+
+ def HookHandlers(self):
+ self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
+ self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
+ self.HookCommand(self.OnCmdGrep, ID_GREP)
+ self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
+ self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
+ self.HookMessage(self.OnLDblClick,win32con.WM_LBUTTONDBLCLK)
+
+ def OnLDblClick(self,params):
+ line = self.GetLine()
+ if regexGrepResult.match(line) > 0:
+ fname = regexGrepResult.group(1)
+ line = string.atoi(regexGrepResult.group(2))
+ scriptutils.JumpToDocument(fname, line)
+ return 0 # dont pass on
+ return 1 # pass it on by default.
+
+ def OnRClick(self, params):
+ menu = win32ui.CreatePopupMenu()
+ flags=win32con.MF_STRING|win32con.MF_ENABLED
+ lineno = self._obj_.LineFromChar(-1) #selection or current line
+ line = self._obj_.GetLine(lineno)
+ if regexGrepResult.match(line) > 0:
+ self.fnm = regexGrepResult.group(1)
+ self.lnnum = string.atoi(regexGrepResult.group(2))
+ menu.AppendMenu(flags, ID_OPEN_FILE, "&Open "+self.fnm)
+ menu.AppendMenu(win32con.MF_SEPARATOR)
+ menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
+ charstart, charend = self._obj_.GetSel()
+ if charstart != charend:
+ linestart = self._obj_.LineIndex(lineno)
+ self.sel = line[charstart-linestart:charend-linestart]
+ menu.AppendMenu(flags, ID_GREP, "&Grep for "+self.sel)
+ menu.AppendMenu(win32con.MF_SEPARATOR)
+ menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
+ menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
+ menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
+ menu.AppendMenu(flags, win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
+ menu.AppendMenu(flags, win32con.MF_SEPARATOR);
+ menu.AppendMenu(flags, ID_SAVERESULTS, 'Sa&ve results')
+ menu.TrackPopupMenu(params[5])
+ return 0
+
+ def OnCmdOpenFile(self, cmd, code):
+ doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
+ if doc:
+ vw = doc.GetFirstView()
+ #hope you have an editor that implements GotoLine()!
+ try:
+ vw.GotoLine(string.atoi(self.lnnum))
+ except:
+ pass
+ return 0
+
+ def OnCmdGrep(self, cmd, code):
+ curparamsstr = self.GetDocument().GetParams()
+ params = string.split(curparamsstr,'\t')
+ params[2] = self.sel
+ greptemplate.setParams(string.join(params,'\t'))
+ greptemplate.OpenDocumentFile()
+ return 0
+
+ def OnTryAgain(self, cmd, code):
+ greptemplate.setParams(self.GetDocument().GetParams())
+ greptemplate.OpenDocumentFile()
+ return 0
+
+ def OnCmdSave(self, cmd, code):
+ flags = win32con.OFN_OVERWRITEPROMPT
+ dlg = win32ui.CreateFileDialog(0, None, None, flags, "Text Files (*.txt)|*.txt||", self)
+ dlg.SetOFNTitle("Save Results As")
+ if dlg.DoModal() == win32con.IDOK:
+ pn = dlg.GetPathName()
+ self._obj_.SaveFile(pn)
+ return 0
+
+ def Append(self, strng):
+ numlines = self.GetLineCount()
+ endpos = self.LineIndex(numlines-1) + len(self.GetLine(numlines-1))
+ self.SetSel(endpos, endpos)
+ self.ReplaceSel(strng)
+
+
+class GrepDialog(dialog.Dialog):
+ def __init__(self, dp, fp, gp, cs, r, v):
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ CS = win32con.WS_CHILD | win32con.WS_VISIBLE
+ tmp = [ ["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")], ]
+ tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS ])
+ tmp.append([EDIT, gp, 101, (52, 7, 144, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
+ tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS ])
+ tmp.append([EDIT, dp, 102, (52, 20, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
+ tmp.append([BUTTON, '...', 110, (182,20, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
+ tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS ])
+ tmp.append([EDIT, fp, 103, (52, 33, 128, 11), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER ])
+ tmp.append([BUTTON, '...', 111, (182,33, 16, 11), CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'Case sensitive', 104, (7, 45, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'Subdirectories', 105, (7, 56, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'Verbose', 106, (7, 67, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'OK', win32con.IDOK, (166,53, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (166,67, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
+ dialog.Dialog.__init__(self, tmp)
+ self.AddDDX(101,'greppattern')
+ self.AddDDX(102,'dirpattern')
+ self.AddDDX(103,'filpattern')
+ self.AddDDX(104,'casesensitive')
+ self.AddDDX(105,'recursive')
+ self.AddDDX(106,'verbose')
+ self._obj_.data['greppattern'] = gp
+ self._obj_.data['dirpattern'] = dp
+ self._obj_.data['filpattern'] = fp
+ self._obj_.data['casesensitive'] = cs
+ self._obj_.data['recursive'] = r
+ self._obj_.data['verbose'] = v
+ self.HookCommand(self.OnMoreDirectories, 110)
+ self.HookCommand(self.OnMoreFiles, 111)
+
+ def OnMoreDirectories(self, cmd, code):
+ self.getMore('Grep\\Directories', 'dirpattern')
+
+ def OnMoreFiles(self, cmd, code):
+ self.getMore('Grep\\File Types', 'filpattern')
+
+ def getMore(self, section, key):
+ self.UpdateData(1)
+ #get the items out of the ini file
+ ini = win32ui.GetProfileFileName()
+ secitems = win32api.GetProfileSection(section, ini)
+ items = []
+ for secitem in secitems:
+ items.append(string.split(secitem,'=')[1])
+ dlg = GrepParamsDialog(items)
+ if dlg.DoModal() == win32con.IDOK:
+ itemstr = string.join(dlg.getItems(),';')
+ self._obj_.data[key] = itemstr
+ #update the ini file with dlg.getNew()
+ i = 0
+ newitems = dlg.getNew()
+ if newitems:
+ items = items + newitems
+ for item in items:
+ win32api.WriteProfileVal(section, `i`, item, ini)
+ i = i + 1
+ self.UpdateData(0)
+
+ def OnOK(self):
+ self.UpdateData(1)
+ for id, name in [(101,'greppattern'), (102,'dirpattern'), (103,'filpattern')]:
+ if not self[name]:
+ self.GetDlgItem(id).SetFocus()
+ win32api.MessageBeep()
+ win32ui.SetStatusText("Please enter a value")
+ return
+ self._obj_.OnOK()
+
+class GrepParamsDialog(dialog.Dialog):
+ def __init__(self, items):
+ self.items = items
+ self.newitems = []
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ CS = win32con.WS_CHILD | win32con.WS_VISIBLE
+ tmp = [ ["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")], ]
+ tmp.append([LISTBOX, '', 107, (7, 7, 150, 72), CS | win32con.LBS_MULTIPLESEL| win32con.LBS_STANDARD | win32con.LBS_HASSTRINGS | win32con.WS_TABSTOP | win32con.LBS_NOTIFY])
+ tmp.append([BUTTON,'OK', win32con.IDOK, (167, 7, 32, 12), CS | win32con.BS_DEFPUSHBUTTON| win32con.WS_TABSTOP])
+ tmp.append([BUTTON,'Cancel', win32con.IDCANCEL, (167,23, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
+ tmp.append([STATIC,'New:', -1, (2, 83, 15, 12), CS])
+ tmp.append([EDIT, '', 108, (18, 83, 139, 12), CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER])
+ tmp.append([BUTTON,'Add', 109, (167,83, 32, 12), CS | win32con.BS_PUSHBUTTON| win32con.WS_TABSTOP])
+ dialog.Dialog.__init__(self, tmp)
+ self.HookCommand(self.OnAddItem, 109)
+ self.HookCommand(self.OnListDoubleClick, 107)
+
+ def OnInitDialog(self):
+ lb = self.GetDlgItem(107)
+ for item in self.items:
+ lb.AddString(item)
+ return self._obj_.OnInitDialog()
+
+ def OnAddItem(self, cmd, code):
+ eb = self.GetDlgItem(108)
+ item = eb.GetLine(0)
+ self.newitems.append(item)
+ lb = self.GetDlgItem(107)
+ i = lb.AddString(item)
+ lb.SetSel(i, 1)
+ return 1
+
+ def OnListDoubleClick(self, cmd, code):
+ if code == win32con.LBN_DBLCLK:
+ self.OnOK()
+ return 1
+
+ def OnOK(self):
+ lb = self.GetDlgItem(107)
+ self.selections = lb.GetSelTextItems()
+ self._obj_.OnOK()
+
+ def getItems(self):
+ return self.selections
+
+ def getNew(self):
+ return self.newitems
+
+try:
+ win32ui.GetApp().RemoveDocTemplate(greptemplate)
+except NameError:
+ pass
+
+greptemplate = GrepTemplate()
diff --git a/Pythonwin/pywin/framework/startup.py b/Pythonwin/pywin/framework/startup.py
new file mode 100644
index 0000000000..e668a94af4
--- /dev/null
+++ b/Pythonwin/pywin/framework/startup.py
@@ -0,0 +1,61 @@
+# startup.py
+#
+"The main application startup code for PythonWin."
+
+#
+# This does the basic command line handling.
+
+# Keep this as short as possible, cos error output is only redirected if
+# this runs OK. Errors in imported modules are much better - the messages go somewhere (not any more :-)
+
+import sys
+import win32ui
+import strop
+
+# You may wish to redirect error output somewhere useful if you have startup errors.
+# eg, 'import win32traceutil' will do this for you.
+# import win32traceutil # Just uncomment this line to see error output!
+
+# An old class I used to use - generally only useful if Pythonwin is running under MSVC
+#class DebugOutput:
+# softspace=1
+# def write(self,message):
+# win32ui.OutputDebug(message)
+#sys.stderr=sys.stdout=DebugOutput()
+
+# To fix a problem with Pythonwin when started from the Pythonwin directory,
+# we update the pywin path to ensure it is absolute.
+# If it is indeed relative, it will be relative to our current directory.
+# If its already absolute, then this will have no affect.
+import pywin
+pywin.__path__[0] = win32ui.FullPath(pywin.__path__[0])
+
+# make a few wierd sys values. This is so later we can clobber sys.argv to trick
+# scripts when running under a GUI environment.
+
+moduleName = "intpyapp"
+sys.appargvoffset = 0
+sys.appargv = sys.argv[:]
+# Must check for /app param here.
+if len(sys.argv)>=2 and strop.lower(sys.argv[0])=='/app':
+ import cmdline
+ moduleName = cmdline.FixArgFileName(sys.argv[1])
+ sys.appargvoffset = 1
+ newargv=sys.argv[sys.appargvoffset:]
+# newargv.insert(0, sys.argv[0])
+ sys.argv = newargv
+
+exec "import %s\n" % moduleName
+
+try:
+ win32ui.GetApp()._obj_
+ # This worked - an app already exists - do nothing more
+except (AttributeError, win32ui.error):
+ # This means either no app object exists at all, or the one
+ # that does exist does not have a Python class (ie, was created
+ # by the host .EXE). In this case, we do the "old style" init...
+ import app
+ if app.AppBuilder is None:
+ raise TypeError, "No application object has been registered"
+
+ app.App = app.AppBuilder()
diff --git a/Pythonwin/pywin/framework/toolmenu.py b/Pythonwin/pywin/framework/toolmenu.py
new file mode 100644
index 0000000000..12a06bdb63
--- /dev/null
+++ b/Pythonwin/pywin/framework/toolmenu.py
@@ -0,0 +1,248 @@
+# toolmenu.py
+
+import win32ui
+import win32con
+import win32api
+import app
+import sys
+import string
+
+tools = {}
+idPos = 100
+
+# The default items should no tools menu exist in the INI file.
+defaultToolMenuItems = [
+ ('Browser', 'win32ui.GetApp().OnViewBrowse(0,0)'),
+ ('Browse PythonPath', 'from pywin.tools import browseProjects;browseProjects.Browse()'),
+ ('Edit Python Path', 'from pywin.tools import regedit;regedit.EditRegistry()'),
+ ('COM Makepy utility', 'from win32com.client import makepy;makepy.main()'),
+ ('COM Browser', 'from win32com.client import combrowse;combrowse.main()'),
+ ('Trace Collector Debugging tool', 'from pywin.tools import TraceCollector;TraceCollector.MakeOutputWindow()'),
+]
+
+def LoadToolMenuItems():
+ # Load from the registry.
+ items = []
+ lookNo = 1
+ while 1:
+ menu = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "", "")
+ if menu=="":
+ break
+ cmd = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "Command", "")
+ items.append(menu, cmd)
+ lookNo = lookNo + 1
+
+ if len(items)==0:
+ items = defaultToolMenuItems
+ return items
+
+def WriteToolMenuItems( items ):
+ # Items is a list of (menu, command)
+ # Delete the entire registry tree.
+ try:
+ mainKey = win32ui.GetAppRegistryKey()
+ toolKey = win32api.RegOpenKey(mainKey, "Tools Menu")
+ except win32ui.error:
+ toolKey = None
+ if toolKey is not None:
+ while 1:
+ try:
+ subkey = win32api.RegEnumKey(toolKey, 0)
+ except win32api.error:
+ break
+ win32api.RegDeleteKey(toolKey, subkey)
+ # Keys are now removed - write the new ones.
+ # But first check if we have the defaults - and if so, dont write anything!
+ if items==defaultToolMenuItems:
+ return
+ itemNo = 1
+ for menu, cmd in items:
+ win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "", menu)
+ win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "Command", cmd)
+ itemNo = itemNo + 1
+
+def SetToolsMenu(menu, menuPos = None):
+ global tools
+ global idPos
+
+ # todo - check the menu does not already exist.
+ # Create the new menu
+ toolsMenu = win32ui.CreatePopupMenu()
+
+ # Load from the ini file.
+ items = LoadToolMenuItems()
+ for menuString, cmd in items:
+ tools[idPos] = (menuString, cmd, menuString)
+ toolsMenu.AppendMenu(win32con.MF_ENABLED|win32con.MF_STRING,idPos, menuString)
+ win32ui.GetMainFrame().HookCommand(HandleToolCommand, idPos)
+ idPos=idPos+1
+
+ # Find the correct spot to insert the new tools menu.
+ if menuPos is None:
+ menuPos = menu.GetMenuItemCount()-2
+ if menuPos<0: menuPos=0
+
+ menu.InsertMenu(menuPos, win32con.MF_BYPOSITION|win32con.MF_ENABLED|win32con.MF_STRING|win32con.MF_POPUP, toolsMenu.GetHandle(), '&Tools')
+
+def HandleToolCommand(cmd, code):
+ import traceback
+ import regsub
+ global tools
+ (menuString, pyCmd, desc) = tools[cmd]
+ win32ui.SetStatusText("Executing tool %s" % desc, 1)
+ pyCmd = regsub.gsub('\\\\n','\n', pyCmd)
+ win32ui.DoWaitCursor(1)
+ oldFlag = None
+ try:
+ oldFlag = sys.stdout.template.writeQueueing
+ sys.stdout.template.writeQueueing = 0
+ except (NameError, AttributeError):
+ pass
+
+ try:
+ exec "%s\n" % pyCmd
+ worked=1
+ except SystemExit:
+ # The program raised a SystemExit - ignore it.
+ worked = 1
+ except:
+ print "Failed to execute command:\n%s" % pyCmd
+ traceback.print_exc()
+ worked=0
+ if oldFlag is not None:
+ sys.stdout.template.writeQueueing = oldFlag
+ win32ui.DoWaitCursor(0)
+ if worked:
+ text = "Completed successfully."
+ else:
+ text = "Error executing %s." % desc
+ win32ui.SetStatusText(text, 1)
+
+# The property page for maintaing the items on the Tools menu.
+import commctrl
+from pywin.mfc import dialog
+
+class ToolMenuPropPage(dialog.PropertyPage):
+ def __init__(self):
+ self.bImChangingEditControls = 0 # Am I programatically changing the controls?
+ dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TOOLMENU)
+
+ def OnInitDialog(self):
+ self.editMenuCommand = self.GetDlgItem(win32ui.IDC_EDIT2)
+ self.butNew = self.GetDlgItem(win32ui.IDC_BUTTON3)
+
+ # Now hook the change notification messages for the edit controls.
+ self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT1)
+ self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT2)
+
+ self.HookNotify(self.OnNotifyListControl, commctrl.LVN_ITEMCHANGED)
+ self.HookNotify(self.OnNotifyListControlEndLabelEdit, commctrl.LVN_ENDLABELEDIT)
+
+ # Hook the button clicks.
+ self.HookCommand(self.OnButtonNew, win32ui.IDC_BUTTON3) # New Item
+ self.HookCommand(self.OnButtonDelete, win32ui.IDC_BUTTON4) # Delete item
+ self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON1) # Move up
+ self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON2) # Move down
+
+ # Setup the columns in the list control
+ lc = self.GetDlgItem(win32ui.IDC_LIST1)
+ rect = lc.GetWindowRect()
+ cx = rect[2] - rect[0]
+ colSize = cx/2 - win32api.GetSystemMetrics(win32con.SM_CXBORDER) - 1
+
+ item = commctrl.LVCFMT_LEFT, colSize, "Menu Text"
+ lc.InsertColumn(0, item)
+
+ item = commctrl.LVCFMT_LEFT, colSize, "Python Command"
+ lc.InsertColumn(1, item)
+
+ # Insert the existing tools menu
+ itemNo = 0
+ for desc, cmd in LoadToolMenuItems():
+ lc.InsertItem(itemNo, desc)
+ lc.SetItemText(itemNo, 1, cmd)
+ itemNo = itemNo + 1
+
+ self.listControl = lc
+ return dialog.PropertyPage.OnInitDialog(self)
+
+ def OnOK(self):
+ # Write the menu back to the registry.
+ items = []
+ itemLook = 0
+ while 1:
+ try:
+ items.append( self.listControl.GetItemText(itemLook, 0), self.listControl.GetItemText(itemLook, 1) )
+ except win32ui.error:
+ # no more items!
+ break
+ itemLook = itemLook + 1
+ WriteToolMenuItems( items )
+ return self._obj_.OnOK()
+
+ def OnCommandEditControls(self, id, cmd):
+# print "OnEditControls", id, cmd
+ if cmd==win32con.EN_CHANGE and not self.bImChangingEditControls:
+ itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ newText = self.editMenuCommand.GetWindowText()
+ self.listControl.SetItemText(itemNo, 1, newText)
+
+ return 0
+
+ def OnNotifyListControlEndLabelEdit(self, id, cmd):
+ newText = self.listControl.GetEditControl().GetWindowText()
+ itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ self.listControl.SetItemText(itemNo, 0, newText)
+
+ def OnNotifyListControl(self, id, cmd):
+# print id, cmd
+ try:
+ itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ except win32ui.error: # No selection!
+ return
+
+ self.bImChangingEditControls = 1
+ try:
+ item = self.listControl.GetItem(itemNo, 1)
+ self.editMenuCommand.SetWindowText(item[4])
+ finally:
+ self.bImChangingEditControls = 0
+
+ return 0 # we have handled this!
+
+ def OnButtonNew(self, id, cmd):
+ if cmd==win32con.BN_CLICKED:
+ newIndex = self.listControl.GetItemCount()
+ self.listControl.InsertItem(newIndex, "Click to edit the text")
+ self.listControl.EnsureVisible(newIndex, 0)
+
+ def OnButtonMove(self, id, cmd):
+ if cmd==win32con.BN_CLICKED:
+ try:
+ itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ except win32ui.error:
+ return
+ menu = self.listControl.GetItemText(itemNo, 0)
+ cmd = self.listControl.GetItemText(itemNo, 1)
+ if id == win32ui.IDC_BUTTON1:
+ # Move up
+ if itemNo > 0:
+ self.listControl.DeleteItem(itemNo)
+ # reinsert it.
+ self.listControl.InsertItem(itemNo-1, menu)
+ self.listControl.SetItemText(itemNo-1, 1, cmd)
+ else:
+ # Move down.
+ if itemNo < self.listControl.GetItemCount()-1:
+ self.listControl.DeleteItem(itemNo)
+ # reinsert it.
+ self.listControl.InsertItem(itemNo+1, menu)
+ self.listControl.SetItemText(itemNo+1, 1, cmd)
+
+ def OnButtonDelete(self, id, cmd):
+ if cmd==win32con.BN_CLICKED:
+ try:
+ itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ except win32ui.error: # No selection!
+ return
+ self.listControl.DeleteItem(itemNo)
diff --git a/Pythonwin/pywin/framework/window.py b/Pythonwin/pywin/framework/window.py
new file mode 100644
index 0000000000..c90b091f94
--- /dev/null
+++ b/Pythonwin/pywin/framework/window.py
@@ -0,0 +1,13 @@
+# Framework Window classes.
+
+# Most Pythonwin windows should use these classes rather than
+# the raw MFC ones if they want Pythonwin specific functionality.
+import pywin.mfc.window
+import win32con
+
+class MDIChildWnd(pywin.mfc.window.MDIChildWnd):
+ def AutoRestore(self):
+ "If the window is minimised or maximised, restore it."
+ p = self.GetWindowPlacement()
+ if p[1]==win32con.SW_MINIMIZE or p[1]==win32con.SW_SHOWMINIMIZED:
+ self.SetWindowPlacement(p[0], win32con.SW_RESTORE, p[2], p[3], p[4])
diff --git a/Pythonwin/pywin/framework/winout.py b/Pythonwin/pywin/framework/winout.py
new file mode 100644
index 0000000000..3d18a7ac21
--- /dev/null
+++ b/Pythonwin/pywin/framework/winout.py
@@ -0,0 +1,476 @@
+# winout.py
+#
+# generic "output window"
+#
+# This Window will detect itself closing, and recreate next time output is
+# written to it.
+
+# This has the option of writing output at idle time (by hooking the
+# idle message, and queueing output) or writing as each
+# write is executed.
+# Updating the window directly gives a jerky appearance as many writes
+# take place between commands, and the windows scrolls, and updates etc
+# Updating at idle-time may defer all output of a long process, giving the
+# appearence nothing is happening.
+# There is a compromise "line" mode, which will output whenever
+# a complete line is available.
+
+# behaviour depends on self.writeQueueing
+
+# This module is thread safe - output can originate from any thread. If any thread
+# other than the main thread attempts to print, it is always queued until next idle time
+
+import sys, string, regex
+from pywin.mfc import docview
+from pywin.framework import app, window
+import win32ui, win32api, win32con
+import Queue
+
+debug = lambda msg: None
+
+#debug=win32ui.OutputDebugString
+#import win32trace;win32trace.InitWrite() # for debugging - delete me!
+#debug = win32trace.write
+
+class flags:
+ # queueing of output.
+ WQ_NONE = 0
+ WQ_LINE = 1
+ WQ_IDLE = 2
+
+#WindowOutputDocumentParent=docview.RichEditDoc
+WindowOutputDocumentParent=docview.Document
+class WindowOutputDocument(WindowOutputDocumentParent):
+ def SaveModified(self):
+ return 1 # say it is OK to destroy my document
+
+class WindowOutputFrame(window.MDIChildWnd):
+ def __init__(self, wnd = None):
+ window.MDIChildWnd.__init__(self, wnd)
+ self.HookMessage(self.OnSizeMove, win32con.WM_SIZE)
+ self.HookMessage(self.OnSizeMove, win32con.WM_MOVE)
+
+ def LoadFrame( self, idResource, style, wndParent, context ):
+ self.template = context.template
+ return self._obj_.LoadFrame(idResource, style, wndParent, context)
+
+ def PreCreateWindow(self, cc):
+ cc = self._obj_.PreCreateWindow(cc)
+ if self.template.defSize and self.template.defSize[0] != self.template.defSize[1]:
+ rect = app.RectToCreateStructRect(self.template.defSize)
+ cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
+ return cc
+ def OnSizeMove(self, msg):
+ # so recreate maintains position.
+ # Need to map coordinates from the
+ # frame windows first child.
+ mdiClient = self.GetParent()
+ self.template.defSize = mdiClient.ScreenToClient(self.GetWindowRect())
+ def OnDestroy(self, message):
+ self.template.OnFrameDestroy(self)
+ return 1
+
+class WindowOutputViewImpl:
+ def __init__(self):
+ self.patErrorMessage=regex.compile('.*File "\(.*\)", line \([0-9]+\)')
+ self.template = self.GetDocument().GetDocTemplate()
+
+ def HookHandlers(self):
+ # Hook for finding and locating error messages
+ self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK)
+ # Hook for the right-click menu.
+ self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
+
+ def OnDestroy(self, msg):
+ self.template.OnViewDestroy(self)
+
+ def OnInitialUpdate(self):
+ self.RestoreKillBuffer()
+ self.SetSel(-2) # end of buffer
+
+ def GetRightMenuItems(self):
+ ret = []
+ flags=win32con.MF_STRING|win32con.MF_ENABLED
+ ret.append(flags, win32ui.ID_EDIT_COPY, '&Copy')
+ ret.append(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
+ return ret
+
+ #
+ # Windows command handlers, virtuals, etc.
+ #
+ def OnRClick(self,params):
+ paramsList = self.GetRightMenuItems()
+ menu = win32ui.CreatePopupMenu()
+ for appendParams in paramsList:
+ if type(appendParams)!=type(()):
+ appendParams = (appendParams,)
+ apply(menu.AppendMenu, appendParams)
+ menu.TrackPopupMenu(params[5]) # track at mouse position.
+ return 0
+
+ def OnLDoubleClick(self,params):
+ if self.HandleSpecialLine():
+ return 0 # dont pass on
+ return 1 # pass it on by default.
+
+ # as this is often used as an output window, exeptions will often
+ # be printed. Therefore, we support this functionality at this level.
+ # Returns TRUE if the current line is an error message line, and will
+ # jump to it. FALSE if no error (and no action taken)
+ def HandleSpecialLine(self):
+ import scriptutils
+ line = self.GetLine()
+ matchResult = self.patErrorMessage.match(line)
+ if matchResult<=0:
+ # No match - try the next line
+ lineNo = self.LineFromChar()
+ if lineNo > 0:
+ line = self.GetLine(lineNo-1)
+ matchResult = self.patErrorMessage.match(line)
+ if matchResult>0:
+ # we have an error line.
+ fileName = self.patErrorMessage.group(1)
+ if fileName[0]=="<":
+ win32ui.SetStatusText("Can not load this file")
+ return 1 # still was an error message.
+ else:
+ lineNoString = self.patErrorMessage.group(2)
+ # Attempt to locate the file (in case it is a relative spec)
+ fileNameSpec = fileName
+ fileName = scriptutils.LocatePythonFile(fileName)
+ if fileName is None:
+ # Dont force update, so it replaces the idle prompt.
+ win32ui.SetStatusText("Cant locate the file '%s'" % (fileNameSpec), 0)
+ return 1
+
+ win32ui.SetStatusText("Jumping to line "+lineNoString+" of file "+fileName,1)
+ if not scriptutils.JumpToDocument(fileName, string.atoi(lineNoString)):
+ win32ui.SetStatusText("Could not open %s" % fileName)
+ return 1 # still was an error message.
+ return 1
+ if line[:11]=="com_error: ":
+ # An OLE Exception - pull apart the exception
+ # and try and locate a help file.
+ try:
+ import win32api, win32con
+ det = eval(string.strip(line[string.find(line,":")+1:]))
+ win32ui.SetStatusText("Opening help file on OLE error...");
+ win32api.WinHelp(win32ui.GetMainFrame().GetSafeHwnd(),det[2][3],win32con.HELP_CONTEXT, det[2][4])
+ return 1
+ except win32api.error, details:
+ try:
+ msg = details[2]
+ except:
+ msg = str(details)
+ win32ui.SetStatusText("The help file could not be opened - %s" % msg)
+ return 1
+ except:
+ win32ui.SetStatusText("Line is a COM error, but no WinHelp details can be parsed");
+ return 0 # not an error line
+ def write(self, msg):
+ return self.template.write(msg)
+ def writelines(self, lines):
+ for line in lines:
+ self.write(line)
+ def flush(self):
+ self.template.flush()
+
+class WindowOutputViewRTF(docview.RichEditView, WindowOutputViewImpl):
+ def __init__(self, doc):
+ docview.RichEditView.__init__(self, doc)
+ WindowOutputViewImpl.__init__(self)
+
+ def OnInitialUpdate(self):
+ WindowOutputViewImpl.OnInitialUpdate(self)
+ return docview.RichEditView.OnInitialUpdate(self)
+
+ def OnDestroy(self, msg):
+ WindowOutputViewImpl.OnDestroy(self, msg)
+ docview.RichEditView.OnDestroy(self, msg)
+
+ def HookHandlers(self):
+ WindowOutputViewImpl.HookHandlers(self)
+# docview.RichEditView.HookHandlers(self)
+
+ def RestoreKillBuffer(self):
+ if len(self.template.killBuffer):
+ self.StreamIn(win32con.SF_RTF, self._StreamRTFIn)
+ self.template.killBuffer = []
+
+ def SaveKillBuffer(self):
+ self.StreamOut(win32con.SF_RTFNOOBJS, self._StreamRTFOut)
+
+ def _StreamRTFOut(self, data):
+ self.template.killBuffer.append(data)
+ return 1 # keep em coming!
+
+ def _StreamRTFIn(self, bytes):
+ try:
+ item = self.template.killBuffer[0]
+ self.template.killBuffer.remove(item)
+ if bytes < len(item):
+ print "Warning - output buffer not big enough!"
+ return item
+ except IndexError:
+ return None
+ def dowrite(self, str):
+ self.SetSel(-2)
+ self.ReplaceSel(str)
+
+import pywin.scintilla.view
+class WindowOutputViewScintilla(pywin.scintilla.view.CScintillaView, WindowOutputViewImpl):
+ def __init__(self, doc):
+ pywin.scintilla.view.CScintillaView.__init__(self, doc)
+ WindowOutputViewImpl.__init__(self)
+
+ def OnInitialUpdate(self):
+ pywin.scintilla.view.CScintillaView.OnInitialUpdate(self)
+ self.SCISetMarginWidth(3)
+ WindowOutputViewImpl.OnInitialUpdate(self)
+
+ def OnDestroy(self, msg):
+ WindowOutputViewImpl.OnDestroy(self, msg)
+ pywin.scintilla.view.CScintillaView.OnDestroy(self, msg)
+
+ def HookHandlers(self):
+ WindowOutputViewImpl.HookHandlers(self)
+ pywin.scintilla.view.CScintillaView.HookHandlers(self)
+
+ def RestoreKillBuffer(self):
+ assert len(self.template.killBuffer) in [0,1], "Unexpected killbuffer contents"
+ if self.template.killBuffer:
+ self.SCIAddText(self.template.killBuffer[0])
+ self.template.killBuffer = []
+ def SaveKillBuffer(self):
+ self.template.killBuffer = [self.GetTextRange(0,-1)]
+ def dowrite(self, str):
+ end = self.GetTextLength()
+ atEnd = end==self.GetSel()[0]
+ self.SCIInsertText(str, end)
+ if atEnd:
+ self.SetSel(self.GetTextLength())
+
+ def _MakeColorizer(self):
+ return None # No colorizer for me!
+
+# win32ui.PumpWaitingMessages(0, -1)
+
+WindowOutputView = WindowOutputViewScintilla
+# The WindowOutput class is actually an MFC template. This is a conventient way of
+# making sure that my state can exist beyond the life of the windows themselves.
+# This is primarily to support the functionality of a WindowOutput window automatically
+# being recreated if necessary when written to.
+class WindowOutput(docview.DocTemplate):
+ """ Looks like a general Output Window - text can be written by the 'write' method.
+ Will auto-create itself on first write, and also on next write after being closed """
+ softspace=1
+ def __init__(self, title=None, defSize=None, queueing = flags.WQ_LINE, \
+ bAutoRestore = 1, style=None,
+ makeDoc = None, makeFrame = None, makeView = None):
+ """ init the output window -
+ Params
+ title=None -- What is the title of the window
+ defSize=None -- What is the default size for the window - if this
+ is a string, the size will be loaded from the ini file.
+ queueing = flags.WQ_LINE -- When should output be written
+ bAutoRestore=1 -- Should a minimized window be restored.
+ style -- Style for Window, or None for default.
+ makeDoc, makeFrame, makeView -- Classes for frame, view and window respectively.
+ """
+ if makeDoc is None: makeDoc = WindowOutputDocument
+ if makeFrame is None: makeFrame = WindowOutputFrame
+ if makeView is None: makeView = WindowOutputViewScintilla
+ docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, \
+ makeDoc, makeFrame, makeView)
+ self.SetDocStrings("\nOutput\n\n\n\n\n\n")
+ win32ui.GetApp().AddDocTemplate(self)
+ self.writeQueueing = queueing
+ self.errorCantRecreate = 0
+ self.killBuffer=[]
+ self.style = style
+ self.bAutoRestore = bAutoRestore
+ self.title = title
+ self.bCreating = 0
+ if type(defSize)==type(''): # is a string - maintain size pos from ini file.
+ self.iniSizeSection = defSize
+ self.defSize = app.LoadWindowSize(defSize)
+ self.loadedSize = self.defSize
+ else:
+ self.iniSizeSection = None
+ self.defSize=defSize
+ self.currentView = None
+ self.outputQueue = Queue.Queue(-1)
+ self.mainThreadId = win32api.GetCurrentThreadId()
+ self.idleHandlerSet = 0
+ self.SetIdleHandler()
+
+ def __del__(self):
+ self.Close()
+
+ def Create(self, title=None, style = None):
+ self.bCreating = 1
+ if title: self.title = title
+ if style: self.style = style
+ doc=self.OpenDocumentFile()
+ if doc is None: return
+ self.currentView = doc.GetFirstView()
+ self.bCreating = 0
+ if self.title: doc.SetTitle(self.title)
+
+ def Close(self):
+ self.RemoveIdleHandler()
+ try:
+ parent = self.currentView.GetParent()
+ except (AttributeError, win32ui.error): # Already closed
+ return
+ parent.DestroyWindow()
+
+ def SetTitle(self, title):
+ self.title = title
+ if self.currentView: self.currentView.GetDocument().SetTitle(self.title)
+
+ def OnViewDestroy(self, view):
+ self.currentView.SaveKillBuffer()
+ self.currentView = None
+
+ def OnFrameDestroy(self, frame):
+ if self.iniSizeSection:
+ # use GetWindowPlacement(), as it works even when min'd or max'd
+ newSize = frame.GetWindowPlacement()[4]
+ if self.loadedSize!=newSize:
+ app.SaveWindowSize(self.iniSizeSection, newSize)
+
+ def SetIdleHandler(self):
+ if not self.idleHandlerSet:
+ debug("Idle handler set\n")
+ win32ui.GetApp().AddIdleHandler(self.QueueIdleHandler)
+ self.idleHandlerSet = 1
+
+ def RemoveIdleHandler(self):
+ if self.idleHandlerSet:
+ debug("Idle handler reset\n")
+ if (win32ui.GetApp().DeleteIdleHandler(self.QueueIdleHandler)==0):
+ debug('Error deleting idle handler\n')
+ self.idleHandlerSet = 0
+
+ def RecreateWindow(self):
+ if self.errorCantRecreate:
+ debug("Error = not trying again")
+ return 0
+ try:
+ # This will fail if app shutting down
+ win32ui.GetMainFrame().GetSafeHwnd()
+ self.Create()
+ return 1
+ except (win32ui.error, AttributeError):
+ self.errorCantRecreate = 1
+ debug("Winout can not recreate the Window!\n")
+ return 0
+
+ # this handles the idle message, and does the printing.
+ def QueueIdleHandler(self,handler,count):
+ if self.outputQueue.empty():
+ return 0
+ try:
+ self.QueueFlush(20)
+ except KeyboardInterrupt:
+ while 1:
+ try:
+ self.outputQueue.get(0)
+ except Queue.Empty:
+ break
+ print "Interrupted."
+ return 0
+ return 1 # More handling until queue empty
+
+ # Returns true if the Window needs to be recreated.
+ def NeedRecreateWindow(self):
+ try:
+ if self.currentView is not None and self.currentView.IsWindow():
+ return 0
+ except (win32ui.error, AttributeError): # Attribute error if the win32ui object has died.
+ pass
+ return 1
+
+ # Returns true if the Window is OK (either cos it was, or because it was recreated
+ def CheckRecreateWindow(self):
+ if self.bCreating: return 0
+ if not self.NeedRecreateWindow():
+ return 1
+ if self.bAutoRestore:
+ if self.RecreateWindow():
+ return 1
+ return 0
+
+ def QueueFlush(self, max = sys.maxint):
+ if not self.CheckRecreateWindow():
+ return
+ items = []
+ while max > 0:
+ try:
+ items.append(self.outputQueue.get_nowait())
+ except Queue.Empty:
+ break
+ max = max - 1
+
+ self.currentView.dowrite(string.join(items,''))
+
+ def HandleOutput(self,message):
+ debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message ))
+ self.outputQueue.put(message)
+ if win32api.GetCurrentThreadId() != self.mainThreadId:
+ debug("not my thread - ignoring queue options!\n")
+ elif self.writeQueueing==flags.WQ_LINE:
+ pos = string.rfind(message, '\n')
+ if pos>0:
+ debug("Line queueing - forcing flush\n")
+ return self.QueueFlush()
+ elif self.writeQueueing==flags.WQ_NONE:
+ debug("WQ_NONE - flushing!\n")
+ return self.QueueFlush()
+ # Let our idle handler get it - wake it up
+ try:
+ win32ui.GetMainFrame().PostMessage(win32con.WM_USER) # Kick main thread off.
+ except win32ui.error:
+ # This can happen as the app is shutting down, and possibly
+ # if the event queue is full - either way, we ignore!
+ win32api.OutputDebugString(message)
+
+ # delegate certain fns to my view.
+ def writelines(self, lines):
+ for line in lines:
+ self.write(line)
+
+ def write(self,message):
+ self.HandleOutput(message)
+
+ def flush(self):
+ self.QueueFlush()
+
+ def HandleSpecialLine(self):
+ self.currentView.HandleSpecialLine()
+
+def RTFWindowOutput(*args, **kw):
+ kw['makeView'] = WindowOutputViewRTF
+ return apply( WindowOutput, args, kw )
+
+
+def thread_test(o):
+ for i in range(5):
+ o.write("Hi from thread %d\n" % (win32api.GetCurrentThreadId()))
+ win32api.Sleep(100)
+
+def test():
+ w = WindowOutput(queueing=flags.WQ_IDLE)
+ w.write("First bit of text\n")
+ import thread
+ for i in range(5):
+ w.write("Hello from the main thread\n")
+ thread.start_new(thread_test, (w,))
+ for i in range(2):
+ w.write("Hello from the main thread\n")
+ win32api.Sleep(50)
+ return w
+
+if __name__=='__main__':
+ test()
diff --git a/Pythonwin/pywin/idle/AutoExpand.py b/Pythonwin/pywin/idle/AutoExpand.py
new file mode 100644
index 0000000000..0d57be4205
--- /dev/null
+++ b/Pythonwin/pywin/idle/AutoExpand.py
@@ -0,0 +1,92 @@
+import string
+import re
+
+###$ event <>
+###$ win
+###$ unix
+
+class AutoExpand:
+
+ keydefs = {
+ '<>': [''],
+ }
+
+ unix_keydefs = {
+ '<>': [''],
+ }
+
+ menudefs = [
+ ('edit', [
+ ('E_xpand word', '<>'),
+ ]),
+ ]
+
+ wordchars = string.letters + string.digits + "_"
+
+ def __init__(self, editwin):
+ self.text = editwin.text
+ self.text.wordlist = None # XXX what is this?
+ self.state = None
+
+ def expand_word_event(self, event):
+ curinsert = self.text.index("insert")
+ curline = self.text.get("insert linestart", "insert lineend")
+ if not self.state:
+ words = self.getwords()
+ index = 0
+ else:
+ words, index, insert, line = self.state
+ if insert != curinsert or line != curline:
+ words = self.getwords()
+ index = 0
+ if not words:
+ self.text.bell()
+ return "break"
+ word = self.getprevword()
+ self.text.delete("insert - %d chars" % len(word), "insert")
+ newword = words[index]
+ index = (index + 1) % len(words)
+ if index == 0:
+ self.text.bell() # Warn we cycled around
+ self.text.insert("insert", newword)
+ curinsert = self.text.index("insert")
+ curline = self.text.get("insert linestart", "insert lineend")
+ self.state = words, index, curinsert, curline
+ return "break"
+
+ def getwords(self):
+ word = self.getprevword()
+ if not word:
+ return []
+ before = self.text.get("1.0", "insert wordstart")
+ wbefore = re.findall(r"\b" + word + r"\w+\b", before)
+ del before
+ after = self.text.get("insert wordend", "end")
+ wafter = re.findall(r"\b" + word + r"\w+\b", after)
+ del after
+ if not wbefore and not wafter:
+ return []
+ words = []
+ dict = {}
+ # search backwards through words before
+ wbefore.reverse()
+ for w in wbefore:
+ if dict.get(w):
+ continue
+ words.append(w)
+ dict[w] = w
+ # search onwards through words after
+ for w in wafter:
+ if dict.get(w):
+ continue
+ words.append(w)
+ dict[w] = w
+ words.append(word)
+ return words
+
+ def getprevword(self):
+ line = self.text.get("insert linestart", "insert")
+ i = len(line)
+ while i > 0 and line[i-1] in self.wordchars:
+ i = i-1
+ return line[i:]
diff --git a/Pythonwin/pywin/idle/__init__.py b/Pythonwin/pywin/idle/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/idle/readme.txt b/Pythonwin/pywin/idle/readme.txt
new file mode 100644
index 0000000000..8ded39fc72
--- /dev/null
+++ b/Pythonwin/pywin/idle/readme.txt
@@ -0,0 +1,16 @@
+Pythonwin IDLE directory
+------------------------
+
+This directory contains IDLE extensions used by
+Pythonwin. In ALL cases, the files in this directory that also appear
+in the main IDLE directory should be indentical to the latest available
+for IDLE.
+
+Eg, If you have Python 1.5.2 installed, the files in this
+directory will be later than the IDLE versions. If you use IDLE from
+the CVS sources, then the files should be identical.
+
+Pythonwin will look for IDLE extensions first in this directory, then on
+the global sys.path. Thus, if you have IDLE installed and run it from
+the CVS sources, you may remove most of the extensions from this
+directory, and the latest CVS version will then be used.
diff --git a/Pythonwin/pywin/mfc/__init__.py b/Pythonwin/pywin/mfc/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Pythonwin/pywin/mfc/activex.py b/Pythonwin/pywin/mfc/activex.py
new file mode 100644
index 0000000000..27866f4b56
--- /dev/null
+++ b/Pythonwin/pywin/mfc/activex.py
@@ -0,0 +1,69 @@
+"""Support for ActiveX control hosting in Pythonwin.
+"""
+import win32ui, win32uiole, window
+import new
+
+class Control(window.Wnd):
+ """An ActiveX control base class. A new class must be derived from both
+ this class and the Events class. See the demos for more details.
+ """
+ def __init__(self):
+ self.__dict__["_dispobj_"] = None
+ window.Wnd.__init__(self)
+
+ def _GetControlCLSID( self ):
+ return self.CLSID
+ def _GetDispatchClass(self):
+ return self.default_interface
+ def _GetEventMap(self):
+ return self.default_source._dispid_to_func_
+
+ def CreateControl(self, windowTitle, style, rect, parent, id):
+ clsid = str(self._GetControlCLSID())
+ self.__dict__["_obj_"] = win32ui.CreateControl(clsid, windowTitle, style, rect, parent, id)
+ klass = self._GetDispatchClass()
+ dispobj = klass(win32uiole.GetIDispatchForWindow(self._obj_))
+ self.HookOleEvents()
+ self.__dict__["_dispobj_"] = dispobj
+
+ def HookOleEvents(self):
+ dict = self._GetEventMap()
+ for dispid, methodName in dict.items():
+ if hasattr(self, methodName):
+ self._obj_.HookOleEvent( getattr(self, methodName), dispid )
+
+ def __getattr__(self, attr):
+ # Delegate attributes to the windows and the Dispatch object for this class
+ try:
+ return window.Wnd.__getattr__(self, attr)
+ except AttributeError:
+ pass
+ return getattr(self._dispobj_, attr)
+ def __setattr__(self, attr, value):
+ if hasattr(self.__dict__, attr):
+ self.__dict__[attr] = value
+ return
+ try:
+ if self._dispobj_:
+ self._dispobj_.__setattr__(attr, value)
+ return
+ except AttributeError:
+ pass
+ self.__dict__[attr] = value
+
+def MakeControlClass( controlClass, name = None ):
+ """Given a CoClass in a generated .py file, this function will return a Class
+ object which can be used as an OCX control.
+
+ This function is used when you do not want to handle any events from the OCX
+ control. If you need events, then you should derive a class from both the
+ activex.Control class and the CoClass
+ """
+ if name is None:
+ name = controlClass.__name__
+ return new.classobj("OCX" + name, (Control, controlClass), {})
+
+def MakeControlInstance( controlClass, name = None ):
+ """As for MakeControlClass(), but returns an instance of the class.
+ """
+ return MakeControlClass(controlClass, name)()
diff --git a/Pythonwin/pywin/mfc/afxres.py b/Pythonwin/pywin/mfc/afxres.py
new file mode 100644
index 0000000000..e679f2e4b9
--- /dev/null
+++ b/Pythonwin/pywin/mfc/afxres.py
@@ -0,0 +1,490 @@
+# Generated by h2py from stdin
+TCS_MULTILINE = 0x0200
+CBRS_ALIGN_LEFT = 0x1000
+CBRS_ALIGN_TOP = 0x2000
+CBRS_ALIGN_RIGHT = 0x4000
+CBRS_ALIGN_BOTTOM = 0x8000
+CBRS_ALIGN_ANY = 0xF000
+CBRS_BORDER_LEFT = 0x0100
+CBRS_BORDER_TOP = 0x0200
+CBRS_BORDER_RIGHT = 0x0400
+CBRS_BORDER_BOTTOM = 0x0800
+CBRS_BORDER_ANY = 0x0F00
+CBRS_TOOLTIPS = 0x0010
+CBRS_FLYBY = 0x0020
+CBRS_FLOAT_MULTI = 0x0040
+CBRS_BORDER_3D = 0x0080
+CBRS_HIDE_INPLACE = 0x0008
+CBRS_SIZE_DYNAMIC = 0x0004
+CBRS_SIZE_FIXED = 0x0002
+CBRS_FLOATING = 0x0001
+CBRS_ORIENT_HORZ = (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM)
+CBRS_ORIENT_VERT = (CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT)
+CBRS_ORIENT_ANY = (CBRS_ORIENT_HORZ|CBRS_ORIENT_VERT)
+CBRS_ALL = 0xFFFF
+CBRS_NOALIGN = 0x00000000
+CBRS_LEFT = (CBRS_ALIGN_LEFT|CBRS_BORDER_RIGHT)
+CBRS_TOP = (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM)
+CBRS_RIGHT = (CBRS_ALIGN_RIGHT|CBRS_BORDER_LEFT)
+CBRS_BOTTOM = (CBRS_ALIGN_BOTTOM|CBRS_BORDER_TOP)
+ID_INDICATOR_EXT = 0xE700
+ID_INDICATOR_CAPS = 0xE701
+ID_INDICATOR_NUM = 0xE702
+ID_INDICATOR_SCRL = 0xE703
+ID_INDICATOR_OVR = 0xE704
+ID_INDICATOR_REC = 0xE705
+ID_INDICATOR_KANA = 0xE706
+ID_SEPARATOR = 0
+AFX_IDW_CONTROLBAR_FIRST = 0xE800
+AFX_IDW_CONTROLBAR_LAST = 0xE8FF
+AFX_IDW_TOOLBAR = 0xE800
+AFX_IDW_STATUS_BAR = 0xE801
+AFX_IDW_PREVIEW_BAR = 0xE802
+AFX_IDW_RESIZE_BAR = 0xE803
+AFX_IDW_DOCKBAR_TOP = 0xE81B
+AFX_IDW_DOCKBAR_LEFT = 0xE81C
+AFX_IDW_DOCKBAR_RIGHT = 0xE81D
+AFX_IDW_DOCKBAR_BOTTOM = 0xE81E
+AFX_IDW_DOCKBAR_FLOAT = 0xE81F
+def AFX_CONTROLBAR_MASK(nIDC): return (1L << (nIDC - AFX_IDW_CONTROLBAR_FIRST))
+
+AFX_IDW_PANE_FIRST = 0xE900
+AFX_IDW_PANE_LAST = 0xE9ff
+AFX_IDW_HSCROLL_FIRST = 0xEA00
+AFX_IDW_VSCROLL_FIRST = 0xEA10
+AFX_IDW_SIZE_BOX = 0xEA20
+AFX_IDW_PANE_SAVE = 0xEA21
+AFX_IDS_APP_TITLE = 0xE000
+AFX_IDS_IDLEMESSAGE = 0xE001
+AFX_IDS_HELPMODEMESSAGE = 0xE002
+AFX_IDS_APP_TITLE_EMBEDDING = 0xE003
+AFX_IDS_COMPANY_NAME = 0xE004
+AFX_IDS_OBJ_TITLE_INPLACE = 0xE005
+ID_FILE_NEW = 0xE100
+ID_FILE_OPEN = 0xE101
+ID_FILE_CLOSE = 0xE102
+ID_FILE_SAVE = 0xE103
+ID_FILE_SAVE_AS = 0xE104
+ID_FILE_PAGE_SETUP = 0xE105
+ID_FILE_PRINT_SETUP = 0xE106
+ID_FILE_PRINT = 0xE107
+ID_FILE_PRINT_DIRECT = 0xE108
+ID_FILE_PRINT_PREVIEW = 0xE109
+ID_FILE_UPDATE = 0xE10A
+ID_FILE_SAVE_COPY_AS = 0xE10B
+ID_FILE_SEND_MAIL = 0xE10C
+ID_FILE_MRU_FIRST = 0xE110
+ID_FILE_MRU_FILE1 = 0xE110
+ID_FILE_MRU_FILE2 = 0xE111
+ID_FILE_MRU_FILE3 = 0xE112
+ID_FILE_MRU_FILE4 = 0xE113
+ID_FILE_MRU_FILE5 = 0xE114
+ID_FILE_MRU_FILE6 = 0xE115
+ID_FILE_MRU_FILE7 = 0xE116
+ID_FILE_MRU_FILE8 = 0xE117
+ID_FILE_MRU_FILE9 = 0xE118
+ID_FILE_MRU_FILE10 = 0xE119
+ID_FILE_MRU_FILE11 = 0xE11A
+ID_FILE_MRU_FILE12 = 0xE11B
+ID_FILE_MRU_FILE13 = 0xE11C
+ID_FILE_MRU_FILE14 = 0xE11D
+ID_FILE_MRU_FILE15 = 0xE11E
+ID_FILE_MRU_FILE16 = 0xE11F
+ID_FILE_MRU_LAST = 0xE11F
+ID_EDIT_CLEAR = 0xE120
+ID_EDIT_CLEAR_ALL = 0xE121
+ID_EDIT_COPY = 0xE122
+ID_EDIT_CUT = 0xE123
+ID_EDIT_FIND = 0xE124
+ID_EDIT_PASTE = 0xE125
+ID_EDIT_PASTE_LINK = 0xE126
+ID_EDIT_PASTE_SPECIAL = 0xE127
+ID_EDIT_REPEAT = 0xE128
+ID_EDIT_REPLACE = 0xE129
+ID_EDIT_SELECT_ALL = 0xE12A
+ID_EDIT_UNDO = 0xE12B
+ID_EDIT_REDO = 0xE12C
+ID_WINDOW_NEW = 0xE130
+ID_WINDOW_ARRANGE = 0xE131
+ID_WINDOW_CASCADE = 0xE132
+ID_WINDOW_TILE_HORZ = 0xE133
+ID_WINDOW_TILE_VERT = 0xE134
+ID_WINDOW_SPLIT = 0xE135
+AFX_IDM_WINDOW_FIRST = 0xE130
+AFX_IDM_WINDOW_LAST = 0xE13F
+AFX_IDM_FIRST_MDICHILD = 0xFF00
+ID_APP_ABOUT = 0xE140
+ID_APP_EXIT = 0xE141
+ID_HELP_INDEX = 0xE142
+ID_HELP_FINDER = 0xE143
+ID_HELP_USING = 0xE144
+ID_CONTEXT_HELP = 0xE145
+ID_HELP = 0xE146
+ID_DEFAULT_HELP = 0xE147
+ID_NEXT_PANE = 0xE150
+ID_PREV_PANE = 0xE151
+ID_FORMAT_FONT = 0xE160
+ID_OLE_INSERT_NEW = 0xE200
+ID_OLE_EDIT_LINKS = 0xE201
+ID_OLE_EDIT_CONVERT = 0xE202
+ID_OLE_EDIT_CHANGE_ICON = 0xE203
+ID_OLE_EDIT_PROPERTIES = 0xE204
+ID_OLE_VERB_FIRST = 0xE210
+ID_OLE_VERB_LAST = 0xE21F
+AFX_ID_PREVIEW_CLOSE = 0xE300
+AFX_ID_PREVIEW_NUMPAGE = 0xE301
+AFX_ID_PREVIEW_NEXT = 0xE302
+AFX_ID_PREVIEW_PREV = 0xE303
+AFX_ID_PREVIEW_PRINT = 0xE304
+AFX_ID_PREVIEW_ZOOMIN = 0xE305
+AFX_ID_PREVIEW_ZOOMOUT = 0xE306
+ID_VIEW_TOOLBAR = 0xE800
+ID_VIEW_STATUS_BAR = 0xE801
+ID_RECORD_FIRST = 0xE900
+ID_RECORD_LAST = 0xE901
+ID_RECORD_NEXT = 0xE902
+ID_RECORD_PREV = 0xE903
+IDC_STATIC = (-1)
+AFX_IDS_SCFIRST = 0xEF00
+AFX_IDS_SCSIZE = 0xEF00
+AFX_IDS_SCMOVE = 0xEF01
+AFX_IDS_SCMINIMIZE = 0xEF02
+AFX_IDS_SCMAXIMIZE = 0xEF03
+AFX_IDS_SCNEXTWINDOW = 0xEF04
+AFX_IDS_SCPREVWINDOW = 0xEF05
+AFX_IDS_SCCLOSE = 0xEF06
+AFX_IDS_SCRESTORE = 0xEF12
+AFX_IDS_SCTASKLIST = 0xEF13
+AFX_IDS_MDICHILD = 0xEF1F
+AFX_IDS_DESKACCESSORY = 0xEFDA
+AFX_IDS_OPENFILE = 0xF000
+AFX_IDS_SAVEFILE = 0xF001
+AFX_IDS_ALLFILTER = 0xF002
+AFX_IDS_UNTITLED = 0xF003
+AFX_IDS_SAVEFILECOPY = 0xF004
+AFX_IDS_PREVIEW_CLOSE = 0xF005
+AFX_IDS_UNNAMED_FILE = 0xF006
+AFX_IDS_ABOUT = 0xF010
+AFX_IDS_HIDE = 0xF011
+AFX_IDP_NO_ERROR_AVAILABLE = 0xF020
+AFX_IDS_NOT_SUPPORTED_EXCEPTION = 0xF021
+AFX_IDS_RESOURCE_EXCEPTION = 0xF022
+AFX_IDS_MEMORY_EXCEPTION = 0xF023
+AFX_IDS_USER_EXCEPTION = 0xF024
+AFX_IDS_PRINTONPORT = 0xF040
+AFX_IDS_ONEPAGE = 0xF041
+AFX_IDS_TWOPAGE = 0xF042
+AFX_IDS_PRINTPAGENUM = 0xF043
+AFX_IDS_PREVIEWPAGEDESC = 0xF044
+AFX_IDS_PRINTDEFAULTEXT = 0xF045
+AFX_IDS_PRINTDEFAULT = 0xF046
+AFX_IDS_PRINTFILTER = 0xF047
+AFX_IDS_PRINTCAPTION = 0xF048
+AFX_IDS_PRINTTOFILE = 0xF049
+AFX_IDS_OBJECT_MENUITEM = 0xF080
+AFX_IDS_EDIT_VERB = 0xF081
+AFX_IDS_ACTIVATE_VERB = 0xF082
+AFX_IDS_CHANGE_LINK = 0xF083
+AFX_IDS_AUTO = 0xF084
+AFX_IDS_MANUAL = 0xF085
+AFX_IDS_FROZEN = 0xF086
+AFX_IDS_ALL_FILES = 0xF087
+AFX_IDS_SAVE_MENU = 0xF088
+AFX_IDS_UPDATE_MENU = 0xF089
+AFX_IDS_SAVE_AS_MENU = 0xF08A
+AFX_IDS_SAVE_COPY_AS_MENU = 0xF08B
+AFX_IDS_EXIT_MENU = 0xF08C
+AFX_IDS_UPDATING_ITEMS = 0xF08D
+AFX_IDS_METAFILE_FORMAT = 0xF08E
+AFX_IDS_DIB_FORMAT = 0xF08F
+AFX_IDS_BITMAP_FORMAT = 0xF090
+AFX_IDS_LINKSOURCE_FORMAT = 0xF091
+AFX_IDS_EMBED_FORMAT = 0xF092
+AFX_IDS_PASTELINKEDTYPE = 0xF094
+AFX_IDS_UNKNOWNTYPE = 0xF095
+AFX_IDS_RTF_FORMAT = 0xF096
+AFX_IDS_TEXT_FORMAT = 0xF097
+AFX_IDS_INVALID_CURRENCY = 0xF098
+AFX_IDS_INVALID_DATETIME = 0xF099
+AFX_IDS_INVALID_DATETIMESPAN = 0xF09A
+AFX_IDP_INVALID_FILENAME = 0xF100
+AFX_IDP_FAILED_TO_OPEN_DOC = 0xF101
+AFX_IDP_FAILED_TO_SAVE_DOC = 0xF102
+AFX_IDP_ASK_TO_SAVE = 0xF103
+AFX_IDP_FAILED_TO_CREATE_DOC = 0xF104
+AFX_IDP_FILE_TOO_LARGE = 0xF105
+AFX_IDP_FAILED_TO_START_PRINT = 0xF106
+AFX_IDP_FAILED_TO_LAUNCH_HELP = 0xF107
+AFX_IDP_INTERNAL_FAILURE = 0xF108
+AFX_IDP_COMMAND_FAILURE = 0xF109
+AFX_IDP_FAILED_MEMORY_ALLOC = 0xF10A
+AFX_IDP_PARSE_INT = 0xF110
+AFX_IDP_PARSE_REAL = 0xF111
+AFX_IDP_PARSE_INT_RANGE = 0xF112
+AFX_IDP_PARSE_REAL_RANGE = 0xF113
+AFX_IDP_PARSE_STRING_SIZE = 0xF114
+AFX_IDP_PARSE_RADIO_BUTTON = 0xF115
+AFX_IDP_PARSE_BYTE = 0xF116
+AFX_IDP_PARSE_UINT = 0xF117
+AFX_IDP_PARSE_DATETIME = 0xF118
+AFX_IDP_PARSE_CURRENCY = 0xF119
+AFX_IDP_FAILED_INVALID_FORMAT = 0xF120
+AFX_IDP_FAILED_INVALID_PATH = 0xF121
+AFX_IDP_FAILED_DISK_FULL = 0xF122
+AFX_IDP_FAILED_ACCESS_READ = 0xF123
+AFX_IDP_FAILED_ACCESS_WRITE = 0xF124
+AFX_IDP_FAILED_IO_ERROR_READ = 0xF125
+AFX_IDP_FAILED_IO_ERROR_WRITE = 0xF126
+AFX_IDP_STATIC_OBJECT = 0xF180
+AFX_IDP_FAILED_TO_CONNECT = 0xF181
+AFX_IDP_SERVER_BUSY = 0xF182
+AFX_IDP_BAD_VERB = 0xF183
+AFX_IDP_FAILED_TO_NOTIFY = 0xF185
+AFX_IDP_FAILED_TO_LAUNCH = 0xF186
+AFX_IDP_ASK_TO_UPDATE = 0xF187
+AFX_IDP_FAILED_TO_UPDATE = 0xF188
+AFX_IDP_FAILED_TO_REGISTER = 0xF189
+AFX_IDP_FAILED_TO_AUTO_REGISTER = 0xF18A
+AFX_IDP_FAILED_TO_CONVERT = 0xF18B
+AFX_IDP_GET_NOT_SUPPORTED = 0xF18C
+AFX_IDP_SET_NOT_SUPPORTED = 0xF18D
+AFX_IDP_ASK_TO_DISCARD = 0xF18E
+AFX_IDP_FAILED_TO_CREATE = 0xF18F
+AFX_IDP_FAILED_MAPI_LOAD = 0xF190
+AFX_IDP_INVALID_MAPI_DLL = 0xF191
+AFX_IDP_FAILED_MAPI_SEND = 0xF192
+AFX_IDP_FILE_NONE = 0xF1A0
+AFX_IDP_FILE_GENERIC = 0xF1A1
+AFX_IDP_FILE_NOT_FOUND = 0xF1A2
+AFX_IDP_FILE_BAD_PATH = 0xF1A3
+AFX_IDP_FILE_TOO_MANY_OPEN = 0xF1A4
+AFX_IDP_FILE_ACCESS_DENIED = 0xF1A5
+AFX_IDP_FILE_INVALID_FILE = 0xF1A6
+AFX_IDP_FILE_REMOVE_CURRENT = 0xF1A7
+AFX_IDP_FILE_DIR_FULL = 0xF1A8
+AFX_IDP_FILE_BAD_SEEK = 0xF1A9
+AFX_IDP_FILE_HARD_IO = 0xF1AA
+AFX_IDP_FILE_SHARING = 0xF1AB
+AFX_IDP_FILE_LOCKING = 0xF1AC
+AFX_IDP_FILE_DISKFULL = 0xF1AD
+AFX_IDP_FILE_EOF = 0xF1AE
+AFX_IDP_ARCH_NONE = 0xF1B0
+AFX_IDP_ARCH_GENERIC = 0xF1B1
+AFX_IDP_ARCH_READONLY = 0xF1B2
+AFX_IDP_ARCH_ENDOFFILE = 0xF1B3
+AFX_IDP_ARCH_WRITEONLY = 0xF1B4
+AFX_IDP_ARCH_BADINDEX = 0xF1B5
+AFX_IDP_ARCH_BADCLASS = 0xF1B6
+AFX_IDP_ARCH_BADSCHEMA = 0xF1B7
+AFX_IDS_OCC_SCALEUNITS_PIXELS = 0xF1C0
+AFX_IDS_STATUS_FONT = 0xF230
+AFX_IDS_TOOLTIP_FONT = 0xF231
+AFX_IDS_UNICODE_FONT = 0xF232
+AFX_IDS_MINI_FONT = 0xF233
+AFX_IDP_SQL_FIRST = 0xF280
+AFX_IDP_SQL_CONNECT_FAIL = 0xF281
+AFX_IDP_SQL_RECORDSET_FORWARD_ONLY = 0xF282
+AFX_IDP_SQL_EMPTY_COLUMN_LIST = 0xF283
+AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH = 0xF284
+AFX_IDP_SQL_ILLEGAL_MODE = 0xF285
+AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED = 0xF286
+AFX_IDP_SQL_NO_CURRENT_RECORD = 0xF287
+AFX_IDP_SQL_NO_ROWS_AFFECTED = 0xF288
+AFX_IDP_SQL_RECORDSET_READONLY = 0xF289
+AFX_IDP_SQL_SQL_NO_TOTAL = 0xF28A
+AFX_IDP_SQL_ODBC_LOAD_FAILED = 0xF28B
+AFX_IDP_SQL_DYNASET_NOT_SUPPORTED = 0xF28C
+AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED = 0xF28D
+AFX_IDP_SQL_API_CONFORMANCE = 0xF28E
+AFX_IDP_SQL_SQL_CONFORMANCE = 0xF28F
+AFX_IDP_SQL_NO_DATA_FOUND = 0xF290
+AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED = 0xF291
+AFX_IDP_SQL_ODBC_V2_REQUIRED = 0xF292
+AFX_IDP_SQL_NO_POSITIONED_UPDATES = 0xF293
+AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED = 0xF294
+AFX_IDP_SQL_DATA_TRUNCATED = 0xF295
+AFX_IDP_SQL_ROW_FETCH = 0xF296
+AFX_IDP_SQL_INCORRECT_ODBC = 0xF297
+AFX_IDP_SQL_UPDATE_DELETE_FAILED = 0xF298
+AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED = 0xF299
+AFX_IDP_DAO_FIRST = 0xF2A0
+AFX_IDP_DAO_ENGINE_INITIALIZATION = 0xF2A0
+AFX_IDP_DAO_DFX_BIND = 0xF2A1
+AFX_IDP_DAO_OBJECT_NOT_OPEN = 0xF2A2
+AFX_IDP_DAO_ROWTOOSHORT = 0xF2A3
+AFX_IDP_DAO_BADBINDINFO = 0xF2A4
+AFX_IDP_DAO_COLUMNUNAVAILABLE = 0xF2A5
+AFX_IDC_LISTBOX = 100
+AFX_IDC_CHANGE = 101
+AFX_IDC_PRINT_DOCNAME = 201
+AFX_IDC_PRINT_PRINTERNAME = 202
+AFX_IDC_PRINT_PORTNAME = 203
+AFX_IDC_PRINT_PAGENUM = 204
+ID_APPLY_NOW = 0x3021
+ID_WIZBACK = 0x3023
+ID_WIZNEXT = 0x3024
+ID_WIZFINISH = 0x3025
+AFX_IDC_TAB_CONTROL = 0x3020
+AFX_IDD_FILEOPEN = 28676
+AFX_IDD_FILESAVE = 28677
+AFX_IDD_FONT = 28678
+AFX_IDD_COLOR = 28679
+AFX_IDD_PRINT = 28680
+AFX_IDD_PRINTSETUP = 28681
+AFX_IDD_FIND = 28682
+AFX_IDD_REPLACE = 28683
+AFX_IDD_NEWTYPEDLG = 30721
+AFX_IDD_PRINTDLG = 30722
+AFX_IDD_PREVIEW_TOOLBAR = 30723
+AFX_IDD_PREVIEW_SHORTTOOLBAR = 30731
+AFX_IDD_INSERTOBJECT = 30724
+AFX_IDD_CHANGEICON = 30725
+AFX_IDD_CONVERT = 30726
+AFX_IDD_PASTESPECIAL = 30727
+AFX_IDD_EDITLINKS = 30728
+AFX_IDD_FILEBROWSE = 30729
+AFX_IDD_BUSY = 30730
+AFX_IDD_OBJECTPROPERTIES = 30732
+AFX_IDD_CHANGESOURCE = 30733
+AFX_IDC_CONTEXTHELP = 30977
+AFX_IDC_MAGNIFY = 30978
+AFX_IDC_SMALLARROWS = 30979
+AFX_IDC_HSPLITBAR = 30980
+AFX_IDC_VSPLITBAR = 30981
+AFX_IDC_NODROPCRSR = 30982
+AFX_IDC_TRACKNWSE = 30983
+AFX_IDC_TRACKNESW = 30984
+AFX_IDC_TRACKNS = 30985
+AFX_IDC_TRACKWE = 30986
+AFX_IDC_TRACK4WAY = 30987
+AFX_IDC_MOVE4WAY = 30988
+AFX_IDB_MINIFRAME_MENU = 30994
+AFX_IDB_CHECKLISTBOX_NT = 30995
+AFX_IDB_CHECKLISTBOX_95 = 30996
+AFX_IDR_PREVIEW_ACCEL = 30997
+AFX_IDI_STD_MDIFRAME = 31233
+AFX_IDI_STD_FRAME = 31234
+AFX_IDC_FONTPROP = 1000
+AFX_IDC_FONTNAMES = 1001
+AFX_IDC_FONTSTYLES = 1002
+AFX_IDC_FONTSIZES = 1003
+AFX_IDC_STRIKEOUT = 1004
+AFX_IDC_UNDERLINE = 1005
+AFX_IDC_SAMPLEBOX = 1006
+AFX_IDC_COLOR_BLACK = 1100
+AFX_IDC_COLOR_WHITE = 1101
+AFX_IDC_COLOR_RED = 1102
+AFX_IDC_COLOR_GREEN = 1103
+AFX_IDC_COLOR_BLUE = 1104
+AFX_IDC_COLOR_YELLOW = 1105
+AFX_IDC_COLOR_MAGENTA = 1106
+AFX_IDC_COLOR_CYAN = 1107
+AFX_IDC_COLOR_GRAY = 1108
+AFX_IDC_COLOR_LIGHTGRAY = 1109
+AFX_IDC_COLOR_DARKRED = 1110
+AFX_IDC_COLOR_DARKGREEN = 1111
+AFX_IDC_COLOR_DARKBLUE = 1112
+AFX_IDC_COLOR_LIGHTBROWN = 1113
+AFX_IDC_COLOR_DARKMAGENTA = 1114
+AFX_IDC_COLOR_DARKCYAN = 1115
+AFX_IDC_COLORPROP = 1116
+AFX_IDC_SYSTEMCOLORS = 1117
+AFX_IDC_PROPNAME = 1201
+AFX_IDC_PICTURE = 1202
+AFX_IDC_BROWSE = 1203
+AFX_IDC_CLEAR = 1204
+AFX_IDD_PROPPAGE_COLOR = 32257
+AFX_IDD_PROPPAGE_FONT = 32258
+AFX_IDD_PROPPAGE_PICTURE = 32259
+AFX_IDB_TRUETYPE = 32384
+AFX_IDS_PROPPAGE_UNKNOWN = 0xFE01
+AFX_IDS_COLOR_DESKTOP = 0xFE04
+AFX_IDS_COLOR_APPWORKSPACE = 0xFE05
+AFX_IDS_COLOR_WNDBACKGND = 0xFE06
+AFX_IDS_COLOR_WNDTEXT = 0xFE07
+AFX_IDS_COLOR_MENUBAR = 0xFE08
+AFX_IDS_COLOR_MENUTEXT = 0xFE09
+AFX_IDS_COLOR_ACTIVEBAR = 0xFE0A
+AFX_IDS_COLOR_INACTIVEBAR = 0xFE0B
+AFX_IDS_COLOR_ACTIVETEXT = 0xFE0C
+AFX_IDS_COLOR_INACTIVETEXT = 0xFE0D
+AFX_IDS_COLOR_ACTIVEBORDER = 0xFE0E
+AFX_IDS_COLOR_INACTIVEBORDER = 0xFE0F
+AFX_IDS_COLOR_WNDFRAME = 0xFE10
+AFX_IDS_COLOR_SCROLLBARS = 0xFE11
+AFX_IDS_COLOR_BTNFACE = 0xFE12
+AFX_IDS_COLOR_BTNSHADOW = 0xFE13
+AFX_IDS_COLOR_BTNTEXT = 0xFE14
+AFX_IDS_COLOR_BTNHIGHLIGHT = 0xFE15
+AFX_IDS_COLOR_DISABLEDTEXT = 0xFE16
+AFX_IDS_COLOR_HIGHLIGHT = 0xFE17
+AFX_IDS_COLOR_HIGHLIGHTTEXT = 0xFE18
+AFX_IDS_REGULAR = 0xFE19
+AFX_IDS_BOLD = 0xFE1A
+AFX_IDS_ITALIC = 0xFE1B
+AFX_IDS_BOLDITALIC = 0xFE1C
+AFX_IDS_SAMPLETEXT = 0xFE1D
+AFX_IDS_DISPLAYSTRING_FONT = 0xFE1E
+AFX_IDS_DISPLAYSTRING_COLOR = 0xFE1F
+AFX_IDS_DISPLAYSTRING_PICTURE = 0xFE20
+AFX_IDS_PICTUREFILTER = 0xFE21
+AFX_IDS_PICTYPE_UNKNOWN = 0xFE22
+AFX_IDS_PICTYPE_NONE = 0xFE23
+AFX_IDS_PICTYPE_BITMAP = 0xFE24
+AFX_IDS_PICTYPE_METAFILE = 0xFE25
+AFX_IDS_PICTYPE_ICON = 0xFE26
+AFX_IDS_COLOR_PPG = 0xFE28
+AFX_IDS_COLOR_PPG_CAPTION = 0xFE29
+AFX_IDS_FONT_PPG = 0xFE2A
+AFX_IDS_FONT_PPG_CAPTION = 0xFE2B
+AFX_IDS_PICTURE_PPG = 0xFE2C
+AFX_IDS_PICTURE_PPG_CAPTION = 0xFE2D
+AFX_IDS_PICTUREBROWSETITLE = 0xFE30
+AFX_IDS_BORDERSTYLE_0 = 0xFE31
+AFX_IDS_BORDERSTYLE_1 = 0xFE32
+AFX_IDS_VERB_EDIT = 0xFE40
+AFX_IDS_VERB_PROPERTIES = 0xFE41
+AFX_IDP_PICTURECANTOPEN = 0xFE83
+AFX_IDP_PICTURECANTLOAD = 0xFE84
+AFX_IDP_PICTURETOOLARGE = 0xFE85
+AFX_IDP_PICTUREREADFAILED = 0xFE86
+AFX_IDP_E_ILLEGALFUNCTIONCALL = 0xFEA0
+AFX_IDP_E_OVERFLOW = 0xFEA1
+AFX_IDP_E_OUTOFMEMORY = 0xFEA2
+AFX_IDP_E_DIVISIONBYZERO = 0xFEA3
+AFX_IDP_E_OUTOFSTRINGSPACE = 0xFEA4
+AFX_IDP_E_OUTOFSTACKSPACE = 0xFEA5
+AFX_IDP_E_BADFILENAMEORNUMBER = 0xFEA6
+AFX_IDP_E_FILENOTFOUND = 0xFEA7
+AFX_IDP_E_BADFILEMODE = 0xFEA8
+AFX_IDP_E_FILEALREADYOPEN = 0xFEA9
+AFX_IDP_E_DEVICEIOERROR = 0xFEAA
+AFX_IDP_E_FILEALREADYEXISTS = 0xFEAB
+AFX_IDP_E_BADRECORDLENGTH = 0xFEAC
+AFX_IDP_E_DISKFULL = 0xFEAD
+AFX_IDP_E_BADRECORDNUMBER = 0xFEAE
+AFX_IDP_E_BADFILENAME = 0xFEAF
+AFX_IDP_E_TOOMANYFILES = 0xFEB0
+AFX_IDP_E_DEVICEUNAVAILABLE = 0xFEB1
+AFX_IDP_E_PERMISSIONDENIED = 0xFEB2
+AFX_IDP_E_DISKNOTREADY = 0xFEB3
+AFX_IDP_E_PATHFILEACCESSERROR = 0xFEB4
+AFX_IDP_E_PATHNOTFOUND = 0xFEB5
+AFX_IDP_E_INVALIDPATTERNSTRING = 0xFEB6
+AFX_IDP_E_INVALIDUSEOFNULL = 0xFEB7
+AFX_IDP_E_INVALIDFILEFORMAT = 0xFEB8
+AFX_IDP_E_INVALIDPROPERTYVALUE = 0xFEB9
+AFX_IDP_E_INVALIDPROPERTYARRAYINDEX = 0xFEBA
+AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME = 0xFEBB
+AFX_IDP_E_SETNOTSUPPORTED = 0xFEBC
+AFX_IDP_E_NEEDPROPERTYARRAYINDEX = 0xFEBD
+AFX_IDP_E_SETNOTPERMITTED = 0xFEBE
+AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME = 0xFEBF
+AFX_IDP_E_GETNOTSUPPORTED = 0xFEC0
+AFX_IDP_E_PROPERTYNOTFOUND = 0xFEC1
+AFX_IDP_E_INVALIDCLIPBOARDFORMAT = 0xFEC2
+AFX_IDP_E_INVALIDPICTURE = 0xFEC3
+AFX_IDP_E_PRINTERERROR = 0xFEC4
+AFX_IDP_E_CANTSAVEFILETOTEMP = 0xFEC5
+AFX_IDP_E_SEARCHTEXTNOTFOUND = 0xFEC6
+AFX_IDP_E_REPLACEMENTSTOOLONG = 0xFEC7
diff --git a/Pythonwin/pywin/mfc/dialog.py b/Pythonwin/pywin/mfc/dialog.py
new file mode 100644
index 0000000000..3db9548ded
--- /dev/null
+++ b/Pythonwin/pywin/mfc/dialog.py
@@ -0,0 +1,255 @@
+""" \
+Base class for Dialogs. Also contains a few useful utility functions
+"""
+# dialog.py
+# Python class for Dialog Boxes in PythonWin.
+
+import win32ui
+import win32con
+import window
+
+def dllFromDll(dllid):
+ " given a 'dll' (maybe a dll, filename, etc), return a DLL object "
+ if dllid==None:
+ return None
+ elif type('')==type(dllid):
+ return win32ui.LoadLibrary(dllid)
+ else:
+ try:
+ dllid.GetFileName()
+ except AttributeError:
+ raise TypeError, "DLL parameter must be None, a filename or a dll object"
+ return dllid
+
+class Dialog(window.Wnd):
+ " Base class for a dialog"
+ def __init__( self, id, dllid=None ):
+ """ id is the resource ID, or a template
+ dllid may be None, a dll object, or a string with a dll name """
+ # must take a reference to the DLL until InitDialog.
+ self.dll=dllFromDll(dllid)
+ if type(id)==type([]): # a template
+ dlg=win32ui.CreateDialogIndirect(id)
+ else:
+ dlg=win32ui.CreateDialog(id, self.dll)
+ window.Wnd.__init__(self, dlg)
+ self.HookCommands()
+ self.bHaveInit = None
+
+ def HookCommands(self):
+ pass
+
+ def OnAttachedObjectDeath(self):
+ self.data = self._obj_.data
+ window.Wnd.OnAttachedObjectDeath(self)
+
+ # provide virtuals.
+ def OnOK(self):
+ self._obj_.OnOK()
+ def OnCancel(self):
+ self._obj_.OnCancel()
+ def OnInitDialog(self):
+ self.bHaveInit = 1
+ if self._obj_.data:
+ self._obj_.UpdateData(0)
+ return 1 # I did NOT set focus to a child window.
+ def OnDestroy(self,msg):
+ self.dll = None # theoretically not needed if object destructs normally.
+ # DDX support
+ def AddDDX( self, *args ):
+ self._obj_.datalist.append(args)
+ # Make a dialog object look like a dictionary for the DDX support
+ def __nonzero__(self):
+ return 1
+ def __len__(self): return len(self.data)
+ def __getitem__(self, key): return self.data[key]
+ def __setitem__(self, key, item): self._obj_.data[key] = item# self.UpdateData(0)
+ def keys(self): return self.data.keys()
+ def items(self): return self.data.items()
+ def values(self): return self.data.values()
+ def has_key(self, key): return self.data.has_key(key)
+
+class PrintDialog(Dialog):
+ " Base class for a print dialog"
+ def __init__(self, pInfo, dlgID,
+ printSetupOnly = 0,
+ flags=(win32ui.PD_ALLPAGES|
+ win32ui.PD_USEDEVMODECOPIES|
+ win32ui.PD_NOPAGENUMS|
+ win32ui.PD_HIDEPRINTTOFILE|
+ win32ui.PD_NOSELECTION),
+ parent=None,
+ dllid=None):
+ self.dll=dllFromDll(dllid)
+ if type(dlgID)==type([]): # a template
+ raise TypeError, "dlgID parameter must be an integer resource ID"
+ else:
+ dlg=win32ui.CreatePrintDialog(dlgID, printSetupOnly,
+ flags, parent,
+ self.dll)
+ window.Wnd.__init__(self, dlg)
+ self.HookCommands()
+ self.bHaveInit = None
+ self.pInfo = pInfo
+ # init values (if PrintSetup is called, values still available)
+ flags = pInfo.GetFlags()
+ self['toFile'] = (flags&win32ui.PD_PRINTTOFILE != 0)
+ self['direct'] = pInfo.GetDirect()
+ self['preview'] = pInfo.GetPreview()
+ self['continuePrinting'] = pInfo.GetContinuePrinting()
+ self['curPage'] = pInfo.GetCurPage()
+ self['numPreviewPages'] = pInfo.GetNumPreviewPages()
+ self['userData'] = pInfo.GetUserData()
+ self['draw'] = pInfo.GetDraw()
+ self['pageDesc'] = pInfo.GetPageDesc()
+ self['minPage'] = pInfo.GetMinPage()
+ self['maxPage'] = pInfo.GetMaxPage()
+ self['offsetPage'] = pInfo.GetOffsetPage()
+ self['fromPage'] = pInfo.GetFromPage()
+ self['toPage'] = pInfo.GetToPage()
+ # these values updated after OnOK
+ self['copies'] = 0
+ self['deviceName'] = ''
+ self['driverName'] = ''
+ self['printAll'] = 0
+ self['printCollate'] = 0
+ self['printRange'] = 0
+ self['printSelection'] = 0
+
+ def OnInitDialog(self):
+ self.pInfo.SetHDC(self.pInfo.CreatePrinterDC())
+
+ def OnCancel(self):
+ del self.pInfo
+
+ def OnOK(self):
+ '''DoModal has finished. Can now access the users choices'''
+ self._obj_.OnOK()
+ pInfo = self.pInfo
+ # user values
+ flags = pInfo.GetFlags()
+ self['toFile'] = (flags&win32ui.PD_PRINTTOFILE != 0)
+ self['direct'] = pInfo.GetDirect()
+ self['preview'] = pInfo.GetPreview()
+ self['continuePrinting'] = pInfo.GetContinuePrinting()
+ self['curPage'] = pInfo.GetCurPage()
+ self['numPreviewPages'] = pInfo.GetNumPreviewPages()
+ self['userData'] = pInfo.GetUserData()
+ self['draw'] = pInfo.GetDraw()
+ self['pageDesc'] = pInfo.GetPageDesc()
+ self['minPage'] = pInfo.GetMinPage()
+ self['maxPage'] = pInfo.GetMaxPage()
+ self['offsetPage'] = pInfo.GetOffsetPage()
+ self['fromPage'] = pInfo.GetFromPage()
+ self['toPage'] = pInfo.GetToPage()
+ self['copies'] = pInfo.GetCopies()
+ self['deviceName'] = pInfo.GetDeviceName()
+ self['driverName'] = pInfo.GetDriverName()
+ self['printAll'] = pInfo.PrintAll()
+ self['printCollate'] = pInfo.PrintCollate()
+ self['printRange'] = pInfo.PrintRange()
+ self['printSelection'] = pInfo.PrintSelection()
+ del self.pInfo
+
+class PropertyPage(Dialog):
+ " Base class for a Property Page"
+ def __init__( self, id, dllid=None, caption=0 ):
+ """ id is the resource ID
+ dllid may be None, a dll object, or a string with a dll name """
+
+ self.dll = dllFromDll(dllid)
+ if self.dll:
+ oldRes = win32ui.SetResource(self.dll)
+ if type(id)==type([]):
+ dlg=win32ui.CreatePropertyPageIndirect(id)
+ else:
+ dlg=win32ui.CreatePropertyPage(id, caption)
+ if self.dll:
+ win32ui.SetResource(oldRes)
+ # dont call dialog init!
+ window.Wnd.__init__(self, dlg)
+ self.HookCommands()
+
+ # DDX support
+# def AddDDX( self, *args ):
+# self._obj_.datalist.append(args)
+ # Make a property page object look like a dictionary for the DDX support
+# def __nonzero__(self):
+# return 1
+# def __len__(self): return len(self._obj_.data)
+# def __getitem__(self, key): return self._obj_.data[key]
+# def __setitem__(self, key, item): self._obj_.data[key] = item # self.sheet.UpdateData(0)
+# def __delitem__(self, key): del self._obj_.data[key]
+# def keys(self): return self._obj_.data.keys()
+# def items(self): return self._obj_.data.items()
+# def values(self): return self._obj_.data.values()
+# def has_key(self, key): return self._obj_.data.has_key(key)
+
+class PropertySheet(window.Wnd):
+ def __init__(self, caption, dll=None, pageList=None ):# parent=None, style,etc):
+ " Initialize a property sheet. pageList is a list of ID's "
+ # must take a reference to the DLL until InitDialog.
+ self.dll=dllFromDll(dll)
+ self.sheet = win32ui.CreatePropertySheet(caption)
+ window.Wnd.__init__(self, self.sheet)
+ if not pageList is None:
+ self.AddPage(pageList)
+
+ def OnInitDialog(self):
+ return self._obj_.OnInitDialog()
+
+ def DoModal(self):
+ if self.dll:
+ oldRes = win32ui.SetResource(self.dll)
+ rc = self.sheet.DoModal()
+ if self.dll:
+ win32ui.SetResource(oldRes)
+ return rc
+
+ def AddPage(self, pages):
+ if self.dll:
+ oldRes = win32ui.SetResource(self.dll)
+ try: # try list style access
+ pages[0]
+ isSeq = 1
+ except (TypeError,KeyError):
+ isSeq = 0
+ if isSeq:
+ for page in pages:
+ self.DoAddSinglePage(page)
+ else:
+ self.DoAddSinglePage(pages)
+ if self.dll:
+ win32ui.SetResource(oldRes)
+
+ def DoAddSinglePage(self, page):
+ "Page may be page, or int ID. Assumes DLL setup "
+ if type(page)==type(0):
+ self.sheet.AddPage(win32ui.CreatePropertyPage(page))
+ else:
+ self.sheet.AddPage(page)
+
+# define some app utility functions.
+def GetSimpleInput(prompt, defValue='', title=None ):
+ """ displays a dialog, and returns a string, or None if cancelled.
+ args prompt, defValue='', title=main frames title """
+ # uses a simple dialog to return a string object.
+ if title is None: title=win32ui.GetMainFrame().GetWindowText()
+ class DlgSimpleInput(Dialog):
+ def __init__(self, prompt, defValue, title ):
+ self.title=title
+ Dialog.__init__(self, win32ui.IDD_SIMPLE_INPUT)
+ self.AddDDX(win32ui.IDC_EDIT1,'result')
+ self.AddDDX(win32ui.IDC_PROMPT1, 'prompt')
+ self._obj_.data['result']=defValue
+ self._obj_.data['prompt']=prompt
+ def OnInitDialog(self):
+ self.SetWindowText(self.title)
+ return Dialog.OnInitDialog(self)
+
+ dlg=DlgSimpleInput( prompt, defValue, title)
+ if dlg.DoModal() <> win32con.IDOK:
+ return None
+ return dlg['result']
+
+
diff --git a/Pythonwin/pywin/mfc/docview.py b/Pythonwin/pywin/mfc/docview.py
new file mode 100644
index 0000000000..ba4971de0e
--- /dev/null
+++ b/Pythonwin/pywin/mfc/docview.py
@@ -0,0 +1,122 @@
+# document and view classes for MFC.
+import win32ui
+import win32con
+import object
+import window
+
+class View(window.Wnd):
+ def __init__(self, initobj):
+ window.Wnd.__init__(self, initobj)
+ def OnInitialUpdate(self):
+ pass
+
+# Simple control based views.
+class CtrlView(View):
+ def __init__(self, doc, wndclass, style=0):
+ View.__init__(self, win32ui.CreateCtrlView(doc, wndclass, style))
+
+class EditView(CtrlView):
+ def __init__(self, doc):
+ View.__init__(self, win32ui.CreateEditView(doc))
+
+class RichEditView(CtrlView):
+ def __init__(self, doc):
+ View.__init__(self, win32ui.CreateRichEditView(doc))
+
+class ListView(CtrlView):
+ def __init__(self, doc):
+ View.__init__(self, win32ui.CreateListView(doc))
+
+class TreeView(CtrlView):
+ def __init__(self, doc):
+ View.__init__(self, win32ui.CreateTreeView(doc))
+
+# Other more advanced views.
+class ScrollView(View):
+ def __init__(self, doc):
+ View.__init__(self, win32ui.CreateView(doc))
+
+class FormView(View):
+ def __init__(self, doc, id):
+ View.__init__(self, win32ui.CreateFormView(doc, id))
+
+class Document(object.CmdTarget):
+ def __init__(self, template, docobj=None):
+ if docobj is None:
+ docobj = template.DoCreateDoc()
+ object.CmdTarget.__init__(self, docobj)
+
+class RichEditDoc(object.CmdTarget):
+ def __init__(self, template):
+ object.CmdTarget.__init__(self, template.DoCreateRichEditDoc())
+
+class CreateContext:
+ "A transient base class used as a CreateContext"
+ def __init__(self, template, doc = None):
+ self.template = template
+ self.doc = doc
+ def __del__(self):
+ self.close()
+ def close(self):
+ self.doc = None
+ self.template = None
+
+class DocTemplate(object.CmdTarget):
+ def __init__(self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None):
+ if resourceId is None: resourceId = win32ui.IDR_PYTHONTYPE
+ object.CmdTarget.__init__(self, self._CreateDocTemplate(resourceId))
+ self.MakeDocument=MakeDocument
+ self.MakeFrame=MakeFrame
+ self.MakeView=MakeView
+ self._SetupSharedMenu_()
+# todo - _SetupSharedMenu should be moved to a framework class.
+ def _SetupSharedMenu_(self):
+ sharedMenu = self.GetSharedMenu()
+ from pywin.framework import toolmenu
+ toolmenu.SetToolsMenu(sharedMenu)
+ from pywin.framework import help
+ help.SetHelpMenuOtherHelp(sharedMenu)
+
+ def _CreateDocTemplate(self, resourceId):
+ return win32ui.CreateDocTemplate(resourceId)
+ def __del__(self):
+ object.CmdTarget.__del__(self)
+ def CreateCreateContext(self, doc=None):
+ return CreateContext(self, doc)
+ def CreateNewFrame(self, doc):
+ makeFrame = self.MakeFrame
+ if makeFrame is None: makeFrame = window.MDIChildWnd
+ wnd = makeFrame()
+ context = self.CreateCreateContext(doc)
+ wnd.LoadFrame(self.GetResourceID(), -1, None, context) # triggers OnCreateClient...
+ return wnd
+ def CreateNewDocument(self):
+ makeDocument = self.MakeDocument
+ if makeDocument is None:
+ makeDocument = Document
+ return makeDocument(self)
+ def CreateView(self, frame, context):
+ makeView = self.MakeView
+ if makeView is None: makeView = EditView
+ view = makeView(context.doc)
+ view.CreateWindow(frame)
+
+class RichEditDocTemplate(DocTemplate):
+ def __init__(self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None):
+ if MakeView is None: MakeView = RichEditView
+ if MakeDocument is None: MakeDocument = RichEditDoc
+ DocTemplate.__init__(self, resourceId, MakeDocument, MakeFrame, MakeView)
+
+ def _CreateDocTemplate(self, resourceId):
+ return win32ui.CreateRichEditDocTemplate(resourceId)
+
+def t():
+ class FormTemplate(DocTemplate):
+ def CreateView(self, frame, context):
+ makeView = self.MakeView
+# view = FormView(context.doc, win32ui.IDD_PROPDEMO1)
+ view = ListView(context.doc)
+ view.CreateWindow(frame)
+
+ t=FormTemplate()
+ return t.OpenDocumentFile(None)
diff --git a/Pythonwin/pywin/mfc/object.py b/Pythonwin/pywin/mfc/object.py
new file mode 100644
index 0000000000..6671e1c76e
--- /dev/null
+++ b/Pythonwin/pywin/mfc/object.py
@@ -0,0 +1,36 @@
+# MFC base classes.
+import sys
+import win32ui
+
+class Object:
+ def __init__(self, initObj = None):
+ self.__dict__['_obj_'] = initObj
+# self._obj_ = initObj
+ if initObj is not None: initObj.AttachObject(self)
+ def __del__(self):
+ self.close()
+ def __getattr__(self, attr): # Make this object look like the underlying win32ui one.
+ # During cleanup __dict__ is not available, causing recursive death.
+ if attr != '__dict__':
+ o = self.__dict__.get('_obj_')
+ if o is not None:
+ return getattr(o, attr)
+ # Only raise this error for non "internal" names -
+ # Python may be calling __len__, __nonzero__, etc, so
+ # we dont want this exception
+ if attr[0]!= '_' and attr[-1] != '_':
+ raise win32ui.error, "The MFC object has died."
+ raise AttributeError, attr
+
+ def OnAttachedObjectDeath(self):
+# print "object", self.__class__.__name__, "dieing"
+ self._obj_ = None
+ def close(self):
+ if self.__dict__.has_key('_obj_'):
+ if self._obj_ is not None:
+ self._obj_.AttachObject(None)
+ self._obj_ = None
+
+class CmdTarget(Object):
+ def __init__(self, initObj):
+ Object.__init__(self, initObj)
diff --git a/Pythonwin/pywin/mfc/thread.py b/Pythonwin/pywin/mfc/thread.py
new file mode 100644
index 0000000000..bf89eb722f
--- /dev/null
+++ b/Pythonwin/pywin/mfc/thread.py
@@ -0,0 +1,22 @@
+# Thread and application objects
+
+import object
+import win32ui
+
+class WinThread(object.CmdTarget):
+ def __init__(self, initObj = None):
+ if initObj is None:
+ initObj = win32ui.CreateThread()
+ object.CmdTarget.__init__(self, initObj)
+
+ def InitInstance(self):
+ pass
+ def ExitInstance(self):
+ pass
+
+
+class WinApp(WinThread):
+ def __init__(self, initApp = None):
+ if initApp is None:
+ initApp = win32ui.GetApp()
+ WinThread.__init__(self, initApp)
diff --git a/Pythonwin/pywin/mfc/window.py b/Pythonwin/pywin/mfc/window.py
new file mode 100644
index 0000000000..86b9d0980d
--- /dev/null
+++ b/Pythonwin/pywin/mfc/window.py
@@ -0,0 +1,42 @@
+# The MFCish window classes.
+import object
+import win32ui
+import win32con
+
+class Wnd(object.CmdTarget):
+ def __init__(self, initobj=None):
+ object.CmdTarget.__init__(self, initobj)
+ if self._obj_: self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
+ def OnDestroy(self, msg):
+ pass
+
+# NOTE NOTE - This facility is currently disabled in Pythonwin!!!!!
+# Note - to process all messages for your window, add the following method
+# to a derived class. This code provides default message handling (ie, is
+# identical, except presumably in speed, as if the method did not exist at
+# all, so presumably will be modified to test for specific messages to be
+# useful!
+# def WindowProc(self, msg, wParam, lParam):
+# rc, lResult = self._obj_.OnWndMsg(msg, wParam, lParam)
+# if not rc: lResult = self._obj_.DefWindowProc(msg, wParam, lParam)
+# return lResult
+
+class FrameWnd(Wnd):
+ def __init__(self, wnd):
+ Wnd.__init__(self, wnd)
+
+class MDIChildWnd(FrameWnd):
+ def __init__(self, wnd = None):
+ if wnd is None:
+ wnd=win32ui.CreateMDIChild()
+ FrameWnd.__init__(self, wnd)
+ def OnCreateClient(self, cp, context):
+ if context is not None and context.template is not None:
+ context.template.CreateView(self, context)
+
+class MDIFrameWnd(FrameWnd):
+ def __init__(self, wnd = None):
+ if wnd is None:
+ wnd=win32ui.CreateMDIFrame()
+ FrameWnd.__init__(self, wnd)
+ self.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
diff --git a/Pythonwin/pywin/scintilla/IDLEenvironment.py b/Pythonwin/pywin/scintilla/IDLEenvironment.py
new file mode 100644
index 0000000000..7b15ef260b
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/IDLEenvironment.py
@@ -0,0 +1,488 @@
+# Code that allows Pythonwin to pretend it is IDLE
+# (at least as far as most IDLE extensions are concerned)
+
+import string
+import win32api
+import win32ui
+import win32con
+import sys
+
+from pywin.mfc.dialog import GetSimpleInput
+
+wordchars = string.uppercase + string.lowercase + string.digits
+
+class TextError(Exception): # When a TclError would normally be raised.
+ pass
+
+class EmptyRange(Exception): # Internally raised.
+ pass
+
+def GetIDLEModule(module):
+ try:
+ # First get it from Pythonwin it is exists.
+ modname = "pywin.idle." + module
+ __import__(modname)
+ except ImportError:
+ # See if I can import it directly (IDLE is probably on the path)
+ modname = module
+ __import__(modname)
+ mod=sys.modules[modname]
+ mod.TclError = TextError # A hack that can go soon!
+ return mod
+
+# A class that attempts to emulate an IDLE editor window.
+# Construct with a Pythonwin view.
+class IDLEEditorWindow:
+ def __init__(self, edit):
+ self.edit = edit
+ self.text = TkText(edit)
+ self.extensions = {}
+ self.extension_menus = {}
+
+ def close(self):
+ self.edit = self.text = None
+ self.extension_menus = None
+ try:
+ for ext in self.extensions.values():
+ closer = getattr(ext, "close", None)
+ if closer is not None:
+ closer()
+ finally:
+ self.extensions = {}
+
+ def IDLEExtension(self, extension):
+ ext = self.extensions.get(extension)
+ if ext is not None: return ext
+ klass = getattr(GetIDLEModule(extension), extension)
+ ext = self.extensions[extension] = klass(self)
+ # Find and bind all the events defined in the extension.
+ events = filter(lambda item: item[-6:]=="_event", dir(klass))
+ for event in events:
+ name = "<<%s>>" % (string.replace(event[:-6], "_", "-"), )
+ self.edit.bindings.bind(name, getattr(ext, event))
+ return ext
+
+ def GetMenuItems(self, menu_name):
+ # Get all menu items for the menu name (eg, "edit")
+ bindings = self.edit.bindings
+ ret = []
+ for ext in self.extensions.values():
+ menudefs = getattr(ext, "menudefs", [])
+ for name, items in menudefs:
+ if name == menu_name:
+ for text, event in filter(lambda item: item is not None, items):
+ text = string.replace(text, "&", "&&")
+ text = string.replace(text, "_", "&")
+ ret.append(text, event)
+ return ret
+
+ ######################################################################
+ # The IDLE "Virtual UI" methods that are exposed to the IDLE extensions.
+ #
+ def askinteger(self, caption, prompt, parent=None, initialvalue=0, minvalue=None, maxvalue=None):
+ while 1:
+ rc = GetSimpleInput(prompt, str(initialvalue), caption)
+ if rc is None: return 0 # Correct "cancel" semantics?
+ err = None
+ try:
+ rc = int(rc)
+ except ValueError:
+ err = "Please enter an integer"
+ if not err and minvalue is not None and rc < minvalue:
+ err = "Please enter an integer greater then or equal to %s" % (minvalue,)
+ if not err and maxvalue is not None and rc > maxvalue:
+ err = "Please enter an integer less then or equal to %s" % (maxvalue,)
+ if err:
+ win32ui.MessageBox(err, caption, win32con.MB_OK)
+ continue
+ return rc
+ def askyesno(self, caption, prompt, parent=None):
+ return win32ui.MessageBox(prompt, caption, win32con.MB_YESNO)==win32con.IDYES
+
+ ######################################################################
+ # The IDLE "Virtual Text Widget" methods that are exposed to the IDLE extensions.
+ #
+
+ # Is character at text_index in a Python string? Return 0 for
+ # "guaranteed no", true for anything else.
+ def is_char_in_string(self, text_index):
+ # A helper for the code analyser - we need internal knowledge of
+ # the colorizer to get this information
+ # This assumes the colorizer has got to this point!
+ text_index = self.text._getoffset(text_index)
+ c = self.text.edit._GetColorizer()
+ if c and c.GetStringStyle(text_index) is None:
+ return 0
+ return 1
+
+ # If a selection is defined in the text widget, return
+ # (start, end) as Tkinter text indices, otherwise return
+ # (None, None)
+ def get_selection_indices(self):
+ try:
+ first = self.text.index("sel.first")
+ last = self.text.index("sel.last")
+ return first, last
+ except TextError:
+ return None, None
+
+ def set_tabwidth(self, width ):
+ self.edit.SCISetTabWidth(width)
+
+ def get_tabwidth(self):
+ return self.edit.GetTabWidth()
+
+# A class providing the generic "Call Tips" interface
+class CallTips:
+ def __init__(self, edit):
+ self.edit = edit
+ def showtip(self, tip_text):
+ self.edit.SCICallTipShow(tip_text)
+ def hidetip(self):
+ self.edit.SCICallTipCancel()
+
+########################################
+#
+# Helpers for the TkText emulation.
+def TkOffsetToIndex(offset, edit):
+ lineoff = 0
+# max = edit.GetTextLength()
+# if offset > max: offset = max
+ line = edit.LineFromChar(offset)
+ lineIndex = edit.LineIndex(line)
+ return "%d.%d" % (line+1, offset-lineIndex)
+
+def _NextTok(str, pos):
+ # Returns (token, endPos)
+ end = len(str)
+ if pos>=end: return None, 0
+ while pos < end and str[pos] in string.whitespace:
+ pos = pos + 1
+ # Special case for +-
+ if str[pos] in '+-':
+ return str[pos],pos+1
+ # Digits also a special case.
+ endPos = pos
+ while endPos < end and str[endPos] in string.digits+".":
+ endPos = endPos + 1
+ if pos!=endPos: return str[pos:endPos], endPos
+ endPos = pos
+ while endPos < end and str[endPos] not in string.whitespace + string.digits + "+-":
+ endPos = endPos + 1
+ if pos!=endPos: return str[pos:endPos], endPos
+ return None, 0
+
+def TkIndexToOffset(bm, edit, marks):
+ base, nextTokPos = _NextTok(bm, 0)
+ if base is None: raise ValueError, "Empty bookmark ID!"
+ if string.find(base,".")>0:
+ try:
+ line, col = string.split(base, ".", 2)
+ if col=="first" or col=="last":
+ # Tag name
+ if line != "sel": raise ValueError, "Tags arent here!"
+ sel = edit.GetSel()
+ if sel[0]==sel[1]:
+ raise EmptyRange
+ if col=="first":
+ pos = sel[0]
+ else:
+ pos = sel[1]
+ else:
+ # Lines are 1 based for tkinter
+ line = int(line)-1
+ if line > edit.GetLineCount():
+ pos = edit.GetTextLength()+1
+ else:
+ pos = edit.LineIndex(line)+int(col)
+ except (ValueError, IndexError):
+ raise ValueError, "Unexpected literal in '%s'" % base
+ elif base == 'insert':
+ pos = edit.GetSel()[0]
+ elif base=='end':
+ pos = edit.GetTextLength()
+ # Pretend there is a trailing '\n' if necessary
+ if edit.SCIGetCharAt(pos) != "\n":
+ pos = pos+1
+ else:
+ try:
+ pos = marks[base]
+ except KeyError:
+ raise ValueError, "Unsupported base offset or undefined mark '%s'" % base
+
+ while 1:
+ word, nextTokPos = _NextTok(bm, nextTokPos)
+ if word is None: break
+ if word in ['+','-']:
+ num, nextTokPos = _NextTok(bm, nextTokPos)
+ if num is None: raise ValueError, "+/- operator needs 2 args"
+ what, nextTokPos = _NextTok(bm, nextTokPos)
+ if what is None: raise ValueError, "+/- operator needs 2 args"
+ if what[0] <> "c": raise ValueError, "+/- only supports chars"
+ if word=='+':
+ pos = pos + int(num)
+ else:
+ pos = pos - int(num)
+ elif word=='wordstart':
+ while pos > 0 and edit.SCIGetCharAt(pos-1) in wordchars:
+ pos = pos - 1
+ elif word=='wordend':
+ end = edit.GetTextLength()
+ while pos < end and edit.SCIGetCharAt(pos) in wordchars:
+ pos = pos + 1
+ elif word=='linestart':
+ while pos > 0 and edit.SCIGetCharAt(pos-1) not in '\n\r':
+ pos = pos - 1
+ elif word=='lineend':
+ end = edit.GetTextLength()
+ while pos < end and edit.SCIGetCharAt(pos) not in '\n\r':
+ pos = pos + 1
+ else:
+ raise ValueError, "Unsupported relative offset '%s'" % word
+ return pos
+# return max(pos, 0) # Tkinter is tollerant of -ve indexes.
+
+# A class that resembles an IDLE (ie, a Tk) text widget.
+# Construct with an edit object (eg, an editor view)
+class TkText:
+ def __init__(self, edit):
+ self.calltips = None
+ self.edit = edit
+ self.marks = {}
+## def __getattr__(self, attr):
+## if attr=="tk": return self # So text.tk.call works.
+## if attr=="master": return None # ditto!
+## raise AttributeError, attr
+## def __getitem__(self, item):
+## if item=="tabs":
+## size = self.edit.GetTabWidth()
+## if size==8: return "" # Tk default
+## return size # correct semantics?
+## elif item=="font": # Used for measurements we dont need to do!
+## return "Dont know the font"
+## raise IndexError, "Invalid index '%s'" % item
+ def make_calltip_window(self):
+ if self.calltips is None:
+ self.calltips = CallTips(self.edit)
+ return self.calltips
+ def _getoffset(self, index):
+ return TkIndexToOffset(index, self.edit, self.marks)
+ def _getindex(self, off):
+ return TkOffsetToIndex(off, self.edit)
+ def _fix_eol_indexes(self, start, end):
+ if start>0 and self.edit.SCIGetCharAt(start)=='\n' and self.edit.SCIGetCharAt(start-1)=='\r':
+ start = start - 1
+ if end < self.edit.GetTextLength() and self.edit.SCIGetCharAt(end-1)=='\r' and self.edit.SCIGetCharAt(end)=='\n':
+ end = end + 1
+ return start, end
+## def get_tab_width(self):
+## return self.edit.GetTabWidth()
+## def call(self, *rest):
+## # Crap to support Tk measurement hacks for tab widths
+## if rest[0] != "font" or rest[1] != "measure":
+## raise ValueError, "Unsupport call type"
+## return len(rest[5])
+## def configure(self, **kw):
+## for name, val in kw.items():
+## if name=="tabs":
+## self.edit.SCISetTabWidth(int(val))
+## else:
+## raise ValueError, "Unsupported configuration item %s" % kw
+ def bind(self, binding, handler):
+ self.edit.bindings.bind(binding, handler)
+ def get(self, start, end = None):
+ try:
+ start = self._getoffset(start)
+ if end is None:
+ end = start+1
+ else:
+ end = self._getoffset(end)
+ except EmptyRange:
+ return ""
+ # Simple semantic checks to conform to the Tk text interface
+ if end <= start: return ""
+ max = self.edit.GetTextLength()
+ checkEnd = 0
+ if end > max:
+ end = max
+ checkEnd = 1
+ start, end = self._fix_eol_indexes(start, end)
+ ret = self.edit.GetTextRange(start, end)
+ # pretend a trailing '\n' exists if necessary.
+ if checkEnd and (not ret or ret[-1] != '\n'): ret = ret + '\n'
+ return string.replace(ret, "\r", "")
+ def index(self, spec):
+ try:
+ return self._getindex(self._getoffset(spec))
+ except EmptyRange:
+ return ""
+ def insert(self, pos, text):
+ try:
+ pos = self._getoffset(pos)
+ except EmptyRange:
+ raise TextError, "Empty range"
+ self.edit.SetSel((pos, pos))
+ # IDLE only deals with "\n" - we will be nicer
+ bits = string.split(text, '\n')
+ self.edit.SCIAddText(bits[0])
+ for bit in bits[1:]:
+ self.edit.SCINewline()
+ self.edit.SCIAddText(bit)
+
+ def delete(self, start, end=None):
+ try:
+ start = self._getoffset(start)
+ if end is not None: end = self._getoffset(end)
+ except EmptyRange:
+ raise TextError, "Empty range"
+ # If end is specified and == start, then we must delete nothing.
+ if start==end: return
+ # If end is not specified, delete one char
+ if end is None:
+ end = start+1
+ else:
+ # Tk says not to delete in this case, but our control would.
+ if end=start and old=end:
+ old = old - (end-start)
+ self.edit.SetSel(old)
+
+ def bell(self):
+ win32api.MessageBeep()
+
+ def see(self, pos):
+ # Most commands we use in Scintilla actually force the selection
+ # to be seen, making this unnecessary.
+ pass
+
+ def mark_set(self, name, pos):
+ try:
+ pos = self._getoffset(pos)
+ except EmptyRange:
+ raise TextError, "Empty range '%s'" % pos
+ if name == "insert":
+ self.edit.SetSel( pos )
+ else:
+ self.marks[name]=pos
+
+ def tag_add(self, name, start, end):
+ if name != "sel": raise ValueError, "Only sel tag is supported"
+ try:
+ start = self._getoffset(start)
+ end = self._getoffset(end)
+ except EmptyRange:
+ raise TextError, "Empty range"
+ self.edit.SetSel( start, end )
+
+ def tag_remove(self, name, start, end):
+ if name !="sel" or start != "1.0" or end != "end":
+ raise ValueError, "Cant remove this tag"
+ # Turn the sel into a cursor
+ self.edit.SetSel(self.edit.GetSel()[0])
+
+ def compare(self, i1, op, i2):
+ try:
+ i1=self._getoffset(i1)
+ except EmptyRange:
+ i1 = ""
+ try:
+ i2=self._getoffset(i2)
+ except EmptyRange:
+ i2 = ""
+ return eval("%d%s%d" % (i1,op,i2))
+
+ def undo_block_start(self):
+ self.edit.SCISetUndoCollection(2)
+
+ def undo_block_stop(self):
+ self.edit.SCIAppendUndoStartAction()
+ self.edit.SCISetUndoCollection(1)
+
+######################################################################
+#
+# Test related code.
+#
+######################################################################
+def TestCheck(index, edit, expected=None):
+ rc = TkIndexToOffset(index, edit, {})
+ if rc != expected:
+ print "ERROR: Index", index,", expected", expected, "but got", rc
+
+def TestGet(fr, to, t, expected):
+ got = t.get(fr, to)
+ if got != expected:
+ print "ERROR: get(%s, %s) expected %s, but got %s" % (`fr`, `to`, `expected`, `got`)
+
+def test():
+ import pywin.framework.editor
+ d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
+ e=d.GetFirstView()
+ t = TkText(e)
+ e.SCIAddText("hi there how\nare you today\r\nI hope you are well")
+ e.SetSel((4,4))
+
+ skip = """
+ TestCheck("insert", e, 4)
+ TestCheck("insert wordstart", e, 3)
+ TestCheck("insert wordend", e, 8)
+ TestCheck("insert linestart", e, 0)
+ TestCheck("insert lineend", e, 12)
+ TestCheck("insert + 4 chars", e, 8)
+ TestCheck("insert +4c", e, 8)
+ TestCheck("insert - 2 chars", e, 2)
+ TestCheck("insert -2c", e, 2)
+ TestCheck("insert-2c", e, 2)
+ TestCheck("insert-2 c", e, 2)
+ TestCheck("insert- 2c", e, 2)
+ TestCheck("1.1", e, 1)
+ TestCheck("1.0", e, 0)
+ TestCheck("2.0", e, 13)
+ try:
+ TestCheck("sel.first", e, 0)
+ print "*** sel.first worked with an empty selection"
+ except TextError:
+ pass
+ e.SetSel((4,5))
+ TestCheck("sel.first- 2c", e, 2)
+ TestCheck("sel.last- 2c", e, 3)
+ """
+ # Check EOL semantics
+ e.SetSel((4,4))
+ TestGet("insert lineend", "insert lineend +1c", t, "\n")
+ e.SetSel((20, 20))
+ TestGet("insert lineend", "insert lineend +1c", t, "\n")
+ e.SetSel((35, 35))
+ TestGet("insert lineend", "insert lineend +1c", t, "\n")
+
+class IDLEWrapper:
+ def __init__(self, control):
+ self.text = control
+
+def IDLETest(extension):
+ import sys, os
+ modname = "pywin.idle." + extension
+ __import__(modname)
+ mod=sys.modules[modname]
+ mod.TclError = TextError
+ klass = getattr(mod, extension)
+
+ # Create a new Scintilla Window.
+ import pywin.framework.editor
+ d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
+ v=d.GetFirstView()
+ fname=os.path.splitext(__file__)[0] + ".py"
+ v.SCIAddText(open(fname).read())
+ d.SetModifiedFlag(0)
+ r=klass( IDLEWrapper( TkText(v) ) )
+ return r
+
+if __name__=='__main__':
+ test()
\ No newline at end of file
diff --git a/Pythonwin/pywin/scintilla/__init__.py b/Pythonwin/pywin/scintilla/__init__.py
new file mode 100644
index 0000000000..8d8ea47073
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/__init__.py
@@ -0,0 +1 @@
+# package init.
\ No newline at end of file
diff --git a/Pythonwin/pywin/scintilla/bindings.py b/Pythonwin/pywin/scintilla/bindings.py
new file mode 100644
index 0000000000..1d4159766a
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/bindings.py
@@ -0,0 +1,169 @@
+import IDLEenvironment
+import string
+import win32ui
+import win32api
+import win32con
+import sys
+
+HANDLER_ARGS_GUESS=0
+HANDLER_ARGS_NATIVE=1
+HANDLER_ARGS_IDLE=2
+HANDLER_ARGS_EXTENSION=3
+
+next_id = 5000
+
+event_to_commands = {}# dict of integer IDs to event names.
+command_to_events = {}# dict of event names to int IDs
+
+def assign_command_id(event, id = 0):
+ global next_id
+ if id == 0:
+ id = event_to_commands.get(event, 0)
+ if id == 0:
+ id = next_id
+ next_id = next_id + 1
+ # Only map the ones we allocated - specified ones are assumed to have a handler
+ command_to_events[id] = event
+ event_to_commands[event] = id
+ return id
+
+class SendCommandHandler:
+ def __init__(self, cmd):
+ self.cmd = cmd
+ def __call__(self, *args):
+ win32ui.GetMainFrame().SendMessage(win32con.WM_COMMAND, self.cmd)
+
+class Binding:
+ def __init__(self, handler, handler_args_type):
+ self.handler = handler
+ self.handler_args_type = handler_args_type
+
+class BindingsManager:
+ def __init__(self, parent_view):
+ self.parent_view = parent_view
+ self.bindings = {} # dict of Binding instances.
+ self.keymap = {}
+
+ def prepare_configure(self):
+ self.keymap = {}
+
+ def complete_configure(self):
+ for id in command_to_events.keys():
+ self.parent_view.HookCommand(self._OnCommand, id)
+
+ def close(self):
+ self.parent_view = self.bindings = self.keymap = None
+
+ def report_error(self, problem):
+ try:
+ win32ui.SetStatusText(problem, 1)
+ except win32ui.error:
+ # No status bar!
+ print problem
+
+ def update_keymap(self, keymap):
+ self.keymap.update(keymap)
+
+ def bind(self, event, handler, handler_args_type = HANDLER_ARGS_GUESS, cid = 0):
+ if handler is None:
+ handler = SendCommandHandler(cid)
+ self.bindings[event] = self._new_binding(handler, handler_args_type)
+ self.bind_command(event, cid)
+
+ def bind_command(self, event, id = 0):
+ "Binds an event to a Windows control/command ID"
+ id = assign_command_id(event, id)
+ return id
+
+ def get_command_id(self, event):
+ id = event_to_commands.get(event)
+ if id is None:
+ # See if we even have an event of that name!?
+ if not self.bindings.has_key(event):
+ return None
+ id = self.bind_command(event)
+ return id
+
+ def _OnCommand(self, id, code):
+ event = command_to_events.get(id)
+ if event is None:
+ self.report_error("No event associated with event ID %d" % id)
+ return 1
+ return self.fire(event)
+
+ def _new_binding(self, event, handler_args_type):
+ return Binding(event, handler_args_type)
+
+ def _get_IDLE_handler(self, ext, handler):
+ try:
+ instance = self.parent_view.idle.IDLEExtension(ext)
+ name = string.replace(handler, "-", "_") + "_event"
+ return getattr(instance, name)
+ except (ImportError, AttributeError):
+ msg = "Can not find event '%s' in IDLE extension '%s'" % (handler, ext)
+ self.report_error(msg)
+ return None
+
+ def fire(self, event, event_param = None):
+ # Fire the specified event. Result is native Pythonwin result
+ # (ie, 1==pass one, 0 or None==handled)
+
+ # First look up the event directly - if there, we are set.
+ binding = self.bindings.get(event)
+ if binding is None:
+ # If possible, find it!
+ # A native method name
+ handler = getattr(self.parent_view, event + "Event", None)
+ if handler is None:
+ # Can't decide if I should report an error??
+ self.report_error("The event name '%s' can not be found." % event)
+ # Either way, just let the default handlers grab it.
+ return 1
+ binding = self._new_binding(handler, HANDLER_ARGS_NATIVE)
+ # Cache it.
+ self.bindings[event] = binding
+
+ handler_args_type = binding.handler_args_type
+ # Now actually fire it.
+ if handler_args_type==HANDLER_ARGS_GUESS:
+ # Can't be native, as natives are never added with "guess".
+ # Must be extension or IDLE.
+ if event[0]=="<":
+ handler_args_type = HANDLER_ARGS_IDLE
+ else:
+ handler_args_type = HANDLER_ARGS_EXTENSION
+ try:
+ if handler_args_type==HANDLER_ARGS_EXTENSION:
+ args = self.parent_view.idle, event_param
+ else:
+ args = (event_param,)
+ rc = apply(binding.handler, args)
+ if handler_args_type==HANDLER_ARGS_IDLE:
+ # Convert to our return code.
+ if rc in [None, "break"]:
+ rc = 0
+ else:
+ rc = 1
+ except:
+ import traceback
+ message = "Firing event '%s' failed." % event
+ print message
+ traceback.print_exc()
+ self.report_error(message)
+ rc = 1 # Let any default handlers have a go!
+ return rc
+
+ def fire_key_event(self, msg):
+ key = msg[2]
+# print key, `chr(key)`
+ keyState = 0
+ if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
+ keyState = keyState | win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED
+ if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
+ keyState = keyState | win32con.SHIFT_PRESSED
+ if win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
+ keyState = keyState | win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
+ keyinfo = key, keyState
+ event = self.keymap.get( keyinfo )
+ if event is None: return 1
+ return self.fire(event, None)
diff --git a/Pythonwin/pywin/scintilla/config.py b/Pythonwin/pywin/scintilla/config.py
new file mode 100644
index 0000000000..405c580e02
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/config.py
@@ -0,0 +1,307 @@
+# config.py - deals with loading configuration information.
+
+# Loads config data from a .cfg file. Also caches the compiled
+# data back into a .cfc file.
+
+# If you are wondering how to avoid needing .cfg files (eg,
+# if you are freezing Pythonwin etc) I suggest you create a
+# .py file, and put the config info in a docstring. Then
+# pass a CStringIO file (rather than a filename) to the
+# config manager.
+import sys
+import string
+import keycodes
+import marshal
+import stat
+import os
+import types
+import traceback
+import pywin
+import glob
+
+debugging = 0
+if debugging:
+ import win32traceutil # Some trace statements fire before the interactive window is open.
+ def trace(*args):
+ sys.stderr.write(string.join(map(str, args), " ") + "\n")
+else:
+ trace = lambda *args: None
+
+compiled_config_version = 1
+
+def split_line(line, lineno):
+ comment_pos = string.find(line, "#")
+ if comment_pos>=0: line = line[:comment_pos]
+ sep_pos = string.rfind(line, "=")
+ if sep_pos == -1:
+ if string.strip(line):
+ print "Warning: Line %d: %s is an invalid entry" % (lineno, `line`)
+ return None, None
+ return "", ""
+ return string.strip(line[:sep_pos]), string.strip(line[sep_pos+1:])
+
+def get_section_header(line):
+ # Returns the section if the line is a section header, else None
+ if line[0] == "[":
+ end = string.find(line, "]")
+ if end==-1: end=len(line)
+ rc = string.lower(line[1:end])
+ try:
+ i = string.index(rc, ":")
+ return rc[:i], rc[i+1:]
+ except ValueError:
+ return rc, ""
+ return None, None
+
+def find_config_file(f):
+ return os.path.join(pywin.__path__[0], f + ".cfg")
+
+def find_config_files():
+ return map( lambda x: os.path.split(x)[1], map( lambda x: os.path.splitext(x)[0], glob.glob(os.path.join(pywin.__path__[0], "*.cfg"))))
+
+class ConfigManager:
+ def __init__(self, f):
+ self.last_error = None
+ self.key_to_events = {}
+ if hasattr(f, "readline"):
+ fp = f
+ self.filename = ""
+ compiled_name = None
+ else:
+ try:
+ f = find_config_file(f)
+ src_stat = os.stat(f)
+ except os.error:
+ self.report_error("Config file '%s' not found" % f)
+ return
+ self.filename = f
+ self.basename = os.path.basename(f)
+ trace("Loading configuration", self.basename)
+ compiled_name = os.path.splitext(f)[0] + ".cfc"
+ try:
+ cf = open(compiled_name, "rb")
+ try:
+ ver = marshal.load(cf)
+ size = marshal.load(cf)
+ mtime = marshal.load(cf)
+ if compiled_config_version == ver and \
+ src_stat[stat.ST_MTIME] == mtime and \
+ src_stat[stat.ST_SIZE] == size:
+ self.cache = marshal.load(cf)
+ trace("Configuration loaded cached", compiled_name)
+ return # We are ready to roll!
+ finally:
+ cf.close()
+ except (os.error, IOError, EOFError):
+ pass
+ fp = open(f)
+ self.cache = {}
+ lineno = 1
+ line = fp.readline()
+ while line:
+ # Skip to the next section (maybe already there!)
+ section, subsection = get_section_header(line)
+ while line and section is None:
+ line = fp.readline()
+ if not line: break
+ lineno = lineno + 1
+ section, subsection = get_section_header(line)
+ if not line: break
+
+ if section=="keys":
+ line, lineno = self._load_keys(subsection, fp, lineno)
+ elif section == "extensions":
+ line, lineno = self._load_extensions(subsection, fp, lineno)
+ elif section == "idle extensions":
+ line, lineno = self._load_idle_extensions(subsection, fp, lineno)
+ elif section == "general":
+ line, lineno = self._load_general(subsection, fp, lineno)
+ else:
+ self.report_error("Unrecognised section header '%s:%s'" % (section,subsection))
+ line = fp.readline()
+ lineno = lineno + 1
+ # Check critical data.
+ if not self.cache.get("keys"):
+ self.report_error("No keyboard definitions were loaded")
+ if not self.last_error and compiled_name:
+ try:
+ cf = open(compiled_name, "wb")
+ marshal.dump(compiled_config_version, cf)
+ marshal.dump(src_stat[stat.ST_SIZE], cf)
+ marshal.dump(src_stat[stat.ST_MTIME], cf)
+ marshal.dump(self.cache, cf)
+ cf.close()
+ except (IOError, EOFError):
+ pass # Ignore errors - may be read only.
+
+ def configure(self, editor, subsections = None):
+ # Execute the extension code, and find any events.
+ # First, we "recursively" connect any we are based on.
+ if subsections is None: subsections = []
+ subsections = [''] + subsections
+ general = self.get_data("general")
+ if general:
+ parents = general.get("based on", [])
+ for parent in parents:
+ trace("Configuration based on", parent, "- loading.")
+ parent = self.__class__(parent)
+ parent.configure(editor)
+ if parent.last_error:
+ self.report_error(parent.last_error)
+
+ bindings = editor.bindings
+ codeob = self.get_data("extension code")
+ if codeob is not None:
+ ns = {}
+ try:
+ exec codeob in ns
+ except:
+ traceback.print_exc()
+ self.report_error("Executing extension code failed")
+ ns = None
+ if ns:
+ num = 0
+ for name, func in ns.items():
+ if type(func)==types.FunctionType and name[:1] != '_':
+ bindings.bind(name, func)
+ num = num + 1
+ trace("Configuration Extension code loaded", num, "events")
+ # Load the idle extensions
+ for subsection in subsections:
+ for ext in self.get_data("idle extensions", {}).get(subsection, []):
+ try:
+ editor.idle.IDLEExtension(ext)
+ trace("Loaded IDLE extension", ext)
+ except:
+ self.report_error("Can not load the IDLE extension '%s'" % ext)
+
+ # Now bind up the key-map (remembering a reverse map
+ subsection_keymap = self.get_data("keys")
+ num_bound = 0
+ for subsection in subsections:
+ keymap = subsection_keymap.get(subsection, {})
+ bindings.update_keymap(keymap)
+ num_bound = num_bound + len(keymap)
+ trace("Configuration bound", num_bound, "keys")
+
+ def get_key_binding(self, event, subsections = None):
+ if subsections is None: subsections = []
+ subsections = [''] + subsections
+
+ subsection_keymap = self.get_data("keys")
+ for subsection in subsections:
+ map = self.key_to_events.get(subsection)
+ if map is None: # Build it
+ map = {}
+ keymap = subsection_keymap.get(subsection, {})
+ for key_info, event in keymap.items():
+ map[event] = key_info
+ self.key_to_events[subsection] = map
+
+ info = map.get(event)
+ if info is not None:
+ return keycodes.make_key_name( info[0], info[1] )
+ return None
+
+ def report_error(self, msg):
+ self.last_error = msg
+ print "Error in %s: %s" % (self.filename, msg)
+ def report_warning(self, msg):
+ print "Warning in %s: %s" % (self.filename, msg)
+
+ def _readline(self, fp, lineno, bStripComments = 1):
+ line = fp.readline()
+ lineno = lineno + 1
+ if line:
+ bBreak = get_section_header(line)[0] is not None # A new section is starting
+ if bStripComments and not bBreak:
+ pos = string.find(line, "#")
+ if pos>=0: line=line[:pos]+"\n"
+ else:
+ bBreak=1
+ return line, lineno, bBreak
+
+ def get_data(self, name, default=None):
+ return self.cache.get(name, default)
+
+ def _save_data(self, name, data):
+ self.cache[name] = data
+ return data
+
+ def _load_general(self, sub_section, fp, lineno):
+ map = {}
+ while 1:
+ line, lineno, bBreak = self._readline(fp, lineno)
+ if bBreak: break
+
+ key, val = split_line(line, lineno)
+ if not key: continue
+ key = string.lower(key)
+ l = map.get(key, [])
+ l.append(val)
+ map[key]=l
+ self._save_data("general", map)
+ return line, lineno
+
+ def _load_keys(self, sub_section, fp, lineno):
+ # Builds a nested dictionary of
+ # (scancode, flags) = event_name
+ main_map = self.get_data("keys", {})
+ map = main_map.get(sub_section, {})
+ while 1:
+ line, lineno, bBreak = self._readline(fp, lineno)
+ if bBreak: break
+
+ key, event = split_line(line, lineno)
+ if not event: continue
+ sc, flag = keycodes.parse_key_name(key)
+ if sc is None:
+ self.report_warning("Line %d: Invalid key name '%s'" % (lineno, key))
+ else:
+ map[sc, flag] = event
+ main_map[sub_section] = map
+ self._save_data("keys", main_map)
+ return line, lineno
+
+ def _load_extensions(self, sub_section, fp, lineno):
+ start_lineno = lineno
+ lines = []
+ while 1:
+ line, lineno, bBreak = self._readline(fp, lineno, 0)
+ if bBreak: break
+ lines.append(line)
+ try:
+ c = compile(string.join(lines, ""), self.filename, "exec")
+ self._save_data("extension code", c)
+ except SyntaxError, details:
+ msg = details[0]
+ errlineno = details[1][1] + start_lineno
+ # Should handle syntax errors better here, and offset the lineno.
+ self.report_error("Compiling extension code failed: Line %d: %s" % (errlineno, msg))
+ return line, lineno
+
+ def _load_idle_extensions(self, sub_section, fp, lineno):
+ extension_map = self.get_data("idle extensions")
+ if extension_map is None: extension_map = {}
+ extensions = []
+ while 1:
+ line, lineno, bBreak = self._readline(fp, lineno)
+ if bBreak: break
+ line = string.strip(line)
+ if line:
+ extensions.append(line)
+ extension_map[sub_section] = extensions
+ self._save_data("idle extensions", extension_map)
+ return line, lineno
+
+def test():
+ import time
+ start = time.clock()
+ f="default"
+ cm = ConfigManager(f)
+ map = cm.get_data("keys")
+ took = time.clock()-start
+ print "Loaded %s items in %.4f secs" % (len(map), took)
+
+if __name__=='__main__':
+ test()
diff --git a/Pythonwin/pywin/scintilla/configui.py b/Pythonwin/pywin/scintilla/configui.py
new file mode 100644
index 0000000000..30b598ed02
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/configui.py
@@ -0,0 +1,193 @@
+from pywin.mfc import dialog
+import win32con
+import win32ui
+import copy
+
+######################################################
+# Property Page for syntax formatting options
+
+# The standard 16 color VGA palette should always be possible
+paletteVGA = ( ("Black",0,0,0), ("Navy",0,0,128), ("Green",0,128,0), ("Cyan",0,128,128),
+ ("Maroon",128,0,0), ("Purple",128,0,128), ("Olive",128,128,0), ("Gray",128,128,128),
+ ("Silver",192,192,192), ("Blue",0,0,255), ("Lime",0,255,0), ("Aqua",0,255,255),
+ ("Red",255,0,0), ("Fuchsia",255,0,255), ("Yellow",255,255,0), ("White",255,255,255) )
+
+def BGR(b,g,r): # Colors in font definitions are integers made up of Blue, Green, and Red bytes
+ return b*256*256 + g*256 + r
+
+class ScintillaFormatPropertyPage(dialog.PropertyPage):
+ def __init__(self, scintillaClass = None):
+ self.scintillaClass = scintillaClass
+ dialog.PropertyPage.__init__(self, win32ui.IDD_PP_FORMAT)
+
+ def OnInitDialog(self):
+ if self.scintillaClass is None:
+ import control
+ sc = control.CScintillaEdit
+ else:
+ sc = self.scintillaClass
+
+ self.scintilla = sc()
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.ES_MULTILINE
+ # Convert the rect size
+ rect = self.MapDialogRect( (5, 5, 120, 75))
+ self.scintilla.CreateWindow(style, rect, self, 111)
+ self.scintilla.SCISetViewWS(1)
+
+ colorizer = self.scintilla._GetColorizer()
+ self.scintilla.SCIAddText(colorizer.GetSampleText())
+ self.scintilla.Reformat()
+ self.styles = self.scintilla._GetColorizer().styles
+
+ self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
+ for c in paletteVGA:
+ self.cbo.AddString(c[0])
+
+ self.cboBoldItalic = self.GetDlgItem(win32ui.IDC_COMBO2)
+ for item in ["Bold Italic", "Bold", "Italic", "Regular"]:
+ self.cboBoldItalic.InsertString(0, item)
+
+ self.butIsDefault = self.GetDlgItem(win32ui.IDC_CHECK1)
+ self.listbox = self.GetDlgItem(win32ui.IDC_LIST1)
+ self.HookCommand(self.OnListCommand, win32ui.IDC_LIST1)
+ names = self.styles.keys()
+ names.sort()
+ for name in names:
+ if self.styles[name].aliased is None:
+ self.listbox.AddString(name)
+ self.listbox.SetCurSel(0)
+
+ idc = win32ui.IDC_RADIO1
+ if not self.scintilla._GetColorizer().bUseFixed: idc = win32ui.IDC_RADIO2
+ self.GetDlgItem(idc).SetCheck(1)
+ self.UpdateUIForStyle(self.styles[names[0]])
+
+ self.scintilla.HookStyleNotify(self)
+ self.HookCommand(self.OnButDefaultFixedFont, win32ui.IDC_BUTTON1)
+ self.HookCommand(self.OnButDefaultPropFont, win32ui.IDC_BUTTON2)
+ self.HookCommand(self.OnButThisFont, win32ui.IDC_BUTTON3)
+ self.HookCommand(self.OnButUseDefaultFont, win32ui.IDC_CHECK1)
+ self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO1)
+ self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO2)
+ self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO1)
+ self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO2)
+
+ def GetSelectedStyle(self):
+ return self.styles[self.listbox.GetText(self.listbox.GetCurSel())]
+
+ def _DoButDefaultFont(self, extra_flags, attr):
+ baseFormat = getattr(self.scintilla._GetColorizer(), attr)
+ flags = extra_flags | win32con.CF_SCREENFONTS | win32con.CF_EFFECTS | win32con.CF_FORCEFONTEXIST
+ d=win32ui.CreateFontDialog(baseFormat, flags, None, self)
+ if d.DoModal()==win32con.IDOK:
+ setattr(self.scintilla._GetColorizer(), attr, d.GetCharFormat())
+ self.OnStyleUIChanged(0, win32con.BN_CLICKED)
+
+ def OnButDefaultFixedFont(self, id, code):
+ if code==win32con.BN_CLICKED:
+ self._DoButDefaultFont(win32con.CF_FIXEDPITCHONLY, "baseFormatFixed")
+ return 1
+
+ def OnButDefaultPropFont(self, id, code):
+ if code==win32con.BN_CLICKED:
+ self._DoButDefaultFont(win32con.CF_SCALABLEONLY, "baseFormatProp")
+ return 1
+
+ def OnButFixedOrDefault(self, id, code):
+ if code==win32con.BN_CLICKED:
+ bUseFixed = id == win32ui.IDC_RADIO1
+ self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck() != 0
+ self.scintilla._GetColorizer().bUseFixed = bUseFixed
+ self.scintilla.Reformat(0)
+ return 1
+
+ def OnButThisFont(self, id, code):
+ if code==win32con.BN_CLICKED:
+ flags = win32con.CF_SCREENFONTS | win32con.CF_EFFECTS | win32con.CF_FORCEFONTEXIST
+ style = self.GetSelectedStyle()
+ d=win32ui.CreateFontDialog(style.format, flags, None, self)
+ if d.DoModal()==win32con.IDOK:
+ style.format = d.GetCharFormat()
+ self.scintilla.Reformat(0)
+ return 1
+
+ def OnButUseDefaultFont(self, id, code):
+ if code == win32con.BN_CLICKED:
+ isDef = self.butIsDefault.GetCheck()
+ self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not isDef)
+ if isDef: # Being reset to the default font.
+ style = self.GetSelectedStyle()
+ style.ForceAgainstDefault()
+ self.UpdateUIForStyle(style)
+ self.scintilla.Reformat(0)
+ else:
+ # User wants to override default -
+ # do nothing!
+ pass
+
+ def OnListCommand(self, id, code):
+ if code==win32con.LBN_SELCHANGE:
+ style = self.GetSelectedStyle()
+ self.UpdateUIForStyle(style)
+ return 1
+
+ def UpdateUIForStyle(self, style ):
+ format = style.format
+ sel = 0
+ for c in paletteVGA:
+ if format[4] == BGR(c[3], c[2], c[1]):
+# print "Style", style.name, "is", c[0]
+ break
+ sel = sel + 1
+ else:
+ sel = -1
+ self.cbo.SetCurSel(sel)
+ self.butIsDefault.SetCheck(style.IsBasedOnDefault())
+ self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not style.IsBasedOnDefault())
+ bold = format[1] & win32con.CFE_BOLD != 0; italic = format[1] & win32con.CFE_ITALIC != 0
+ self.cboBoldItalic.SetCurSel( bold*2 + italic )
+
+ def OnStyleUIChanged(self, id, code):
+ if code in [win32con.BN_CLICKED, win32con.CBN_SELCHANGE]:
+ style = self.GetSelectedStyle()
+ self.ApplyUIFormatToStyle(style)
+ self.scintilla.Reformat(0)
+ return 0
+ return 1
+
+ def ApplyUIFormatToStyle(self, style):
+ format = style.format
+ color = paletteVGA[self.cbo.GetCurSel()]
+ effect = 0
+ sel = self.cboBoldItalic.GetCurSel()
+ if sel==0:
+ effect = 0
+ elif sel==1:
+ effect = win32con.CFE_ITALIC
+ elif sel==2:
+ effect = win32con.CFE_BOLD
+ else:
+ effect = win32con.CFE_BOLD | win32con.CFE_ITALIC
+ maskFlags=format[0]|win32con.CFM_COLOR|win32con.CFM_BOLD|win32con.CFM_ITALIC
+ style.format = (maskFlags, effect, style.format[2], style.format[3], BGR(color[3], color[2], color[1])) + style.format[5:]
+
+ def OnOK(self):
+ self.scintilla._GetColorizer().SavePreferences()
+ # Now tell _all_ Scintilla controls we can find to reformat
+ # themselves. Only ones attached to the formatter will have
+ # any visible changes (although all will reload their options)
+ for templ in win32ui.GetApp().GetDocTemplateList( ):
+ for d in templ.GetDocumentList( ):
+ # Try all documents, but only coloreditor.EditorDocument will respond
+ try:
+ fn = d.Reformat
+ except AttributeError:
+ continue
+ fn()
+ return 1
+
+def test():
+ page = ColorEditorPropertyPage()
+ sheet = pywin.mfc.dialog.PropertySheet("Test")
+ sheet.AddPage(page)
+ sheet.CreateWindow()
diff --git a/Pythonwin/pywin/scintilla/control.py b/Pythonwin/pywin/scintilla/control.py
new file mode 100644
index 0000000000..13fca92169
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/control.py
@@ -0,0 +1,297 @@
+# An Python interface to the Scintilla control.
+#
+# Exposes Python classes that allow you to use Scintilla as
+# a "standard" MFC edit control (eg, control.GetTextLength(), control.GetSel()
+# plus many Scintilla specific features (eg control.SCIAddStyledText())
+
+from pywin.mfc import window
+import win32con
+import win32ui
+import win32api
+import array
+import struct
+import string
+import os
+from scintillacon import *
+
+# Load Scintilla.dll to get access to the control.
+# We expect to find this in the same directory as win32ui.pyd
+try:
+ dllid = win32api.LoadLibrary(os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla.DLL"))
+except win32api.error: # Not there - lets see if Windows can find it by searching?
+ dllid = win32api.LoadLibrary("Scintilla.DLL")
+
+EM_GETTEXTRANGE = 1099
+EM_EXLINEFROMCHAR = 1078
+EM_FINDTEXTEX = 1103
+EM_GETSELTEXT = 1086
+
+class ScintillaControlInterface:
+ def SCIAddText(self, text):
+ sma = array.array('c', text)
+ (a,l) = sma.buffer_info()
+ self.SendScintilla(SCI_ADDTEXT, l, a)
+ def SCIAddStyledText(self, text, style = None):
+ # If style is None, text is assumed to be a "native" Scintilla buffer.
+ # If style is specified, text is a normal string, and the style is
+ # assumed to apply to the entire string.
+ if style is not None:
+ text = map(lambda char, style=style: char+chr(style), text)
+ text = string.join(text, '')
+ sma = array.array("c", text)
+ (a,l) = sma.buffer_info()
+ self.SendScintilla(SCI_ADDSTYLEDTEXT, l, a)
+ def SCIInsertText(self, text, pos=-1):
+ sma = array.array('c', text+"\0")
+ (a,l) = sma.buffer_info()
+ self.SendScintilla(SCI_INSERTTEXT, pos, a)
+ def SCISetSavePoint(self):
+ self.SendScintilla(SCI_SETSAVEPOINT)
+ def SCISetUndoCollection(self, collectFlag):
+ self.SendScintilla(SCI_SETUNDOCOLLECTION, collectFlag)
+ def SCIAppendUndoStartAction(self):
+ self.SendScintilla(SCI_APPENDUNDOSTARTACTION)
+
+ def SCIGetCurrentPos(self):
+ return self.SendScintilla(SCI_GETCURRENTPOS)
+ def SCIGetCharAt(self, pos):
+ # Must ensure char is unsigned!
+ return chr(self.SendScintilla(SCI_GETCHARAT, pos) & 0xFF)
+ def SCIGotoLine(self, line):
+ self.SendScintilla(SCI_GOTOLINE, line)
+
+ ####################################
+ # Styling
+ def SCIGetEndStyled(self):
+ return self.SendScintilla(SCI_GETENDSTYLED)
+ def SCIStyleSetFore(self, num, v):
+ return self.SendScintilla(SCI_STYLESETFORE, num, v)
+ def SCIStyleSetFont(self, num, name):
+ buff = array.array('c', name + "\0")
+ addressBuffer = buff.buffer_info()[0]
+ self.SendScintilla(SCI_STYLESETFONT, num, addressBuffer)
+ def SCIStyleSetBold(self, num, bBold):
+ self.SendScintilla(SCI_STYLESETBOLD, num, bBold)
+ def SCIStyleSetItalic(self, num, bItalic):
+ self.SendScintilla(SCI_STYLESETITALIC, num, bItalic)
+ def SCIStyleSetSize(self, num, size):
+ self.SendScintilla(SCI_STYLESETSIZE, num, size)
+ def SCIGetViewWS(self):
+ return self.SendScintilla(SCI_GETVIEWWS)
+ def SCISetViewWS(self, val):
+ self.SendScintilla(SCI_SETVIEWWS, not (val==0))
+ self.InvalidateRect()
+ def SCISetTabWidth(self, width):
+ self.SendScintilla(SCI_SETTABWIDTH, width, 0)
+ def SCIStartStyling(self, pos, mask):
+ self.SendScintilla(SCI_STARTSTYLING, pos, mask)
+ def SCISetStyling(self, pos, attr):
+ self.SendScintilla(SCI_SETSTYLING, pos, attr)
+ def SCISetStylingEx(self, ray): # ray is an array.
+ address, length = ray.buffer_info()
+ self.SendScintilla(SCI_SETSTYLINGEX, length, address)
+ def SCIGetStyleAt(self, pos):
+ return self.SendScintilla(SCI_GETSTYLEAT, pos)
+ def SCISetMarginWidth(self, width):
+ self.SendScintilla(SCI_SETMARGINWIDTH, width)
+ # Markers
+ def SCIMarkerDefine(self, markerNum, markerType):
+ self.SendScintilla(SCI_MARKERDEFINE, markerNum, markerType)
+ def SCIMarkerSetFore(self, markerNum, fore):
+ self.SendScintilla(SCI_MARKERSETFORE, markerNum, fore)
+ def SCIMarkerSetBack(self, markerNum, back):
+ self.SendScintilla(SCI_MARKERSETBACK, markerNum, back)
+ def SCIMarkerAdd(self, lineNo, markerNum):
+ self.SendScintilla(SCI_MARKERADD, lineNo, markerNum)
+ def SCIMarkerDelete(self, lineNo, markerNum):
+ self.SendScintilla(SCI_MARKERDELETE, lineNo, markerNum)
+ def SCIMarkerDeleteAll(self, markerNum=-1):
+ self.SendScintilla(SCI_MARKERDELETEALL, markerNum)
+ def SCIMarkerGet(self, lineNo):
+ return self.SendScintilla(SCI_MARKERGET, lineNo)
+ def SCIMarkerNext(self, lineNo, markerNum):
+ return self.SendScintilla(SCI_MARKERNEXT, lineNo, markerNum)
+ def SCICancel(self):
+ self.SendScintilla(SCI_CANCEL)
+ # AutoComplete
+ def SCIAutoCShow(self, text):
+ if type(text) in [type([]), type(())]:
+ text = string.join(text)
+ buff = array.array('c', text + "\0")
+ addressBuffer = buff.buffer_info()[0]
+ return self.SendScintilla(SCI_AUTOCSHOW, 0, addressBuffer)
+ def SCIAutoCCancel(self):
+ self.SendScintilla(SCI_AUTOCCANCEL)
+ def SCIAutoCActive(self):
+ return self.SendScintilla(SCI_AUTOCACTIVE)
+ def SCIAutoCComplete(self):
+ return self.SendScintilla(SCI_AUTOCCOMPLETE)
+ def SCIAutoCStops(self, stops):
+ buff = array.array('c', stops + "\0")
+ addressBuffer = buff.buffer_info()[0]
+ self.SendScintilla(SCI_AUTOCSTOPS, 0, addressBuffer)
+ # Call tips
+ def SCICallTipShow(self, text, pos=-1):
+ if pos==-1: pos = self.GetSel()[0]
+ buff = array.array('c', text + "\0")
+ addressBuffer = buff.buffer_info()[0]
+ self.SendScintilla(SCI_CALLTIPSHOW, pos, addressBuffer)
+ def SCICallTipCancel(self):
+ self.SendScintilla(SCI_CALLTIPCANCEL)
+ def SCICallTipActive(self):
+ return self.SendScintilla(SCI_CALLTIPACTIVE)
+ def SCICallTipPosStart(self):
+ return self.SendScintilla(SCI_CALLTIPPOSSTART)
+ def SCINewline(self):
+ self.SendScintilla(SCI_NEWLINE)
+
+class CScintillaEditInterface(ScintillaControlInterface):
+ def close(self):
+ self.colorizer = None
+ def Clear(self):
+ self.SendScintilla(win32con.WM_CLEAR)
+ def Clear(self):
+ self.SendScintilla(win32con.WM_CLEAR)
+ def FindText(self, flags, range, findText):
+ buff = array.array('c', findText + "\0")
+ addressBuffer = buff.buffer_info()[0]
+ ft = struct.pack('llLll', range[0], range[1], addressBuffer, 0, 0)
+ ftBuff = array.array('c', ft)
+ addressFtBuff = ftBuff.buffer_info()[0]
+ rc = self.SendScintilla(EM_FINDTEXTEX, flags, addressFtBuff)
+ ftUnpacked = struct.unpack('llLll', ftBuff.tostring())
+ return rc, (ftUnpacked[3], ftUnpacked[4])
+
+ def GetSel(self):
+ currentPos = self.SendScintilla(SCI_GETCURRENTPOS)
+ anchorPos = self.SendScintilla(SCI_GETANCHOR)
+ if currentPos < anchorPos:
+ return (currentPos, anchorPos)
+ else:
+ return (anchorPos, currentPos)
+ return currentPos;
+
+ def GetSelText(self):
+ start, end = self.GetSel()
+ txtBuf = array.array('c', " " * ((end-start)+1))
+ addressTxtBuf = txtBuf.buffer_info()[0]
+ self.SendScintilla(EM_GETSELTEXT, 0, addressTxtBuf)
+ return txtBuf.tostring()[:-1]
+
+ def SetSel(self, start=0, end=None):
+ if type(start)==type(()):
+ assert end is None, "If you pass a point in the first param, the second must be None"
+ start, end = start
+ elif end is None:
+ end = start
+ if start < 0: start = self.GetTextLength()
+ if end < 0: end = self.GetTextLength()
+ assert start <= self.GetTextLength(), "The start postion is invalid"
+ assert end <= self.GetTextLength(), "The end postion is invalid"
+ self.SendScintilla(win32con.EM_SETSEL, start, end)
+
+ def GetLineCount(self):
+ return self.SendScintilla(win32con.EM_GETLINECOUNT)
+
+ def LineFromChar(self, charPos=-1):
+ if charPos==-1: charPos = self.GetSel()[0]
+ assert charPos >= 0 and charPos <= self.GetTextLength(), "The charPos postion is invalid"
+ return self.SendScintilla(EM_EXLINEFROMCHAR, charPos)
+
+ def LineIndex(self, line):
+ return self.SendScintilla(win32con.EM_LINEINDEX, line)
+
+ def GetCurLineNumber(self):
+ return self.LineFromChar(self.SCIGetCurrentPos())
+
+ def GetTextLength(self):
+ return self.SendScintilla(win32con.WM_GETTEXTLENGTH)
+
+ def GetTextRange(self, start = 0, end = -1):
+ if end == -1: end = self.SendScintilla(win32con.WM_GETTEXTLENGTH)
+ assert end>=start, "Negative index requested (%d/%d)" % (start, end)
+ assert start >= 0 and start <= self.GetTextLength(), "The start postion is invalid"
+ assert end >= 0 and end <= self.GetTextLength(), "The end postion is invalid"
+ initer = "=" * (end - start + 1)
+ buff = array.array('c', initer)
+ addressBuffer = buff.buffer_info()[0]
+ tr = struct.pack('llL', start, end, addressBuffer)
+ trBuff = array.array('c', tr)
+ addressTrBuff = trBuff.buffer_info()[0]
+ numChars = self.SendScintilla(EM_GETTEXTRANGE, 0, addressTrBuff)
+ return buff.tostring()[:numChars]
+
+ def ReplaceSel(self, str):
+ buff = array.array('c', str + "\0")
+ self.SendScintilla(win32con.EM_REPLACESEL, 0, buff.buffer_info()[0]);
+ buff = None
+
+ def GetLine(self, line=-1):
+ if line == -1: line = self.GetCurLineNumber()
+ start = self.LineIndex(line)
+ end = self.LineIndex(line+1)
+ return self.GetTextRange(start, end)
+
+ def SetReadOnly(self, flag = 1):
+ return self.SendScintilla(win32con.EM_SETREADONLY, flag)
+
+ def LineScroll(self, lines, cols=0):
+ return self.SendScintilla(win32con.EM_LINESCROLL, cols, lines)
+
+ def GetFirstVisibleLine(self):
+ return self.SendScintilla(win32con.EM_GETFIRSTVISIBLELINE)
+
+ def SetWordWrap(self, mode):
+ if mode <> win32ui.CRichEditView_WrapNone:
+ raise ValueError, "We dont support word-wrap (I dont think :-)"
+
+class CScintillaColorEditInterface(CScintillaEditInterface):
+ ################################
+ # Plug-in colorizer support
+ def _GetColorizer(self):
+ if not hasattr(self, "colorizer"):
+ self.colorizer = self._MakeColorizer()
+ return self.colorizer
+ def _MakeColorizer(self):
+ # Give parent a chance to hook.
+ if hasattr(self.GetParent(), "_MakeColorizer"):
+ return self.GetParent()._MakeColorizer()
+ import formatter
+ return formatter.PythonSourceFormatter(self)
+
+ def Reformat(self, bReload=1):
+ c = self._GetColorizer()
+ if c is not None: c.Reformat(bReload)
+
+ # The Parent window will normally hook
+ def HookStyleNotify(self, parent = None):
+ if self._GetColorizer() is not None: # No need if we have no color!
+ if parent is None: parent = self.GetParentFrame()
+ parent.HookNotify(self.OnStyleNeeded, SCN_STYLENEEDED)
+
+ def OnStyleNeeded(self, std, extra):
+ bytes = win32ui.GetBytes( extra, struct.calcsize("iii") )
+ endPosPaint, ch, modifiers = struct.unpack("iii", bytes)
+ endStyledChar = self.SendScintilla(SCI_GETENDSTYLED)
+ lineEndStyled = self.LineFromChar(endStyledChar)
+ endStyled = self.LineIndex(lineEndStyled)
+ #print "enPosPaint %d endStyledChar %d lineEndStyled %d endStyled %d" % (endPosPaint, endStyledChar, lineEndStyled, endStyled)
+ self._GetColorizer().Colorize(endStyled, endPosPaint)
+
+class CScintillaEdit(window.Wnd, CScintillaColorEditInterface):
+ def __init__(self, wnd=None):
+ if wnd is None:
+ wnd = win32ui.CreateWnd()
+ window.Wnd.__init__(self, wnd)
+ def SendScintilla(self, msg, w=0, l=0):
+ return self.SendMessage(msg, w, l)
+ def CreateWindow(self, style, rect, parent, id):
+ self._obj_.CreateWindow(
+ "Scintilla",
+ "Scintilla",
+ style,
+ rect,
+ parent,
+ id,
+ None)
+
diff --git a/Pythonwin/pywin/scintilla/document.py b/Pythonwin/pywin/scintilla/document.py
new file mode 100644
index 0000000000..05edd5cb00
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/document.py
@@ -0,0 +1,37 @@
+import win32ui
+from pywin.mfc import docview
+
+ParentScintillaDocument=docview.Document
+class CScintillaDocument(ParentScintillaDocument):
+ "A SyntEdit document. "
+ def DeleteContents(self):
+ self.text = ""
+
+ def OnOpenDocument(self, filename):
+ # init data members
+ #print "Opening", filename
+ self.SetPathName(filename) # Must set this early!
+ try:
+ f = open(filename, 'rb')
+ try:
+ self.text = f.read()
+ finally:
+ f.close()
+ except IOError:
+ win32ui.MessageBox("Could not load the file from %s" % filename)
+ return 0
+
+ if self.GetFirstView():
+ self.GetFirstView()._SetLoadedText(self.text)
+ self.SetModifiedFlag(0) # No longer dirty
+ return 1
+
+ def SaveFile(self, fileName):
+ view = self.GetFirstView()
+ ok = view.SaveTextFile(fileName)
+ if ok:
+ self._ApplyOptionalToViews("SCISetSavePoint")
+ return ok
+
+ def Reformat(self):
+ self._ApplyOptionalToViews("Reformat")
diff --git a/Pythonwin/pywin/scintilla/find.py b/Pythonwin/pywin/scintilla/find.py
new file mode 100644
index 0000000000..0b3ddb10f8
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/find.py
@@ -0,0 +1,284 @@
+# find.py - Find and Replace
+import win32con, win32api
+import win32ui
+from pywin.mfc import dialog
+import afxres
+from pywin.framework import scriptutils
+
+FOUND_NOTHING=0
+FOUND_NORMAL=1
+FOUND_LOOPED_BACK=2
+FOUND_NEXT_FILE=3
+
+class SearchParams:
+ def __init__(self, other=None):
+ if other is None:
+ self.__dict__['findText'] = ""
+ self.__dict__['replaceText'] = ""
+ self.__dict__['matchCase'] = 0
+ self.__dict__['matchWords'] = 0
+ self.__dict__['acrossFiles'] = 0
+ self.__dict__['sel'] = (-1,-1)
+ self.__dict__['keepDialogOpen']=0
+ else:
+ self.__dict__.update(other.__dict__)
+ # Helper so we cant misspell attributes :-)
+ def __setattr__(self, attr, val):
+ if not hasattr(self, attr):
+ raise AttributeError, attr
+ self.__dict__[attr]=val
+
+curDialog = None
+lastSearch = SearchParams()
+
+def ShowFindDialog():
+ _ShowDialog(FindDialog)
+
+def ShowReplaceDialog():
+ _ShowDialog(ReplaceDialog)
+
+def _ShowDialog(dlgClass):
+ global curDialog
+ if curDialog is not None:
+ if curDialog.__class__ != dlgClass:
+ curDialog.DestroyWindow()
+ curDialog = None
+ else:
+ curDialog.SetFocus()
+ if curDialog is None:
+ curDialog = dlgClass()
+ curDialog.CreateWindow()
+
+def FindNext():
+ params = SearchParams(lastSearch)
+ params.sel = (-1,-1)
+ if not params.findText:
+ ShowFindDialog()
+ else:
+ return _FindIt(None, params)
+
+def _GetControl(control=None):
+ if control is None:
+ control = scriptutils.GetActiveEditControl()
+ if control is None:
+ raise RuntimeError, "Cant find the window to search in!"
+ return control
+
+def _FindIt(control, searchParams):
+ global lastSearch
+ control = _GetControl(control)
+
+ # Move to the next char, so we find the next one.
+ flags = 0
+ if searchParams.matchWords: flags = flags | win32con.FR_WHOLEWORD
+ if searchParams.matchCase: flags = flags | win32con.FR_MATCHCASE
+ if searchParams.sel == (-1,-1):
+ sel = control.GetSel()
+ # If the position is the same as we found last time,
+ # then we assume it is a "FindNext"
+ if sel==lastSearch.sel:
+ sel = sel[0]+1, sel[0]+1
+ else:
+ sel = searchParams.sel
+
+ if sel[0]==sel[1]: sel=sel[0], control.GetTextLength()
+
+ rc = FOUND_NOTHING
+ # (Old edit control will fail here!)
+ posFind, foundSel = control.FindText(flags, sel, searchParams.findText)
+ lastSearch = SearchParams(searchParams)
+ if posFind >= 0:
+ rc = FOUND_NORMAL
+ control.SetSel(foundSel)
+ control.SetFocus()
+ win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
+ if rc == FOUND_NOTHING and lastSearch.acrossFiles:
+ # Loop around all documents. First find this document.
+ try:
+ try:
+ doc = control.GetDocument()
+ except AttributeError:
+ try:
+ doc = control.GetParent().GetDocument()
+ except AttributeError:
+ print "Cant find a document for the control!"
+ doc = None
+ if doc is not None:
+ template = doc.GetDocTemplate()
+ alldocs = template.GetDocumentList()
+ mypos = lookpos = alldocs.index(doc)
+ while 1:
+ lookpos = (lookpos+1) % len(alldocs)
+ if lookpos == mypos:
+ break
+ view = alldocs[lookpos].GetFirstView()
+ posFind, foundSel = view.FindText(flags, (0, view.GetTextLength()), searchParams.findText)
+ if posFind >= 0:
+ nChars = foundSel[1]-foundSel[0]
+ lineNo = view.LineFromChar(posFind) # zero based.
+ lineStart = view.LineIndex(lineNo)
+ colNo = posFind - lineStart # zero based.
+ scriptutils.JumpToDocument(alldocs[lookpos].GetPathName(), lineNo+1, colNo+1, nChars)
+ rc = FOUND_NEXT_FILE
+ break
+ except win32ui.error:
+ pass
+ if rc == FOUND_NOTHING:
+ # Loop around this control - attempt to find from the start of the control.
+ posFind, foundSel = control.FindText(flags, (0, sel[0]-1), searchParams.findText)
+ if posFind >= 0:
+ control.SetSel(foundSel)
+ control.SetFocus()
+ win32ui.SetStatusText("Not found! Searching from the top of the file.")
+ rc = FOUND_LOOPED_BACK
+ else:
+ lastSearch.sel=-1,-1
+ win32ui.SetStatusText("Can not find '%s'" % searchParams.findText )
+
+ if rc != FOUND_NOTHING:
+ lastSearch.sel = foundSel
+
+ return rc
+
+def _ReplaceIt(control):
+ control = _GetControl(control)
+ statusText = "Can not find '%s'." % lastSearch.findText
+ rc = FOUND_NOTHING
+ if lastSearch.sel != (-1,-1):
+ control.ReplaceSel(lastSearch.replaceText)
+ rc = FindNext()
+ if rc !=FOUND_NOTHING:
+ statusText = win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)
+ win32ui.SetStatusText(statusText)
+ return rc
+
+class FindReplaceDialog(dialog.Dialog):
+ def __init__(self):
+ dialog.Dialog.__init__(self,self._GetDialogTemplate())
+ self.HookCommand(self.OnFindNext, 109)
+
+ def OnInitDialog(self):
+ self.editFindText = self.GetDlgItem(102)
+ self.butMatchWords = self.GetDlgItem(105)
+ self.butMatchCase = self.GetDlgItem(107)
+ self.butKeepDialogOpen = self.GetDlgItem(115)
+ self.butAcrossFiles = self.GetDlgItem(116)
+
+ self.editFindText.SetWindowText(lastSearch.findText)
+ self.editFindText.SetSel(0, -2)
+ self.editFindText.SetFocus()
+ self.butMatchWords.SetCheck(lastSearch.matchWords)
+ self.butMatchCase.SetCheck(lastSearch.matchCase)
+ self.butKeepDialogOpen.SetCheck(lastSearch.keepDialogOpen)
+ self.butAcrossFiles.SetCheck(lastSearch.acrossFiles)
+ return dialog.Dialog.OnInitDialog(self)
+
+ def OnDestroy(self, msg):
+ global curDialog
+ curDialog = None
+ return dialog.Dialog.OnDestroy(self, msg)
+
+ def DoFindNext(self):
+ params = SearchParams()
+ params.findText = self.editFindText.GetWindowText()
+ params.matchCase = self.butMatchCase.GetCheck()
+ params.matchWords = self.butMatchWords.GetCheck()
+ params.acrossFiles = self.butAcrossFiles.GetCheck()
+ return _FindIt(None, params)
+
+ def OnFindNext(self, id, code):
+ if not self.editFindText.GetWindowText():
+ win32api.MessageBeep()
+ return
+ if self.DoFindNext() != FOUND_NOTHING and not self.butKeepDialogOpen.GetCheck():
+ self.DestroyWindow()
+
+class FindDialog(FindReplaceDialog):
+ def _GetDialogTemplate(self):
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ visible = win32con.WS_CHILD | win32con.WS_VISIBLE
+ dt = [
+ ["Find", (0, 2, 240, 65), style, None, (8, "MS Sans Serif")],
+ ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
+ ["Edit", "", 102, (50, 7, 120, 12), visible | win32con.WS_BORDER | win32con.WS_TABSTOP],
+ ["Button", "Match &whole word only", 105, (5, 23, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Match &case", 107, (5, 33, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Keep &dialog open", 115, (5, 43, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Across &open files", 116, (5, 52, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "&Find Next", 109, (185, 5, 50, 14), visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP],
+ ["Button", "Cancel", win32con.IDCANCEL, (185, 23, 50, 14), visible | win32con.WS_TABSTOP],
+ ]
+ return dt
+
+class ReplaceDialog(FindReplaceDialog):
+ def _GetDialogTemplate(self):
+ style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
+ visible = win32con.WS_CHILD | win32con.WS_VISIBLE
+ dt = [
+ ["Replace", (0, 2, 240, 85), style, None, (8, "MS Sans Serif")],
+ ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible],
+ ["Edit", "", 102, (60, 7, 110, 12), visible | win32con.WS_BORDER | win32con.WS_TABSTOP],
+ ["Static", "Re&place with:", 103, (5, 25, 50, 10), visible],
+ ["Edit", "", 104, (60, 24, 110, 12), visible | win32con.WS_BORDER | win32con.WS_TABSTOP],
+ ["Button", "Match &whole word only", 105, (5, 42, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Match &case", 107, (5, 52, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Keep &dialog open", 115, (5, 62, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "Across &open files", 116, (5, 72, 100, 10), visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP],
+ ["Button", "&Find Next", 109, (185, 5, 50, 14), visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP],
+ ["Button", "&Replace", 110, (185, 23, 50, 14), visible | win32con.WS_TABSTOP],
+ ["Button", "Replace &All", 111, (185, 41, 50, 14), visible | win32con.WS_TABSTOP],
+ ["Button", "Cancel", win32con.IDCANCEL, (185, 59, 50, 14), visible | win32con.WS_TABSTOP],
+
+
+ ]
+ return dt
+
+ def OnInitDialog(self):
+ rc = FindReplaceDialog.OnInitDialog(self)
+ self.HookCommand(self.OnReplace, 110)
+ self.HookCommand(self.OnReplaceAll, 111)
+ self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
+ self.editReplaceText = self.GetDlgItem(104)
+ self.editReplaceText.SetWindowText(lastSearch.replaceText)
+ self.editReplaceText.SetSel(0, -2)
+ self.butReplace = self.GetDlgItem(110)
+ self.butReplaceAll = self.GetDlgItem(111)
+ self.CheckButtonStates()
+ return rc
+
+ def CheckButtonStates(self):
+ # We can do a "Replace" or "Replace All" if the current selection
+ # is the same as the search text.
+ ft = self.editFindText.GetWindowText()
+ control = _GetControl()
+# bCanReplace = len(ft)>0 and control.GetSelText() == ft
+ bCanReplace = lastSearch.sel == control.GetSel()
+ self.butReplace.EnableWindow(bCanReplace)
+ self.butReplaceAll.EnableWindow(bCanReplace)
+
+ def OnActivate(self, msg):
+ wparam = msg[2]
+ fActive = win32api.LOWORD(wparam)
+ if fActive != win32con.WA_INACTIVE:
+ self.CheckButtonStates()
+
+ def OnFindNext(self, id, code):
+ self.DoFindNext()
+ self.CheckButtonStates()
+
+ def OnReplace(self, id, code):
+ lastSearch.replaceText = self.editReplaceText.GetWindowText()
+ _ReplaceIt(None)
+
+ def OnReplaceAll(self, id, code):
+ lastSearch.replaceText = self.editReplaceText.GetWindowText()
+ num = 0
+ while _ReplaceIt(None) != FOUND_NOTHING:
+ num = num + 1
+
+ win32ui.SetStatusText("Replaced %d occurrences" % num)
+ if num > 0 and not self.butKeepDialogOpen.GetCheck():
+ self.DestroyWindow()
+
+if __name__=='__main__':
+ ShowFindDialog()
diff --git a/Pythonwin/pywin/scintilla/formatter.py b/Pythonwin/pywin/scintilla/formatter.py
new file mode 100644
index 0000000000..a782a29e52
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/formatter.py
@@ -0,0 +1,392 @@
+# Does Python source formatting for Scintilla controls.
+import win32ui
+import win32con
+import string
+import array
+
+WM_KICKIDLE = 0x036A
+
+debugging = 0
+if debugging:
+ # Output must go to another process else the result of
+ # the printing itself will trigger again trigger a trace.
+ import sys, win32traceutil, win32trace
+ def trace(*args):
+ win32trace.write(string.join(map(str, args), " ") + "\n")
+else:
+ trace = lambda *args: None
+
+class Style:
+ """Represents a single format
+ """
+ def __init__(self, name, format):
+ self.name = name # Name the format representes eg, "String", "Class"
+ if type(format)==type(''):
+ self.aliased = format
+ self.format = None
+ else:
+ self.format = format
+ self.aliased = None
+ self.stylenum = None # Not yet registered.
+ def IsBasedOnDefault(self):
+ return len(self.format)==5
+ # If the currently extended font defintion matches the
+ # default format, restore the format to the "simple" format.
+ def NormalizeAgainstDefault(self, defaultFormat):
+ if self.IsBasedOnDefault():
+ return 0 # No more to do, and not changed.
+ bIsDefault = self.format[7] == defaultFormat[7] and \
+ self.format[2] == defaultFormat[2]
+ if bIsDefault:
+ self.ForceAgainstDefault()
+ return bIsDefault
+ def ForceAgainstDefault(self):
+ self.format = self.format[:5]
+
+# An abstract formatter
+class Formatter:
+ def __init__(self, scintilla):
+ self.bCompleteWhileIdle = 1
+ self.bHaveIdleHandler = 0 # Dont currently have an idle handle
+ self.scintilla = scintilla
+ self.nextstylenum = 0
+ self.baseFormatFixed = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
+ self.baseFormatProp = (-402653169, 0, 200, 0, 0, 0, 49, 'Arial')
+ self.bUseFixed = 1
+ self.styles = {} # Indexed by name
+ self.styles_by_id = {} # Indexed by allocated ID.
+
+ def GetSampleText(self):
+ return "Sample Text for the Format Dialog"
+
+ def ColorSeg(self, start, end, styleName):
+ end = end+1
+# assert end-start>=0, "Can't have negative styling"
+ stylenum = self.styles[styleName].stylenum
+ while start 0:
+ stylenum = scintilla.SCIGetStyleAt(start - 1)
+ styleStart = self.GetStyleByNum(stylenum).name
+ else:
+ styleStart = None
+# trace("Coloring", start, end, end-start, len(stringVal), styleStart, self.scintilla.SCIGetCharAt(start))
+ scintilla.SCIStartStyling(start, 31)
+ self.style_buffer = array.array("c", chr(0)*len(stringVal))
+ self.ColorizeString(stringVal, styleStart)
+ scintilla.SCISetStylingEx(self.style_buffer)
+ self.style_buffer = None
+# trace("After styling, end styled is", self.scintilla.SCIGetEndStyled())
+ if self.bCompleteWhileIdle and not self.bHaveIdleHandler and end!=-1 and end < scintilla.GetTextLength():
+ self.bHaveIdleHandler = 1
+ win32ui.GetApp().AddIdleHandler(self.DoMoreColoring)
+ # Kicking idle makes the app seem slower when initially repainting!
+# win32ui.GetMainFrame().PostMessage(WM_KICKIDLE, 0, 0)
+
+ def DoMoreColoring(self, handler, count):
+ try:
+ scintilla = self.scintilla
+ endStyled = scintilla.SCIGetEndStyled()
+ lineStartStyled = scintilla.LineFromChar(endStyled)
+ start = scintilla.LineIndex(lineStartStyled)
+ end = scintilla.LineIndex(lineStartStyled+1)
+
+ finished = end >= scintilla.GetTextLength()
+ self.Colorize(start, end)
+ except (win32ui.error, AttributeError):
+ # Window may have closed before we finished - no big deal!
+ finished = 1
+
+ if finished:
+ self.bHaveIdleHandler = 0
+ win32ui.GetApp().DeleteIdleHandler(handler)
+ return not finished
+
+ # Some functions for loading and saving preferences. By default
+ # an INI file (well, MFC maps this to the registry) is used.
+ def LoadPreferences(self):
+ self.baseFormatFixed = eval(self.LoadPreference("Base Format Fixed", str(self.baseFormatFixed)))
+ self.baseFormatProp = eval(self.LoadPreference("Base Format Proportional", str(self.baseFormatProp)))
+ self.bUseFixed = int(self.LoadPreference("Use Fixed", 1))
+ for style in self.styles.values():
+ new = self.LoadPreference(style.name, str(style.format))
+ style.format = eval(new)
+
+ def LoadPreference(self, name, default):
+ return win32ui.GetProfileVal("Format", name, default)
+
+ def SavePreferences(self):
+ self.SavePreference("Base Format Fixed", str(self.baseFormatFixed))
+ self.SavePreference("Base Format Proportional", str(self.baseFormatProp))
+ self.SavePreference("Use Fixed", self.bUseFixed)
+ for style in self.styles.values():
+ if style.aliased is None:
+ self.SavePreference(style.name, str(style.format))
+ def SavePreference(self, name, value):
+ win32ui.WriteProfileVal("Format", name, value)
+
+# A Formatter that knows how to format Python source
+from keyword import iskeyword
+
+wordstarts = '_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+wordchars = '._0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+operators = '%^&*()-+=|{}[]:;<>,/?!.~'
+
+STYLE_DEFAULT = "Whitespace"
+STYLE_COMMENT = "Comment"
+STYLE_NUMBER = "Number"
+STYLE_STRING = "String"
+STYLE_SQSTRING = "SQ String"
+STYLE_TQSSTRING = "TQS String"
+STYLE_TQDSTRING = "TQD String"
+STYLE_KEYWORD = "Keyword"
+STYLE_CLASS = "Class"
+STYLE_METHOD = "Method"
+STYLE_OPERATOR = "Operator"
+STYLE_IDENTIFIER = "Identifier"
+
+STRING_STYLES = [STYLE_STRING, STYLE_SQSTRING, STYLE_TQSSTRING, STYLE_TQDSTRING]
+
+# the default font tuples to use for Python coloring
+classfmt = (0, 1, 200, 0, 16711680)
+keywordfmt = (0, 1, 200, 0, 8388608)
+cmntfmt = (0, 2, 200, 0, 32768)
+quotefmt = (0, 0, 200, 0, 32896)
+nmberfmt = (0, 0, 200, 0, 8421376)
+methodfmt = (0, 1, 200, 0, 8421376)
+dfltfmt = (0, 0, 200, 0, 8421504)
+opfmt = (0, 1, 200, 0, 0)
+idfmt = (0, 0, 200, 0, 0)
+
+class PythonSourceFormatter(Formatter):
+ def __init__(self, scintilla):
+ Formatter.__init__(self, scintilla)
+ self.SetStyles()
+
+ def GetSampleText(self):
+ return "class Sample(Super):\n def Fn(self):\n # A bitOPy\n dest = 'dest.html'\n timeOut = 1024\n\ta = a + 1\n"
+
+ def LoadStyles(self):
+ pass
+
+ def SetStyles(self):
+ self.RegisterStyle( Style(STYLE_DEFAULT, dfltfmt ) )
+ self.RegisterStyle( Style(STYLE_COMMENT, cmntfmt ) )
+ self.RegisterStyle( Style(STYLE_NUMBER, nmberfmt ) )
+ self.RegisterStyle( Style(STYLE_STRING, quotefmt ) )
+ self.RegisterStyle( Style(STYLE_SQSTRING, STYLE_STRING ) )
+ self.RegisterStyle( Style(STYLE_TQSSTRING, STYLE_STRING ) )
+ self.RegisterStyle( Style(STYLE_TQDSTRING, STYLE_STRING ) )
+ self.RegisterStyle( Style(STYLE_KEYWORD, keywordfmt ) )
+ self.RegisterStyle( Style(STYLE_CLASS, classfmt ) )
+ self.RegisterStyle( Style(STYLE_METHOD, methodfmt ) )
+ self.RegisterStyle( Style(STYLE_OPERATOR, opfmt ) )
+ self.RegisterStyle( Style(STYLE_IDENTIFIER, idfmt ) )
+
+ def GetStringStyle(self, pos):
+ style = self.styles_by_id[self.scintilla.SCIGetStyleAt(pos)]
+ if style.name in STRING_STYLES:
+ return style
+ return None
+
+ def ClassifyWord(self, cdoc, start, end, prevWord):
+ word = cdoc[start:end+1]
+ attr = STYLE_IDENTIFIER
+ if prevWord == "class":
+ attr = STYLE_CLASS
+ elif prevWord == "def":
+ attr = STYLE_METHOD
+ elif cdoc[start] in string.digits:
+ attr = STYLE_NUMBER
+ elif iskeyword(word):
+ attr = STYLE_KEYWORD
+ self.ColorSeg(start, end, attr)
+ return word
+
+ def ColorizeString(self, str, styleStart):
+ if styleStart is None: styleStart = STYLE_DEFAULT
+ return self.ColorizePythonCode(str, 0, styleStart)
+
+ def ColorizePythonCode(self, cdoc, charStart, styleStart):
+ # Straight translation of C++, should do better
+ lengthDoc = len(cdoc)
+ if lengthDoc <= charStart: return
+ prevWord = ""
+ state = styleStart
+ chPrev = chPrev2 = chPrev3 = ' '
+ chNext = cdoc[charStart]
+ chNext2 = cdoc[charStart]
+ startSeg = i = charStart
+ while i < lengthDoc:
+ ch = chNext
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ chNext2 = ' '
+ if i+2 < lengthDoc: chNext2 = cdoc[i+2]
+ if state == STYLE_DEFAULT:
+ if ch in wordstarts:
+ self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
+ state = STYLE_KEYWORD
+ startSeg = i
+ elif ch == '#':
+ self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
+ state = STYLE_COMMENT
+ startSeg = i
+ elif ch == '\"':
+ self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
+ startSeg = i
+ state = STYLE_COMMENT
+ if chNext == '\"' and chNext2 == '\"':
+ i = i + 2
+ state = STYLE_TQDSTRING
+ ch = ' '
+ chPrev = ' '
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ else:
+ state = STYLE_STRING
+ elif ch == '\'':
+ self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
+ startSeg = i
+ state = STYLE_COMMENT
+ if chNext == '\'' and chNext2 == '\'':
+ i = i + 2
+ state = STYLE_TQSSTRING
+ ch = ' '
+ chPrev = ' '
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ else:
+ state = STYLE_SQSTRING
+ elif ch in operators:
+ self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT)
+ self.ColorSeg(i, i, STYLE_OPERATOR)
+ startSeg = i+1
+ elif state == STYLE_KEYWORD:
+ if ch not in wordchars:
+ prevWord = self.ClassifyWord(cdoc, startSeg, i-1, prevWord)
+ state = STYLE_DEFAULT
+ startSeg = i
+ if ch == '#':
+ state = STYLE_COMMENT
+ elif ch == '\"':
+ if chNext == '\"' and chNext2 == '\"':
+ i = i + 2
+ state = STYLE_TQDSTRING
+ ch = ' '
+ chPrev = ' '
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ else:
+ state = STYLE_STRING
+ elif ch == '\'':
+ if chNext == '\'' and chNext2 == '\'':
+ i = i + 2
+ state = STYLE_TQSSTRING
+ ch = ' '
+ chPrev = ' '
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ else:
+ state = STYLE_SQSTRING
+ elif ch in operators:
+ self.ColorSeg(startSeg, i, STYLE_OPERATOR)
+ startSeg = i+1
+ elif state == STYLE_COMMENT:
+ if ch == '\r' or ch == '\n':
+ self.ColorSeg(startSeg, i-1, STYLE_COMMENT)
+ state = STYLE_DEFAULT
+ startSeg = i
+ elif state == STYLE_STRING:
+ if ch == '\\':
+ if chNext == '\"' or chNext == '\'' or chNext == '\\':
+ i = i + 1
+ ch = chNext
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ elif ch == '\"':
+ self.ColorSeg(startSeg, i, STYLE_STRING)
+ state = STYLE_DEFAULT
+ startSeg = i+1
+ elif state == STYLE_SQSTRING:
+ if ch == '\\':
+ if chNext == '\"' or chNext == '\'' or chNext == '\\':
+ i = i+1
+ ch = chNext
+ chNext = ' '
+ if i+1 < lengthDoc: chNext = cdoc[i+1]
+ elif ch == '\'':
+ self.ColorSeg(startSeg, i, STYLE_SQSTRING)
+ state = STYLE_DEFAULT
+ startSeg = i+1
+ elif state == STYLE_TQSSTRING:
+ if ch == '\'' and chPrev == '\'' and chPrev2 == '\'' and chPrev3 != '\\':
+ self.ColorSeg(startSeg, i, STYLE_TQSSTRING)
+ state = STYLE_DEFAULT
+ startSeg = i+1
+ elif state == STYLE_TQDSTRING and ch == '\"' and chPrev == '\"' and chPrev2 == '\"' and chPrev3 != '\\':
+ self.ColorSeg(startSeg, i, STYLE_TQDSTRING)
+ state = STYLE_DEFAULT
+ startSeg = i+1
+ chPrev3 = chPrev2
+ chPrev2 = chPrev
+ chPrev = ch
+ i = i + 1
+ if startSeg < lengthDoc:
+ if state == STYLE_KEYWORD:
+ self.ClassifyWord(cdoc, startSeg, lengthDoc-1, prevWord)
+ else:
+ self.ColorSeg(startSeg, lengthDoc-1, state)
diff --git a/Pythonwin/pywin/scintilla/keycodes.py b/Pythonwin/pywin/scintilla/keycodes.py
new file mode 100644
index 0000000000..f56797c60c
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/keycodes.py
@@ -0,0 +1,151 @@
+import string
+import win32con
+
+char_ranges = [
+ (string.lowercase, -32),
+ (string.digits, 0),
+ ("?><:[]\\", 128),
+ (";", 127),
+ ("=", 126),
+ ("/.,", 144),
+ ("`{}|", 96),
+ ("_", 94),
+ ("-+", 144),
+ ("'", 183),
+ ('"', 188),
+ ("~", 66),
+]
+
+key_name_to_code = {}
+key_code_to_name = {}
+
+_better_names = [
+ ("esc", win32con.VK_ESCAPE),
+ ("enter", win32con.VK_RETURN),
+ ("pgup", win32con.VK_BACK),
+ ("pgdn", win32con.VK_NEXT),
+]
+def _fillmap():
+ # Pull the VK_names from win32con
+ names = filter(lambda entry: entry[:3]=="VK_", win32con.__dict__.keys())
+ for name in names:
+ n = string.lower(name[3:])
+ val = getattr(win32con, name)
+ key_name_to_code[n] = val
+ key_code_to_name[val] = n
+ # Some better named we know about
+ for name, code in _better_names:
+ key_name_to_code[name] = code
+ key_code_to_name[code] = name
+ # And the char_ranges map above
+ for chars, offset in char_ranges:
+ for char in chars:
+ key_name_to_code[char] = ord(char)+offset
+ key_code_to_name[ord(char)+offset] = char
+
+_fillmap()
+
+def get_scan_code(chardesc):
+ return key_name_to_code.get(string.lower(chardesc))
+
+modifiers = {
+ "alt" : win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,
+ "lalt" : win32con.LEFT_ALT_PRESSED,
+ "ralt" : win32con.RIGHT_ALT_PRESSED,
+ "ctrl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
+ "ctl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
+ "control" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
+ "lctrl" : win32con.LEFT_CTRL_PRESSED,
+ "lctl" : win32con.LEFT_CTRL_PRESSED,
+ "rctrl" : win32con.RIGHT_CTRL_PRESSED,
+ "rctl" : win32con.RIGHT_CTRL_PRESSED,
+ "shift" : win32con.SHIFT_PRESSED,
+ "key" : 0, # ignore key tag.
+}
+
+def parse_key_name(name):
+ name = name + "-" # Add a sentinal
+ start = pos = 0
+ max = len(name)
+ flags = 0
+ scancode = None
+ while pos" % scancode )
+ sep = "+"
+ if sep in parts: sep = "-"
+ return string.join(map(string.capitalize, parts), sep)
+
+def _psc(char):
+ sc = get_scan_code(char)
+ print "Char %s -> %d -> %s" % (`char`, sc, key_code_to_name.get(sc))
+
+def test1():
+ for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":
+ _psc(ch)
+ for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:
+ _psc(code)
+
+def _pkn(n):
+ scancode, flags = parse_key_name(n)
+ print "%s -> %s,%s -> %s" % (n, scancode, flags, make_key_name(scancode, flags))
+
+def test2():
+ _pkn("ctrl+alt-shift+x")
+ _pkn("ctrl-home")
+ _pkn("Shift-+")
+ _pkn("Shift--")
+ _pkn("Shift+-")
+ _pkn("Shift++")
+ _pkn("LShift-+")
+ _pkn("ctl+home")
+ _pkn("ctl+enter")
+ _pkn("alt+return")
+ _pkn("Alt+/")
+
+if __name__=='__main__':
+ test2()
\ No newline at end of file
diff --git a/Pythonwin/pywin/scintilla/scintillacon.py b/Pythonwin/pywin/scintilla/scintillacon.py
new file mode 100644
index 0000000000..ee982cab5e
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/scintillacon.py
@@ -0,0 +1,140 @@
+# Generated by h2py from scintilla.h
+def SCINTILLA(obj): return GTK_CHECK_CAST (obj, scintilla_get_type (), ScintillaObject)
+
+def IS_SCINTILLA(obj): return GTK_CHECK_TYPE (obj, scintilla_get_type ())
+
+SCI_START = 2000
+SCI_ADDTEXT = SCI_START + 1
+SCI_ADDSTYLEDTEXT = SCI_START + 2
+SCI_INSERTTEXT = SCI_START + 3
+SCI_CLEARALL = SCI_START + 4
+SCI_GETLENGTH = SCI_START + 6
+SCI_GETCHARAT = SCI_START + 7
+SCI_GETCURRENTPOS = SCI_START + 8
+SCI_GETANCHOR = SCI_START + 9
+SCI_GETSTYLEAT = SCI_START + 10
+SCI_REDO = SCI_START + 11
+SCI_SETUNDOCOLLECTION = SCI_START + 12
+SCI_SELECTALL = SCI_START + 13
+SCI_SETSAVEPOINT = SCI_START + 14
+SCI_GETSTYLEDTEXT = SCI_START + 15
+SCI_GETVIEWWS = SCI_START + 20
+SCI_SETVIEWWS = SCI_START + 21
+SCI_CHANGEPOSITION = SCI_START + 22
+SCI_GOTOLINE = SCI_START + 24
+SCI_GOTOPOS = SCI_START + 25
+SCI_SETANCHOR = SCI_START + 26
+SCI_GETCURLINE = SCI_START + 27
+SCI_GETENDSTYLED = SCI_START + 28
+SCI_GETEOLMODE = SCI_START + 30
+SCI_SETEOLMODE = SCI_START + 31
+SC_EOL_CRLF = 0
+SC_EOL_CR = 1
+SC_EOL_LF = 2
+SCI_STARTSTYLING = SCI_START + 32
+SCI_SETSTYLING = SCI_START + 33
+SCI_SETMARGINWIDTH = SCI_START + 34
+SCI_SETBUFFEREDDRAW = SCI_START + 35
+SCI_SETTABWIDTH = SCI_START + 36
+SCI_SETCODEPAGE = SCI_START + 37
+SCI_SETLINENUMBERWIDTH = SCI_START + 38
+SCI_SETUSEPALETTE = SCI_START + 39
+MARKER_MAX = 31
+SC_MARK_CIRCLE = 0
+SC_MARK_ROUNDRECT = 1
+SC_MARK_ARROW = 2
+SC_MARK_SMALLRECT = 3
+SC_MARK_SHORTARROW = 4
+SCI_MARKERDEFINE = SCI_START + 40
+SCI_MARKERSETFORE = SCI_START + 41
+SCI_MARKERSETBACK = SCI_START + 42
+SCI_MARKERADD = SCI_START + 43
+SCI_MARKERDELETE = SCI_START + 44
+SCI_MARKERDELETEALL = SCI_START + 45
+SCI_MARKERGET = SCI_START + 46
+SCI_MARKERNEXT = SCI_START + 47
+STYLE_MAX = 31
+SCI_STYLECLEARALL = SCI_START + 50
+SCI_STYLESETFORE = SCI_START + 51
+SCI_STYLESETBACK = SCI_START + 52
+SCI_STYLESETBOLD = SCI_START + 53
+SCI_STYLESETITALIC = SCI_START + 54
+SCI_STYLESETSIZE = SCI_START + 55
+SCI_STYLESETFONT = SCI_START + 56
+SCI_SETFORE = SCI_START + 60
+SCI_SETBACK = SCI_START + 61
+SCI_SETBOLD = SCI_START + 62
+SCI_SETITALIC = SCI_START + 63
+SCI_SETSIZE = SCI_START + 64
+SCI_SETFONT = SCI_START + 65
+SCI_SETSELFORE = SCI_START + 67
+SCI_SETSELBACK = SCI_START + 68
+SCI_SETCARETFORE = SCI_START + 69
+SCI_ASSIGNCMDKEY = SCI_START + 70
+SCI_CLEARCMDKEY = SCI_START + 71
+SCI_CLEARALLCMDKEYS = SCI_START + 72
+SCI_SETSTYLINGEX = SCI_START + 73
+SCI_APPENDUNDOSTARTACTION = SCI_START + 74
+INDIC_MAX = 2
+INDIC_PLAIN = 0
+INDIC_SQUIGGLE = 1
+INDIC_TT = 2
+INDIC0_MASK = 32
+INDIC1_MASK = 64
+INDIC2_MASK = 128
+INDICS_MASK = (INDIC0_MASK | INDIC1_MASK | INDIC2_MASK)
+SCI_INDICSETSTYLE = SCI_START + 80
+SCI_INDICGETSTYLE = SCI_START + 81
+SCI_INDICSETFORE = SCI_START + 82
+SCI_INDICGETFORE = SCI_START + 83
+SCI_AUTOCSHOW = SCI_START + 100
+SCI_AUTOCCANCEL = SCI_START + 101
+SCI_AUTOCACTIVE = SCI_START + 102
+SCI_AUTOCPOSSTART = SCI_START + 103
+SCI_AUTOCCOMPLETE = SCI_START + 104
+SCI_AUTOCSTOPS = SCI_START + 105
+SCI_CALLTIPSHOW = SCI_START + 200
+SCI_CALLTIPCANCEL = SCI_START + 201
+SCI_CALLTIPACTIVE = SCI_START + 202
+SCI_CALLTIPPOSSTART = SCI_START + 203
+SCI_CALLTIPSETHLT = SCI_START + 204
+SCI_LINEDOWN = SCI_START + 300
+SCI_LINEDOWNEXTEND = SCI_START + 301
+SCI_LINEUP = SCI_START + 302
+SCI_LINEUPEXTEND = SCI_START + 303
+SCI_CHARLEFT = SCI_START + 304
+SCI_CHARLEFTEXTEND = SCI_START + 305
+SCI_CHARRIGHT = SCI_START + 306
+SCI_CHARRIGHTEXTEND = SCI_START + 307
+SCI_WORDLEFT = SCI_START + 308
+SCI_WORDLEFTEXTEND = SCI_START + 309
+SCI_WORDRIGHT = SCI_START + 310
+SCI_WORDRIGHTEXTEND = SCI_START + 311
+SCI_HOME = SCI_START + 312
+SCI_HOMEEXTEND = SCI_START + 313
+SCI_LINEEND = SCI_START + 314
+SCI_LINEENDEXTEND = SCI_START + 315
+SCI_DOCUMENTSTART = SCI_START + 316
+SCI_DOCUMENTSTARTEXTEND = SCI_START + 317
+SCI_DOCUMENTEND = SCI_START + 318
+SCI_DOCUMENTENDEXTEND = SCI_START + 319
+SCI_PAGEUP = SCI_START + 320
+SCI_PAGEUPEXTEND = SCI_START + 321
+SCI_PAGEDOWN = SCI_START + 322
+SCI_PAGEDOWNEXTEND = SCI_START + 323
+SCI_EDITTOGGLEOVERTYPE = SCI_START + 324
+SCI_CANCEL = SCI_START + 325
+SCI_DELETEBACK = SCI_START + 326
+SCI_TAB = SCI_START + 327
+SCI_BACKTAB = SCI_START + 328
+SCI_NEWLINE = SCI_START + 329
+SCI_FORMFEED = SCI_START + 330
+SCI_VCHOME = SCI_START + 331
+SCI_VCHOMEEXTEND = SCI_START + 332
+SCI_GRABFOCUS = SCI_START + 400
+SCN_STYLENEEDED = 2000
+SCN_CHARADDED = 2001
+SCN_SAVEPOINTREACHED = 2002
+SCN_SAVEPOINTLEFT = 2003
+SCN_MODIFYATTEMPTRO = 2004
+SCN_KEY = 2005
diff --git a/Pythonwin/pywin/scintilla/view.py b/Pythonwin/pywin/scintilla/view.py
new file mode 100644
index 0000000000..d1c6fff760
--- /dev/null
+++ b/Pythonwin/pywin/scintilla/view.py
@@ -0,0 +1,365 @@
+# A general purpose MFC CCtrlView view that uses Scintilla.
+
+import control
+import IDLEenvironment # IDLE emulation.
+from pywin.mfc import docview
+from scintillacon import *
+import win32con
+import win32api
+import win32ui
+import afxres
+import string
+import array
+import sys
+import types
+import __main__ # for attribute lookup
+import bindings
+import keycodes
+import regex
+
+wordbreaks = "._" + string.uppercase + string.lowercase + string.digits
+
+patImport=regex.symcomp('import \(.*\)')
+
+_event_commands = [
+ # File menu
+ "win32ui.ID_FILE_LOCATE", "win32ui.ID_FILE_CHECK", "afxres.ID_FILE_CLOSE",
+ "afxres.ID_FILE_NEW", "afxres.ID_FILE_OPEN", "afxres.ID_FILE_SAVE",
+ "afxres.ID_FILE_SAVE_AS", "win32ui.ID_FILE_SAVE_ALL",
+ # Edit menu
+ "afxres.ID_EDIT_UNDO", "afxres.ID_EDIT_REDO", "afxres.ID_EDIT_CUT",
+ "afxres.ID_EDIT_COPY", "afxres.ID_EDIT_PASTE", "afxres.ID_EDIT_SELECT_ALL",
+ "afxres.ID_EDIT_FIND", "afxres.ID_EDIT_REPEAT", "afxres.ID_EDIT_REPLACE",
+ # View menu
+ "win32ui.ID_VIEW_WHITESPACE", "win32ui.ID_VIEW_FIXED_FONT",
+ "win32ui.ID_VIEW_BROWSE", "win32ui.ID_VIEW_INTERACTIVE",
+ # Window menu
+ "afxres.ID_WINDOW_ARRANGE", "afxres.ID_WINDOW_CASCADE",
+ "afxres.ID_WINDOW_NEW", "afxres.ID_WINDOW_SPLIT",
+ "afxres.ID_WINDOW_TILE_HORZ", "afxres.ID_WINDOW_TILE_VERT",
+ # Others
+ "afxres.ID_APP_EXIT", "afxres.ID_APP_ABOUT",
+]
+
+_extra_event_commands = [
+ ("EditDelete", afxres.ID_EDIT_CLEAR),
+ ("LocateModule", win32ui.ID_FILE_LOCATE),
+ ("GotoLine", win32ui.ID_EDIT_GOTO_LINE),
+ ("DbgBreakpointToggle", win32ui.IDC_DBG_ADD),
+ ("DbgGo", win32ui.IDC_DBG_GO),
+ ("DbgStepOver", win32ui.IDC_DBG_STEPOVER),
+ ("DbgStep", win32ui.IDC_DBG_STEP),
+ ("DbgStepOut", win32ui.IDC_DBG_STEPOUT),
+ ("DbgBreakpointClearAll", win32ui.IDC_DBG_CLEAR),
+ ("DbgClose", win32ui.IDC_DBG_CLOSE),
+]
+
+event_commands = []
+def _CreateEvents():
+ for name in _event_commands:
+ val = eval(name)
+ name_parts = string.split(name, "_")[1:]
+ name_parts = map(string.capitalize, name_parts)
+ event =string.join(name_parts,'')
+ event_commands.append(event, val)
+ for name, id in _extra_event_commands:
+ event_commands.append(name, id)
+
+_CreateEvents()
+del _event_commands; del _extra_event_commands
+
+command_reflectors = [
+ (win32ui.ID_EDIT_UNDO, win32con.WM_UNDO),
+ (win32ui.ID_EDIT_REDO, SCI_REDO),
+ (win32ui.ID_EDIT_CUT, win32con.WM_CUT),
+ (win32ui.ID_EDIT_COPY, win32con.WM_COPY),
+ (win32ui.ID_EDIT_PASTE, win32con.WM_PASTE),
+ (win32ui.ID_EDIT_CLEAR, win32con.WM_CLEAR),
+ (win32ui.ID_EDIT_SELECT_ALL, SCI_SELECTALL),
+]
+
+# Supposed to look like an MFC CEditView, but
+# also supports IDLE extensions and other source code generic features.
+class CScintillaView(docview.CtrlView, control.CScintillaColorEditInterface):
+ def __init__(self, doc):
+ docview.CtrlView.__init__(self, doc, "Scintilla", win32con.WS_CHILD | win32con.WS_VSCROLL | win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE)
+ self._tabWidth = 8 # Mirror of what we send to Scintilla - never change this directly
+ self.bAutoCompleteAttributes = 1
+ self.bShowCallTips = 1
+ self.bindings = bindings.BindingsManager(self)
+
+ self.idle = IDLEenvironment.IDLEEditorWindow(self)
+ self.idle.IDLEExtension("AutoExpand")
+ def SendScintilla(self, msg, w=0, l=0):
+ return self._obj_.SendMessage(msg, w, l)
+
+ def SCISetTabWidth(self, width):
+ # I need to remember the tab-width for the AutoIndent extension. This may go.
+ self._tabWidth = width
+ control.CScintillaEditInterface.SCISetTabWidth(self, width)
+
+ def GetTabWidth(self):
+ return self._tabWidth
+
+ def HookHandlers(self):
+ parent = self.GetParentFrame()
+
+ # Create events for all the menu names.
+ for name, val in event_commands:
+# handler = lambda id, code, tosend=val, parent=parent: parent.OnCommand(tosend, 0) and 0
+ self.bindings.bind(name, None, cid=val)
+
+ # Hook commands that do nothing other than send Scintilla messages.
+ for command, reflection in command_reflectors:
+ handler = lambda id, code, ss=self.SendScintilla, tosend=reflection: ss(tosend) and 0
+ self.HookCommand(handler, command)
+
+ parent.HookNotify(self.OnSavePointReached, SCN_SAVEPOINTREACHED)
+ parent.HookNotify(self.OnSavePointLeft, SCN_SAVEPOINTLEFT)
+ self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE)
+ self.HookCommandUpdate(self.OnUpdateViewWS, win32ui.ID_VIEW_WHITESPACE)
+ self.HookCommand(self.OnCmdViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
+ self.HookCommandUpdate(self.OnUpdateViewFixedFont, win32ui.ID_VIEW_FIXED_FONT)
+ self.HookCommand(self.OnCmdFileLocate, win32ui.ID_FILE_LOCATE)
+ self.HookCommand(self.OnCmdEditFind, win32ui.ID_EDIT_FIND)
+ self.HookCommand(self.OnCmdEditRepeat, win32ui.ID_EDIT_REPEAT)
+ self.HookCommand(self.OnCmdEditReplace, win32ui.ID_EDIT_REPLACE)
+ self.HookCommand(self.OnCmdGotoLine, win32ui.ID_EDIT_GOTO_LINE)
+ # Key bindings.
+ self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
+ self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
+ # Hook colorizer.
+ self.HookStyleNotify()
+
+ def OnInitialUpdate(self):
+ self.SCISetSavePoint()
+ self.SCISetUndoCollection(1)
+
+ self.HookHandlers()
+
+ # Tell scintilla what characters should abort auto-complete.
+ self.SCIAutoCStops(string.whitespace+"()[]:;+-/*=\\?'!#@$%^&,<>\"'|" )
+
+ # Load the configuration information.
+ self.OnConfigChange()
+ try:
+ self._SetLoadedText(self.GetDocument().text)
+ except AttributeError: # Not one of our docs - thats OK, but the text is their job!
+ pass
+
+ self.SetSel()
+
+ def _GetSubConfigNames(self):
+ return None # By default we use only sections without sub-sections.
+
+ def OnConfigChange(self):
+ self.bindings.prepare_configure()
+ try:
+ self.DoConfigChange()
+ self.Reformat(1)
+ finally:
+ self.bindings.complete_configure()
+
+ def DoConfigChange(self):
+ # Bit of a hack I dont kow what to do about?
+ from pywin.framework.editor import GetEditorOption
+ self.bAutoCompleteAttributes = GetEditorOption("Autocomplete Attributes", 1)
+ self.bShowCallTips = GetEditorOption("Show Call Tips", 1)
+ # Update the key map and extension data.
+ configManager.configure(self, self._GetSubConfigNames())
+ if configManager.last_error:
+ win32ui.MessageBox(configManager.last_error, "Configuration Error")
+
+ def OnDestroy(self, msg):
+ self.bindings.close()
+ self.bindings = None
+ self.idle.close()
+ self.idle = None
+ control.CScintillaColorEditInterface.close(self)
+ return docview.CtrlView.OnDestroy(self, msg)
+
+ # Helper to add an event to a menu.
+ def AppendMenu(self, menu, text="", event=None, flags = None, checked=0):
+ if event is None:
+ assert flags is not None, "No event or custom flags!"
+ cmdid = 0
+ else:
+ cmdid = self.bindings.get_command_id(event)
+ if cmdid is None:
+ # No event of that name - no point displaying it.
+ print 'View.AppendMenu(): Unknown event "%s" specified for menu text "%s" - ignored' % (event, text)
+ return
+ keyname = configManager.get_key_binding( event, self._GetSubConfigNames() )
+ if keyname is not None:
+ text = text + "\t" + keyname
+ if flags is None: flags = win32con.MF_STRING|win32con.MF_ENABLED
+ if checked: flags = flags | win32con.MF_CHECKED
+ menu.AppendMenu(flags, cmdid, text)
+
+ def OnKeyDown(self, msg):
+ return self.bindings.fire_key_event( msg )
+
+ def GotoEndOfFileEvent(self, event):
+ self.SetSel(-1)
+
+ def KeyDotEvent(self, event):
+ self.SCIAddText(".")
+ if self.bAutoCompleteAttributes:
+ self._AutoComplete()
+
+ # View Whitespace UI.
+ def OnCmdViewWS(self, cmd, code): # Handle the menu command
+ viewWS = self.SCIGetViewWS()
+ self.SCISetViewWS(not viewWS)
+ def OnUpdateViewWS(self, cmdui): # Update the tick on the UI.
+ cmdui.SetCheck(self.SCIGetViewWS())
+ cmdui.Enable()
+
+ def OnCmdViewFixedFont(self, cmd, code): # Handle the menu command
+ self._GetColorizer().bUseFixed = not self._GetColorizer().bUseFixed
+ self.Reformat(0)
+ def OnUpdateViewFixedFont(self, cmdui): # Update the tick on the UI.
+ c = self._GetColorizer()
+ if c is not None: cmdui.SetCheck(c.bUseFixed)
+ cmdui.Enable(c is not None)
+
+ def OnCmdEditFind(self, cmd, code):
+ import find
+ find.ShowFindDialog()
+ def OnCmdEditRepeat(self, cmd, code):
+ import find
+ find.FindNext()
+ def OnCmdEditReplace(self, cmd, code):
+ import find
+ find.ShowReplaceDialog()
+
+ def OnCmdFileLocate(self, cmd, id):
+ line=string.strip(self.GetLine())
+ import pywin.framework.scriptutils
+ if patImport.match(line)==len(line):
+ # Module name on this line - locate that!
+ modName = patImport.group('name')
+ fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
+ if fileName is None:
+ win32ui.SetStatusText("Can't locate module %s" % modName)
+ return 1 # Let the default get it.
+ else:
+ win32ui.GetApp().OpenDocumentFile(fileName)
+ else:
+ # Just to a "normal" locate - let the default handler get it.
+ return 1
+ return 0
+
+ def OnCmdGotoLine(self, cmd, id):
+ try:
+ lineNo = string.atoi(raw_input("Enter Line Number"))
+ except (ValueError, KeyboardInterrupt):
+ return 0
+ self.SCIGotoLine(lineNo-1)
+ return 0
+
+ # #####################
+ # File related functions
+ # Helper to transfer text from the MFC document to the control.
+ def OnSavePointReached(self, std, extra):
+ self.GetDocument().SetModifiedFlag(0)
+
+ def OnSavePointLeft(self, std, extra):
+ self.GetDocument().SetModifiedFlag(1)
+
+ def _SetLoadedText(self, text):
+ if self.IsWindow():
+ # Turn off undo collection while loading
+ self.SendScintilla(SCI_SETUNDOCOLLECTION, 0, 0)
+ # Make sure the control isnt read-only
+ self.SetReadOnly(0)
+
+ doc = self.GetDocument()
+ sm = text
+ if sm:
+ sma = array.array('c', sm)
+ (a,l) = sma.buffer_info()
+ self.SendScintilla(SCI_CLEARALL)
+ self.SendScintilla(SCI_ADDTEXT, l, a)
+ sma = None
+ self.SendScintilla(SCI_SETUNDOCOLLECTION, 1, 0)
+ self.SendScintilla(win32con.EM_EMPTYUNDOBUFFER, 0, 0)
+
+ def SaveTextFile(self, filename):
+ doc = self.GetDocument()
+ s = self.GetTextRange()
+ f = open(filename, 'wb')
+ f.write(s)
+ f.close()
+ doc.SetModifiedFlag(0)
+ return 1
+
+ def _AutoComplete(self):
+ ob = self._GetObjectAtPos()
+ self.SCICancel() # Cancel tooltips and old auto-complete lists.
+ if ob is not None:
+ items = []
+ try:
+ items = items + dir(ob)
+ except AttributeError:
+ pass # object has no __dict__
+ try:
+ items = items + dir(ob.__class__)
+ except AttributeError:
+ pass
+ # Reduce __special_names__
+ items = filter(lambda word: word[:2]!='__' or word[-2:]!='__', items)
+ if items:
+ self.SCIAutoCShow(items)
+
+ def _GetObjectAtPos(self, pos=-1):
+ left, right = self._GetWordSplit()
+ if left: # It is an attribute lookup
+ # How is this for a hack!
+ namespace = sys.modules.copy()
+ namespace.update(__main__.__dict__)
+ try:
+ return eval(left, namespace)
+ except:
+ pass
+ return None
+
+ def _GetWordSplit(self, pos=-1):
+ if pos==-1: pos = self.GetSel()[0]-1 # Character before current one
+ limit = self.GetTextLength()
+ before = []
+ after = []
+ index = pos-1
+ while index>=0:
+ char = self.SCIGetCharAt(index)
+ if char not in wordbreaks: break
+ before.insert(0, char)
+ index = index-1
+ index = pos
+ while index<=limit:
+ char = self.SCIGetCharAt(index)
+ if char not in wordbreaks: break
+ after.append(char)
+ index=index+1
+ return string.join(before,''), string.join(after,'')
+
+def LoadConfiguration():
+ global configManager
+ # Bit of a hack I dont kow what to do about?
+ from config import ConfigManager
+ configName = rc = win32ui.GetProfileVal("Editor", "Keyboard Config", "default")
+ configManager = ConfigManager(configName)
+ if configManager.last_error:
+ bTryDefault = 0
+ msg = "Error loading configuration '%s'\n\n%s" % (configName, configManager.last_error)
+ if configName != "default":
+ msg = msg + "\n\nThe default configuration will be loaded."
+ bTryDefault = 1
+ win32ui.MessageBox(msg)
+ if bTryDefault:
+ configManager = ConfigManager("default")
+ if configManager.last_error:
+ win32ui.MessageBox("Error loading configuration 'default'\n\n%s" % (configManager.last_error))
+
+configManager = None
+LoadConfiguration()
diff --git a/Pythonwin/pywin/tools/TraceCollector.py b/Pythonwin/pywin/tools/TraceCollector.py
new file mode 100644
index 0000000000..53b9df533f
--- /dev/null
+++ b/Pythonwin/pywin/tools/TraceCollector.py
@@ -0,0 +1,59 @@
+# win32traceutil like utility for Pythonwin
+import thread
+import win32trace, win32event, win32api
+from pywin.framework import winout
+
+outputWindow = None
+
+def CollectorThread(stopEvent, file):
+ win32trace.InitRead()
+ handle = win32trace.GetHandle()
+ # Run this thread at a lower priority to the main message-loop (and printing output)
+ # thread can keep up
+ import win32process
+ win32process.SetThreadPriority(win32api.GetCurrentThread(), win32process.THREAD_PRIORITY_BELOW_NORMAL)
+
+ try:
+ while 1:
+ rc = win32event.WaitForMultipleObjects((handle, stopEvent), 0, win32event.INFINITE)
+ if rc == win32event.WAIT_OBJECT_0:
+ file.write(win32trace.read())
+ else:
+ # Stop event
+ break
+ finally:
+ win32trace.TermRead()
+ print "Thread dieing"
+
+class WindowOutput(winout.WindowOutput):
+ def __init__(self, *args):
+ apply(winout.WindowOutput.__init__, (self,)+args)
+ self.hStopThread = win32event.CreateEvent(None, 0, 0, None)
+ thread.start_new(CollectorThread, (self.hStopThread, self))
+ def _StopThread(self):
+ win32event.SetEvent(self.hStopThread)
+ self.hStopThread = None
+ def Close(self):
+ self._StopThread()
+ winout.WindowOutput.Close(self)
+# def OnViewDestroy(self, frame):
+# return winout.WindowOutput.OnViewDestroy(self, frame)
+# def Create(self, title=None, style = None):
+# rc = winout.WindowOutput.Create(self, title, style)
+ return rc
+
+
+def MakeOutputWindow():
+ # Note that it will not show until the first string written or
+ # you pass bShow = 1
+ global outputWindow
+ if outputWindow is None:
+ title = "Python Trace Collector"
+ # queueingFlag doesnt matter, as all output will come from new thread
+
+ outputWindow = WindowOutput(title, title)
+ outputWindow.write('')
+ return outputWindow
+
+if __name__=='__main__':
+ MakeOutputWindow()
diff --git a/Pythonwin/pywin/tools/__init__.py b/Pythonwin/pywin/tools/__init__.py
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/Pythonwin/pywin/tools/__init__.py
@@ -0,0 +1,2 @@
+
+
diff --git a/Pythonwin/pywin/tools/browseProjects.py b/Pythonwin/pywin/tools/browseProjects.py
new file mode 100644
index 0000000000..b291b564e9
--- /dev/null
+++ b/Pythonwin/pywin/tools/browseProjects.py
@@ -0,0 +1,254 @@
+import hierlist, string, regutil, os
+import win32con, win32ui, win32api
+import commctrl
+from pywin.mfc import dialog
+import glob
+import pyclbr
+import pywin.framework.scriptutils
+import afxres
+
+class HLIErrorItem(hierlist.HierListItem):
+ def __init__(self, text):
+ self.text = text
+ hierlist.HierListItem.__init__(self)
+ def GetText(self):
+ return self.text
+
+class HLICLBRItem(hierlist.HierListItem):
+ def __init__(self, name, file, lineno, suffix = ""):
+ self.name = name
+ self.file = file
+ self.lineno = lineno
+ self.suffix = suffix
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+ def GetText(self):
+ return self.name + self.suffix
+ def TakeDefaultAction(self):
+ if self.file:
+ pywin.framework.scriptutils.JumpToDocument(self.file, self.lineno, bScrollToTop=1)
+ else:
+ win32ui.SetStatusText("The source of this object is unknown")
+ def PerformItemSelected(self):
+ if self.file is None:
+ msg = "%s - source can not be located." % (self.name, )
+ else:
+ msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
+ win32ui.SetStatusText(msg)
+
+class HLICLBRClass(HLICLBRItem):
+ def __init__(self, clbrclass, suffix = ""):
+ try:
+ name = clbrclass.name
+ file = clbrclass.file
+ lineno = clbrclass.lineno
+ self.super = clbrclass.super
+ self.methods = clbrclass.methods
+ except AttributeError:
+ name = clbrclass
+ file = lineno = None
+ self.super = []; self.methods = {}
+ HLICLBRItem.__init__(self, name, file, lineno, suffix)
+ def GetSubList(self):
+ ret = []
+ for c in self.super:
+ ret.append(HLICLBRClass(c, " (Parent class)"))
+ for meth, lineno in self.methods.items():
+ ret.append(HLICLBRMethod(meth, self.file, lineno, " (method)"))
+ return ret
+ def IsExpandable(self):
+ return len(self.methods) + len(self.super)
+ def GetBitmapColumn(self):
+ return 21
+
+class HLICLBRFunction(HLICLBRClass):
+ def GetBitmapColumn(self):
+ return 22
+class HLICLBRMethod(HLICLBRItem):
+ def GetBitmapColumn(self):
+ return 22
+
+class HLIModuleItem(hierlist.HierListItem):
+ def __init__(self, path):
+ hierlist.HierListItem.__init__(self)
+ self.path = path
+ def GetText(self):
+ return os.path.split(self.path)[1] + " (module)"
+ def IsExpandable(self):
+ return 1
+ def TakeDefaultAction(self):
+ win32ui.GetApp().OpenDocumentFile( self.path )
+ def GetBitmapColumn(self):
+ col = 4 # Default
+ try:
+ if win32api.GetFileAttributes(self.path) & win32con.FILE_ATTRIBUTE_READONLY:
+ col = 5
+ except win32api.error:
+ pass
+ return col
+ def GetSubList(self):
+ mod, path = pywin.framework.scriptutils.GetPackageModuleName(self.path)
+ win32ui.SetStatusText("Building class list - please wait...", 1)
+ win32ui.DoWaitCursor(1)
+ try:
+ try:
+ reader = pyclbr.readmodule_ex # Post 1.5.2 interface.
+ extra_msg = " or functions"
+ except AttributeError:
+ reader = pyclbr.readmodule
+ extra_msg = ""
+ data = reader(mod, [path])
+ if data:
+ ret = []
+ for item in data.values():
+ if item.__class__ != pyclbr.Class: # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
+ ret.append(HLICLBRFunction( item, " (function)" ) )
+ else:
+ ret.append(HLICLBRClass( item, " (class)") )
+ ret.sort()
+ return ret
+ else:
+ return [HLIErrorItem("No Python classes%s in module." % (extra_msg,))]
+ finally:
+ win32ui.DoWaitCursor(0)
+ win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
+
+def MakePathSubList(path):
+ ret = []
+ for filename in glob.glob(os.path.join(path,'*')):
+ if os.path.isdir(filename) and os.path.isfile(os.path.join(filename, "__init__.py")):
+ ret.append(HLIDirectoryItem(filename, os.path.split(filename)[1]))
+ else:
+ if string.lower(os.path.splitext(filename)[1]) in ['.py', '.pyw']:
+ ret.append(HLIModuleItem(filename))
+ return ret
+
+class HLIDirectoryItem(hierlist.HierListItem):
+ def __init__(self, path, displayName = None, bSubDirs = 0):
+ hierlist.HierListItem.__init__(self)
+ self.path = path
+ self.bSubDirs = bSubDirs
+ if displayName:
+ self.displayName = displayName
+ else:
+ self.displayName = path
+ def IsExpandable(self):
+ return 1
+ def GetText(self):
+ return self.displayName
+
+ def GetSubList(self):
+ ret = MakePathSubList(self.path)
+ if os.path.split(self.path)[1] == "win32com": # Complete and utter hack for win32com.
+ try:
+ path = win32api.GetFullPathName(os.path.join(self.path, "..\\win32comext"))
+ ret = ret + MakePathSubList(path)
+ except win32ui.error:
+ pass
+ return ret
+
+class HLIProjectRoot(hierlist.HierListItem):
+ def __init__(self, projectName, displayName = None):
+ hierlist.HierListItem.__init__(self)
+ self.projectName = projectName
+ self.displayName = displayName or projectName
+ def GetText(self):
+ return self.displayName
+ def IsExpandable(self):
+ return 1
+ def GetSubList(self):
+ paths = regutil.GetRegisteredNamedPath(self.projectName)
+ pathList = string.split(paths,";")
+ if len(pathList)==1: # Single dir - dont bother putting the dir in
+ ret = MakePathSubList(pathList[0])
+ else:
+ ret = map( HLIDirectoryItem, pathList )
+ return ret
+
+class HLIRoot(hierlist.HierListItem):
+ def __init__(self):
+ hierlist.HierListItem.__init__(self)
+ def IsExpandable(self):
+ return 1
+ def GetSubList(self):
+ keyStr = regutil.BuildDefaultPythonKey() + "\\PythonPath"
+ hKey = win32api.RegOpenKey(regutil.GetRootKey(), keyStr)
+ try:
+ ret = []
+ ret.append(HLIProjectRoot("", "Standard Python Library")) # The core path.
+ index = 0
+ while 1:
+ try:
+ ret.append(HLIProjectRoot(win32api.RegEnumKey(hKey, index)))
+ index = index + 1
+ except win32api.error:
+ break
+ return ret
+ finally:
+ win32api.RegCloseKey(hKey)
+
+class dynamic_browser (dialog.Dialog):
+ style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE
+ cs = (
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE |
+ commctrl.TVS_HASLINES |
+ commctrl.TVS_LINESATROOT |
+ commctrl.TVS_HASBUTTONS
+ )
+
+ dt = [
+ ["Python Projects", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
+ ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs]
+ ]
+
+ def __init__ (self, hli_root):
+ dialog.Dialog.__init__ (self, self.dt)
+ self.hier_list = hierlist.HierListWithItems (
+ hli_root,
+ win32ui.IDB_BROWSER_HIER
+ )
+ self.HookMessage (self.on_size, win32con.WM_SIZE)
+
+ def OnInitDialog (self):
+ self.hier_list.HierInit (self)
+ return dialog.Dialog.OnInitDialog (self)
+
+ def on_size (self, params):
+ lparam = params[3]
+ w = win32api.LOWORD(lparam)
+ h = win32api.HIWORD(lparam)
+ self.GetDlgItem (win32ui.IDC_LIST1).MoveWindow((0,0,w,h))
+
+def BrowseDialog():
+ root = HLIRoot()
+ if not root.IsExpandable():
+ raise TypeError, "Browse() argument must have __dict__ attribute, or be a Browser supported type"
+
+ dlg = dynamic_browser (root)
+ dlg.CreateWindow()
+
+def DockableBrowserCreator(parent):
+ root = HLIRoot()
+ hl = hierlist.HierListWithItems (
+ root,
+ win32ui.IDB_BROWSER_HIER
+ )
+
+ style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS
+
+ control = win32ui.CreateTreeCtrl()
+ control.CreateWindow(style, (0, 0, 150, 300), parent, win32ui.IDC_LIST1)
+ list = hl.HierInit (parent, control)
+ return control
+
+def DockablePathBrowser():
+ import pywin.docking.DockingBar
+ bar = pywin.docking.DockingBar.DockingBar()
+ bar.CreateWindow(win32ui.GetMainFrame(), DockableBrowserCreator, "Path Browser", 0x8e0a)
+ bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
+ bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
+ win32ui.GetMainFrame().DockControlBar(bar)
+
+# The "default" entry point
+Browse = DockablePathBrowser
diff --git a/Pythonwin/pywin/tools/browser.py b/Pythonwin/pywin/tools/browser.py
new file mode 100644
index 0000000000..0e92a41634
--- /dev/null
+++ b/Pythonwin/pywin/tools/browser.py
@@ -0,0 +1,401 @@
+# basic module browser.
+
+# usage:
+# >>> import browser
+# >>> browser.Browse()
+# or
+# >>> browser.Browse(your_module)
+import __main__
+import string
+import win32ui
+from pywin.mfc import dialog
+
+import hierlist
+from types import *
+
+
+special_names = [ '__doc__', '__name__', '__self__' ]
+
+#
+# HierList items
+class HLIPythonObject(hierlist.HierListItem):
+ def __init__(self, myobject=None, name=None ):
+ hierlist.HierListItem.__init__(self)
+ self.myobject = myobject
+ self.knownExpandable = None
+ if name:
+ self.name=name
+ else:
+ try:
+ self.name=str(myobject.__name__)
+ except (AttributeError, TypeError):
+ try:
+ r = repr(myobject)
+ if len(r)>20:
+ r = r[:20] + "..."
+ self.name=r
+ except (AttributeError, TypeError):
+ self.name="???"
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+ def __repr__(self):
+ try:
+ type = self.GetHLIType()
+ except:
+ type = "Generic"
+ return "HLIPythonObject("+type+") - name: "+ self.name + " object: " + repr(self.myobject)
+ def GetText(self):
+ try:
+ return str(self.name) + ' (' + self.GetHLIType() + ')'
+ except AttributeError:
+ return str(self.name) + ' = ' + repr(self.myobject)
+ def InsertDocString(self, lst):
+ ob = None
+ try:
+ ob = self.myobject.__doc__
+ except (AttributeError, TypeError):
+ pass
+ if ob:
+ lst.insert(0, HLIDocString( ob, "Doc" ))
+
+ def GetSubList(self):
+ ret = []
+ try:
+ for (key, ob) in self.myobject.__dict__.items():
+ if key not in special_names:
+ ret.append(MakeHLI( ob, key ) )
+ except (AttributeError, TypeError):
+ pass
+ try:
+ for name in self.myobject.__methods__:
+ ret.append(HLIMethod( name )) # no MakeHLI, as cant auto detect
+ except (AttributeError, TypeError):
+ pass
+ try:
+ for member in self.myobject.__members__:
+ if not member in special_names:
+ ret.append(MakeHLI(getattr(self.myobject, member), member))
+ except (AttributeError, TypeError):
+ pass
+ ret.sort()
+ self.InsertDocString(ret)
+ return ret
+ # if the has a dict, it is expandable.
+ def IsExpandable(self):
+ if self.knownExpandable is None:
+ self.knownExpandable = self.CalculateIsExpandable()
+ return self.knownExpandable
+
+ def CalculateIsExpandable(self):
+ try:
+ if self.myobject.__doc__:
+ return 1
+ except (AttributeError, TypeError):
+ pass
+ try:
+ for key in self.myobject.__dict__.keys():
+ if key not in special_names:
+ return 1
+ except (AttributeError, TypeError):
+ pass
+ try:
+ self.myobject.__methods__
+ return 1
+ except (AttributeError, TypeError):
+ pass
+ try:
+ for item in self.myobject.__members__:
+ if item not in special_names:
+ return 1
+ except (AttributeError, TypeError):
+ pass
+ return 0
+ def GetBitmapColumn(self):
+ if self.IsExpandable():
+ return 0
+ else:
+ return 4
+ def TakeDefaultAction(self):
+ ShowObject(self.myobject, self.name)
+
+
+class HLIDocString(HLIPythonObject):
+ def GetHLIType(self):
+ return "DocString"
+ def GetText(self):
+ return string.strip(self.myobject)
+ def IsExpandable(self):
+ return 0
+ def GetBitmapColumn(self):
+ return 6
+
+class HLIModule(HLIPythonObject):
+ def GetHLIType(self):
+ return "Module"
+
+class HLIFrame(HLIPythonObject):
+ def GetHLIType(self):
+ return "Stack Frame"
+
+class HLITraceback(HLIPythonObject):
+ def GetHLIType(self):
+ return "Traceback"
+
+class HLIClass(HLIPythonObject):
+ def GetHLIType(self):
+ return "Class"
+ def GetSubList(self):
+ ret = []
+ for base in self.myobject.__bases__:
+ ret.append( MakeHLI(base, 'Base class: ' + base.__name__ ) )
+ ret = ret + HLIPythonObject.GetSubList(self)
+ return ret
+
+class HLIMethod(HLIPythonObject):
+ # myobject is just a string for methods.
+ def GetHLIType(self):
+ return "Method"
+ def GetText(self):
+ return "Method: " + self.myobject + '()'
+
+class HLICode(HLIPythonObject):
+ def GetHLIType(self):
+ return "Code"
+ def IsExpandable(self):
+ return self.myobject
+ def GetSubList(self):
+ ret = []
+ ret.append( MakeHLI( self.myobject.co_consts, "Constants (co_consts)" ))
+ ret.append( MakeHLI( self.myobject.co_names, "Names (co_names)" ))
+ ret.append( MakeHLI( self.myobject.co_filename, "Filename (co_filename)" ))
+ ret.append( MakeHLI( self.myobject.co_argcount, "Number of args (co_argcount)"))
+ ret.append( MakeHLI( self.myobject.co_varnames, "Param names (co_varnames)"))
+
+ return ret
+
+class HLIInstance(HLIPythonObject):
+ def GetHLIType(self):
+ return "Instance"
+ def GetText(self):
+ return str(self.name) + ' (Instance of class ' + str(self.myobject.__class__.__name__) + ')'
+ def IsExpandable(self):
+ return 1
+ def GetSubList(self):
+ ret = []
+ ret.append( MakeHLI( self.myobject.__class__) )
+ ret = ret + HLIPythonObject.GetSubList(self)
+ return ret
+
+
+class HLIBuiltinFunction(HLIPythonObject):
+ def GetHLIType(self):
+ return "Builtin Function"
+
+class HLIFunction(HLIPythonObject):
+ def GetHLIType(self):
+ return "Function"
+ def IsExpandable(self):
+ return 1
+ def GetSubList(self):
+ ret = []
+# ret.append( MakeHLI( self.myobject.func_argcount, "Arg Count" ))
+ try:
+ ret.append( MakeHLI( self.myobject.func_argdefs, "Arg Defs" ))
+ except AttributeError:
+ pass
+ ret.append( MakeHLI( self.myobject.func_code, "Code" ))
+ ret.append( MakeHLI( self.myobject.func_globals, "Globals" ))
+ self.InsertDocString(ret)
+ return ret
+
+class HLISeq(HLIPythonObject):
+ def GetHLIType(self):
+ return "Sequence (abstract!)"
+ def IsExpandable(self):
+ return len(self.myobject)>0
+ def GetSubList(self):
+ ret = []
+ pos=0
+ for item in self.myobject:
+ ret.append(MakeHLI( item, '['+str(pos)+']' ) )
+ pos=pos+1
+ self.InsertDocString(ret)
+ return ret
+
+class HLIList(HLISeq):
+ def GetHLIType(self):
+ return "List"
+
+class HLITuple(HLISeq):
+ def GetHLIType(self):
+ return "Tuple"
+
+class HLIDict(HLIPythonObject):
+ def GetHLIType(self):
+ return "Dict"
+ def IsExpandable(self):
+ try:
+ self.myobject.__doc__
+ return 1
+ except (AttributeError, TypeError):
+ return len(self.myobject) > 0
+ def GetSubList(self):
+ ret = []
+ keys = self.myobject.keys()
+ keys.sort()
+ for key in keys:
+ ob = self.myobject[key]
+ ret.append(MakeHLI( ob, key ) )
+ self.InsertDocString(ret)
+ return ret
+
+TypeMap = { ClassType : HLIClass,
+ FunctionType: HLIFunction,
+ TupleType: HLITuple,
+ DictType: HLIDict,
+ ListType: HLIList,
+ ModuleType: HLIModule,
+ InstanceType : HLIInstance,
+ CodeType : HLICode,
+ BuiltinFunctionType : HLIBuiltinFunction,
+ FrameType : HLIFrame,
+ TracebackType : HLITraceback,
+ }
+
+def MakeHLI( ob, name=None ):
+ try:
+ cls = TypeMap[type(ob)]
+ except KeyError:
+ cls = HLIPythonObject
+ return cls( ob, name )
+
+#########################################
+#
+# Dialog related.
+
+
+class DialogShowObject(dialog.Dialog):
+ def __init__(self, object, title):
+ self.object = object
+ self.title = title
+ dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT)
+ def OnInitDialog(self):
+ import regsub
+ self.SetWindowText(self.title)
+ self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
+ try:
+ strval = str(self.object)
+ except:
+ t, v, tb = sys.exc_info()
+ strval = "Exception getting object value\n\n%s:%s" % (t, v)
+ strval = regsub.gsub('\n','\r\n', strval)
+ self.edit.ReplaceSel(strval)
+
+def ShowObject(object, title):
+ dlg = DialogShowObject(object, title)
+ dlg.DoModal()
+
+# And some mods for a sizable dialog from Sam Rushing!
+import win32con
+import win32api
+import commctrl
+
+class dynamic_browser (dialog.Dialog):
+ style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE
+ cs = (
+ win32con.WS_CHILD |
+ win32con.WS_VISIBLE |
+ commctrl.TVS_HASLINES |
+ commctrl.TVS_LINESATROOT |
+ commctrl.TVS_HASBUTTONS
+ )
+
+ dt = [
+ ["Python Object Browser", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
+ ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs]
+ ]
+
+ def __init__ (self, hli_root):
+ dialog.Dialog.__init__ (self, self.dt)
+ self.hier_list = hierlist.HierListWithItems (
+ hli_root,
+ win32ui.IDB_BROWSER_HIER
+ )
+ self.HookMessage (self.on_size, win32con.WM_SIZE)
+
+ def OnInitDialog (self):
+ self.hier_list.HierInit (self)
+ return dialog.Dialog.OnInitDialog (self)
+
+ def on_size (self, params):
+ lparam = params[3]
+ w = win32api.LOWORD(lparam)
+ h = win32api.HIWORD(lparam)
+ self.GetDlgItem (win32ui.IDC_LIST1).MoveWindow((0,0,w,h))
+
+def Browse (ob=__main__):
+ " Browse the argument, or the main dictionary "
+ root = MakeHLI (ob, 'root')
+ if not root.IsExpandable():
+ raise TypeError, "Browse() argument must have __dict__ attribute, or be a Browser supported type"
+
+ dlg = dynamic_browser (root)
+ dlg.CreateWindow()
+
+#
+#
+# Classes for using the browser in an MDI window, rather than a dialog
+#
+from pywin.mfc import docview
+class BrowserTemplate(docview.DocTemplate):
+ def __init__(self):
+ docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView)
+
+ def OpenObject(self, root): # Use this instead of OpenDocumentFile.
+ # Look for existing open document
+ for doc in self.GetDocumentList():
+ if doc.root==root:
+ doc.GetFirstView().ActivateFrame()
+ return doc
+ # not found - new one.
+ doc = BrowserDocument(self, root)
+ frame = self.CreateNewFrame(doc)
+ doc.OnNewDocument()
+ self.InitialUpdateFrame(frame, doc, 1)
+ return doc
+
+class BrowserDocument (docview.Document):
+ def __init__(self, template, root):
+ docview.Document.__init__(self, template)
+ self.root = root
+ self.SetTitle("Browser: " + root.name)
+ def OnOpenDocument (self, name):
+ raise TypeError, "This template can not open files"
+ return 0
+
+class BrowserView(docview.TreeView):
+ def OnInitialUpdate(self):
+ import commctrl
+ rc = self._obj_.OnInitialUpdate()
+ list=hierlist.HierListWithItems( self.GetDocument().root, win32ui.IDB_BROWSER_HIER, win32ui.AFX_IDW_PANE_FIRST)
+ list.HierInit(self.GetParent())
+ list.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
+ return rc
+
+template = None
+def MakeTemplate():
+ global template
+ if template is None:
+ template = BrowserTemplate() #win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView)
+
+def BrowseMDI(ob=__main__):
+ """Browse an object using an MDI window.
+ """
+
+ MakeTemplate()
+ root = MakeHLI(ob, repr(ob))
+ if not root.IsExpandable():
+ raise TypeError, "Browse() argument must have __dict__ attribute, or be a Browser supported type"
+
+ template.OpenObject(root)
+
diff --git a/Pythonwin/pywin/tools/hierlist.py b/Pythonwin/pywin/tools/hierlist.py
new file mode 100644
index 0000000000..e4104f61b4
--- /dev/null
+++ b/Pythonwin/pywin/tools/hierlist.py
@@ -0,0 +1,222 @@
+# hierlist
+#
+# IMPORTANT - Please read before using.
+
+# This module exposes an API for a Hierarchical Tree Control.
+# Previously, a custom tree control was included in Pythonwin which
+# has an API very similar to this.
+
+# The current control used is the common "Tree Control". This module exists now
+# to provide an API similar to the old control, but for the new Tree control.
+
+# If you need to use the Tree Control, you may still find this API a reasonable
+# choice. However, you should investigate using the tree control directly
+# to provide maximum flexibility (but with extra work).
+
+
+import win32ui
+import win32con
+from win32api import RGB
+
+from pywin.mfc import object, window, docview, dialog
+import commctrl
+
+# helper to get the text of an arbitary item
+def GetItemText(item):
+ if type(item)==type(()) or type(item)==type([]):
+ use = item[0]
+ else:
+ use = item
+ if type(use)==type(''):
+ return use
+ else:
+ return repr(item)
+
+
+class HierDialog(dialog.Dialog):
+ def __init__(self, title, hierList, bitmapID = win32ui.IDB_HIERFOLDERS, dlgID = win32ui.IDD_TREE, dll = None, childListBoxID = win32ui.IDC_LIST1):
+ dialog.Dialog.__init__(self, dlgID, dll ) # reuse this dialog.
+ self.hierList=hierList
+ self.dlgID = dlgID
+ self.title=title
+# self.childListBoxID = childListBoxID
+ def OnInitDialog(self):
+ self.SetWindowText(self.title)
+ self.hierList.HierInit(self)
+ return dialog.Dialog.OnInitDialog(self)
+
+class HierList(object.Object):
+ def __init__(self, root, bitmapID = win32ui.IDB_HIERFOLDERS, listBoxId = None, bitmapMask = None): # used to create object.
+ self.list = self._obj_ = None
+ self.bitmapID = bitmapID
+ self.root = root
+ self.listBoxId = listBoxId
+ self.itemHandleMap = {}
+ self.filledItemHandlesMap = {}
+ self.bitmapMask = bitmapMask
+ def ItemFromHandle(self, handle):
+ return self.itemHandleMap[handle]
+ def SetStyle(self, newStyle):
+ import win32api
+ hwnd = self._obj_.GetSafeHwnd()
+ style = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE);
+ win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, (style | newStyle) )
+
+ def HierInit(self, parent, listControl = None ): # Used when window first exists.
+ # this also calls "Create" on the listbox.
+ # params - id of listbbox, ID of bitmap, size of bitmaps
+ if self.bitmapMask is None:
+ bitmapMask = RGB(0,0,255)
+ else:
+ bitmapMask = self.bitmapMask
+ self.imageList = win32ui.CreateImageList(self.bitmapID, 16, 0, bitmapMask)
+ if listControl is None:
+ if self.listBoxId is None: self.listBoxId = win32ui.IDC_LIST1
+ self.list = self._obj_ = parent.GetDlgItem(self.listBoxId)
+ else:
+ self.list = self._obj_ = listControl
+ lbid = listControl.GetDlgCtrlID()
+ assert self.listBoxId is None or self.listBoxId == lbid, "An invalid listbox control ID has been specified (specified as %s, but exists as %s)" % (self.listBoxId, lbid)
+ self.listBoxId = lbid
+ self.list.SetImageList(self.imageList, commctrl.LVSIL_NORMAL)
+# self.list.AttachObject(self)
+ parent.HookNotify(self.OnTreeItemExpanding, commctrl.TVN_ITEMEXPANDING)
+ parent.HookNotify(self.OnTreeItemSelChanged, commctrl.TVN_SELCHANGED)
+ parent.HookNotify(self.OnTreeItemDoubleClick, commctrl.NM_DBLCLK)
+
+ if self.root:
+ self.AcceptRoot(self.root)
+
+ def HierTerm(self):
+ self.list.DeleteAllItems()
+ self.root = None
+ self.itemHandleMap = {}
+ self.filledItemHandlesMap = {}
+ parent = self.GetParentFrame()
+ parent.HookNotify(None, commctrl.TVN_ITEMEXPANDING)
+ parent.HookNotify(None, commctrl.TVN_SELCHANGED)
+ parent.HookNotify(None, commctrl.NM_DBLCLK)
+ self.list = None
+
+ def OnTreeItemDoubleClick(self,(hwndFrom, idFrom, code), extra):
+ if idFrom != self.listBoxId: return None
+ item = self.itemHandleMap[self._obj_.GetSelectedItem()]
+ self.TakeDefaultAction(item)
+ return 1
+
+ def OnTreeItemExpanding(self,(hwndFrom, idFrom, code), extra):
+ if idFrom != self.listBoxId: return None
+ action, itemOld, itemNew, pt = extra
+ itemHandle = itemNew[0]
+ if not self.filledItemHandlesMap.has_key(itemHandle):
+ item = self.itemHandleMap[itemHandle]
+ self.AddSubList(itemHandle, self.GetSubList(item))
+ self.filledItemHandlesMap[itemHandle] = None
+ return 0
+
+ def OnTreeItemSelChanged(self,(hwndFrom, idFrom, code), extra):
+ if idFrom != self.listBoxId: return None
+ action, itemOld, itemNew, pt = extra
+ itemHandle = itemNew[0]
+ item = self.itemHandleMap[itemHandle]
+ self.PerformItemSelected(item)
+ return 1
+
+ def AddSubList(self, parentHandle, subItems):
+ for item in subItems:
+ text = self.GetText(item)
+# hitem = self.list.InsertItem(text, 0, 1)
+ if self.IsExpandable(item):
+ cItems = 1 # Trick it !!
+ else:
+ cItems = 0
+ bitmapCol = self.GetBitmapColumn(item)
+ bitmapSel = self.GetSelectedBitmapColumn(item)
+ if bitmapSel is None: bitmapSel = bitmapCol
+ hitem = self.list.InsertItem(parentHandle, commctrl.TVI_LAST, (None, None, None, text, bitmapCol, bitmapSel, cItems, 0))
+ self.itemHandleMap[hitem] = item
+
+ def ItemFromHandle(self, handle):
+ return self.itemHandleMap[handle]
+
+ def AcceptRoot(self, root):
+ self.list.DeleteAllItems()
+ self.itemHandleMap = {}
+ self.filledItemHandlesMap = {}
+ subItems = self.GetSubList(root)
+ self.AddSubList(0, subItems)
+
+ def GetBitmapColumn(self, item):
+ if self.IsExpandable(item):
+ return 0
+ else:
+ return 4
+ def GetSelectedBitmapColumn(self, item):
+ return None # Use standard.
+
+ def GetSelectedBitmapColumn(self, item):
+ return 0
+
+ def CheckChangedChildren(self):
+ return self.list.CheckChangedChildren()
+ def GetText(self,item):
+ return GetItemText(item)
+ def PerformItemSelected(self, item):
+ try:
+ win32ui.SetStatusText('Selected ' + self.GetText(item))
+ except win32ui.error: # No status bar!
+ pass
+ def TakeDefaultAction(self, item):
+ win32ui.MessageBox('Got item ' + self.GetText(item))
+
+##########################################################################
+#
+# Classes for use with seperate HierListItems.
+#
+#
+class HierListWithItems(HierList):
+ def __init__(self, root, bitmapID = win32ui.IDB_HIERFOLDERS, listBoxID = None, bitmapMask = None): # used to create object.
+ HierList.__init__(self, root, bitmapID, listBoxID, bitmapMask )
+ def DelegateCall( self, fn):
+ return fn()
+ def GetBitmapColumn(self, item):
+ rc = self.DelegateCall(item.GetBitmapColumn)
+ if rc is None:
+ rc = HierList.GetBitmapColumn(self, item)
+ return rc
+ def GetSelectedBitmapColumn(self, item):
+ return self.DelegateCall(item.GetSelectedBitmapColumn)
+ def IsExpandable(self, item):
+ return self.DelegateCall( item.IsExpandable)
+ def GetText(self, item):
+ return self.DelegateCall( item.GetText )
+ def GetSubList(self, item):
+ return self.DelegateCall(item.GetSubList)
+ def PerformItemSelected(self, item):
+ func = getattr(item, "PerformItemSelected", None)
+ if func is None:
+ return HierList.PerformItemSelected( self, item )
+ else:
+ return self.DelegateCall(func)
+
+ def TakeDefaultAction(self, item):
+ func = getattr(item, "TakeDefaultAction", None)
+ if func is None:
+ return HierList.TakeDefaultAction( self, item )
+ else:
+ return self.DelegateCall(func)
+
+# A hier list item - for use with a HierListWithItems
+class HierListItem:
+ def __init__(self):
+ pass
+ def GetText(self):
+ pass
+ def GetSubList(self):
+ pass
+ def IsExpandable(self):
+ pass
+ def GetBitmapColumn(self):
+ return None # indicate he should do it.
+ def GetSelectedBitmapColumn(self):
+ return None # same as other
diff --git a/Pythonwin/pywin/tools/regedit.py b/Pythonwin/pywin/tools/regedit.py
new file mode 100644
index 0000000000..ad8c56b6b9
--- /dev/null
+++ b/Pythonwin/pywin/tools/regedit.py
@@ -0,0 +1,264 @@
+# Regedit - a Registry Editor for Python
+import win32api, win32ui, win32con, commctrl
+from pywin.mfc import window, docview, dialog
+import hierlist
+import regutil
+
+class SplitterFrame(window.MDIChildWnd):
+ def __init__(self):
+ # call base CreateFrame
+ self.images = None
+ window.MDIChildWnd.__init__(self)
+
+ def OnCreateClient(self, cp, context):
+ splitter = win32ui.CreateSplitter()
+ doc = context.doc
+ frame_rect = self.GetWindowRect()
+ size = ((frame_rect[2] - frame_rect[0]),
+ (frame_rect[3] - frame_rect[1])/2)
+ sub_size = (size[0]/3, size[1])
+ splitter.CreateStatic (self, 1, 2)
+ # CTreeControl view
+ self.keysview = RegistryTreeView(doc)
+ # CListControl view
+ self.valuesview = RegistryValueView(doc)
+
+ splitter.CreatePane (self.keysview, 0, 0, (sub_size))
+ splitter.CreatePane (self.valuesview, 0, 1, (0,0)) # size ignored.
+ splitter.SetRowInfo(0, size[1] ,0)
+ # Setup items in the imagelist
+
+ return 1
+
+ def OnItemDoubleClick(self,(hwndFrom, idFrom, code), extra):
+ if idFrom==win32ui.AFX_IDW_PANE_FIRST:
+ # Tree control
+ return None
+ elif idFrom==win32ui.AFX_IDW_PANE_FIRST + 1:
+ item = self.keysview.SelectedItem()
+ self.valuesview.EditValue(item)
+ return 0
+ # List control
+ else:
+ return None # Pass it on
+
+ def PerformItemSelected(self,item):
+ return self.valuesview.UpdateForRegItem(item)
+
+ def OnDestroy(self, msg):
+ window.MDIChildWnd.OnDestroy(self, msg)
+ if self.images:
+ self.images.DeleteImageList()
+ self.images = None
+
+class RegistryTreeView(docview.TreeView):
+ def OnInitialUpdate(self):
+ rc = self._obj_.OnInitialUpdate()
+ self.frame = self.GetParent().GetParent()
+ doc = self.GetDocument()
+ regroot = doc.root
+ subkey = doc.subkey
+ self.hierList = hierlist.HierListWithItems( HLIRegistryKey(regroot, subkey, "Root"), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST)
+ self.hierList.HierInit(self.frame, self.GetTreeCtrl())
+ self.hierList.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
+ self.hierList.PerformItemSelected = self.PerformItemSelected
+
+ self.frame.HookNotify(self.frame.OnItemDoubleClick, commctrl.NM_DBLCLK)
+# self.frame.HookNotify(self.frame.OnItemRightClick, commctrl.NM_RCLICK)
+ self.HookMessage(self.OnItemRightClick, win32con.WM_RBUTTONUP)
+
+ def OnItemRightClick(self, msg):
+ menu = win32ui.CreatePopupMenu()
+ menu.AppendMenu(win32con.MF_STRING|win32con.MF_ENABLED,1000, "Delete Key")
+ self.HookCommand(self.OnDeleteKey, 1000)
+ menu.TrackPopupMenu(win32api.GetCursorPos()) # track at mouse position.
+ return None
+
+ def OnDeleteKey(self,command, code):
+ print "Have Delete Key"
+ print self.SelectedItem().GetText()
+ print self.SearchSelectedItem().GetText()
+
+ def PerformItemSelected(self, item):
+ return self.frame.PerformItemSelected(item)
+
+ def SelectedItem(self):
+ return self.hierList.ItemFromHandle(self.hierList.GetSelectedItem())
+
+ def SearchSelectedItem(self):
+ handle = self.hierList.GetChildItem(0)
+ while 1:
+# print "State is", self.hierList.GetItemState(handle, -1)
+ if self.hierList.GetItemState(handle, commctrl.TVIS_FOCUSED):
+# print "Item is ", self.hierList.ItemFromHandle(handle)
+ return self.hierList.ItemFromHandle(handle)
+ handle = self.hierList.GetNextSiblingItem(handle)
+
+class RegistryValueView(docview.ListView):
+ def OnInitialUpdate(self):
+ hwnd = self._obj_.GetSafeHwnd()
+ style = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE);
+ win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, (style & ~commctrl.LVS_TYPEMASK) | commctrl.LVS_REPORT);
+
+ itemDetails = (commctrl.LVCFMT_LEFT, 100, "Name", 0)
+ self.InsertColumn(0, itemDetails)
+ itemDetails = (commctrl.LVCFMT_LEFT, 500, "Data", 0)
+ self.InsertColumn(1, itemDetails)
+
+ def UpdateForRegItem(self, item):
+ self.DeleteAllItems()
+ hkey = win32api.RegOpenKey(item.keyRoot, item.keyName)
+ try:
+ valNum = 0
+ ret = []
+ while 1:
+ try:
+ res = win32api.RegEnumValue(hkey, valNum)
+ except win32api.error:
+ break
+ name = res[0]
+ if not name: name = "(Default)"
+ self.InsertItem(valNum, name)
+ self.SetItemText(valNum, 1, str(res[1]))
+ valNum = valNum + 1
+ finally:
+ win32api.RegCloseKey(hkey)
+ def EditValue(self, item):
+ # Edit the current value
+ class EditDialog(dialog.Dialog):
+ def __init__(self, item):
+ self.item = item
+ dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT)
+ def OnInitDialog(self):
+ self.SetWindowText("Enter new value")
+ self.GetDlgItem(win32con.IDCANCEL).ShowWindow(win32con.SW_SHOW)
+ self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
+ # Modify the edit windows style
+ style = win32api.GetWindowLong(self.edit.GetSafeHwnd(), win32con.GWL_STYLE)
+ style = style & (~win32con.ES_WANTRETURN)
+ win32api.SetWindowLong(self.edit.GetSafeHwnd(), win32con.GWL_STYLE, style)
+ self.edit.SetWindowText(str(self.item))
+ self.edit.SetSel(-1)
+ return dialog.Dialog.OnInitDialog(self)
+ def OnDestroy(self,msg):
+ self.newvalue = self.edit.GetWindowText()
+
+ try:
+ index = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
+ except win32ui.error:
+ return # No item selected.
+
+ if index==0:
+ keyVal = ""
+ else:
+ keyVal = self.GetItemText(index,0)
+ # Query for a new value.
+ try:
+ newVal = self.GetItemsCurrentValue(item, keyVal)
+ except TypeError, details:
+ win32ui.MessageBox(details)
+ return
+
+ d = EditDialog(newVal)
+ if d.DoModal()==win32con.IDOK:
+ try:
+ self.SetItemsCurrentValue(item, keyVal, d.newvalue)
+ except win32api.error, (rc, fn, desc):
+ win32ui.MessageBox("Error setting value\r\n\n%s" % desc)
+ self.UpdateForRegItem(item)
+
+ def GetItemsCurrentValue(self, item, valueName):
+ hkey = win32api.RegOpenKey(item.keyRoot, item.keyName)
+ try:
+ val, type = win32api.RegQueryValueEx(hkey, valueName)
+ if type != win32con.REG_SZ:
+ raise TypeError, "Only strings can be edited"
+ return val
+ finally:
+ win32api.RegCloseKey(hkey)
+
+ def SetItemsCurrentValue(self, item, valueName, value):
+ # ** Assumes already checked is a string.
+ hkey = win32api.RegOpenKey(item.keyRoot, item.keyName , 0, win32con.KEY_SET_VALUE)
+ try:
+ win32api.RegSetValueEx(hkey, valueName, 0, win32con.REG_SZ, value)
+ finally:
+ win32api.RegCloseKey(hkey)
+
+
+class RegTemplate(docview.DocTemplate):
+ def __init__(self):
+ docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None)
+
+# def InitialUpdateFrame(self, frame, doc, makeVisible=1):
+# self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
+# frame.InitialUpdateFrame(doc, makeVisible)
+
+ def OpenRegistryKey(self, root = None, subkey = None): # Use this instead of OpenDocumentFile.
+ # Look for existing open document
+ if root is None: root = regutil.GetRootKey()
+ if subkey is None: subkey = regutil.BuildDefaultPythonKey()
+ for doc in self.GetDocumentList():
+ if doc.root==root and doc.subkey==subkey:
+ doc.GetFirstView().ActivateFrame()
+ return doc
+ # not found - new one.
+ doc = RegDocument(self, root, subkey)
+ frame = self.CreateNewFrame(doc)
+ doc.OnNewDocument()
+ self.InitialUpdateFrame(frame, doc, 1)
+ return doc
+
+class RegDocument (docview.Document):
+ def __init__(self, template, root, subkey):
+ docview.Document.__init__(self, template)
+ self.root = root
+ self.subkey = subkey
+ self.SetTitle("Registry Editor: " + subkey)
+
+ def OnOpenDocument (self, name):
+ raise TypeError, "This template can not open files"
+ return 0
+
+
+class HLIRegistryKey(hierlist.HierListItem):
+ def __init__( self, keyRoot, keyName, userName ):
+ self.keyRoot = keyRoot
+ self.keyName = keyName
+ self.userName = userName
+ hierlist.HierListItem.__init__(self)
+ def GetText(self):
+ return self.userName
+ def IsExpandable(self):
+ hkey = win32api.RegOpenKey(self.keyRoot, self.keyName)
+ try:
+ keys, vals, dt = win32api.RegQueryInfoKey(hkey)
+ return (keys>0)
+ finally:
+ win32api.RegCloseKey(hkey)
+
+ def GetSubList(self):
+ hkey = win32api.RegOpenKey(self.keyRoot, self.keyName)
+ win32ui.DoWaitCursor(1)
+ try:
+ keyNum = 0
+ ret = []
+ while 1:
+ try:
+ key = win32api.RegEnumKey(hkey, keyNum)
+ except win32api.error:
+ break
+ ret.append(HLIRegistryKey(self.keyRoot, self.keyName + "\\" + key, key))
+ keyNum = keyNum + 1
+ finally:
+ win32api.RegCloseKey(hkey)
+ win32ui.DoWaitCursor(0)
+ return ret
+
+template = RegTemplate()
+
+def EditRegistry(root = None, key = None):
+ doc=template.OpenRegistryKey(root, key)
+
+if __name__=='__main__':
+ EditRegistry()
diff --git a/Pythonwin/pywin/tools/regpy.py b/Pythonwin/pywin/tools/regpy.py
new file mode 100644
index 0000000000..79b3f3e347
--- /dev/null
+++ b/Pythonwin/pywin/tools/regpy.py
@@ -0,0 +1,53 @@
+# (sort-of) Registry editor
+import win32ui
+import dialog
+import win32con
+import commctrl
+
+class RegistryControl:
+ def __init__(self, key):
+ self.key = key
+
+class RegEditPropertyPage(dialog.PropertyPage):
+ IDC_LISTVIEW = 1000
+ def GetTemplate(self):
+ "Return the template used to create this dialog"
+
+ w = 152 # Dialog width
+ h = 122 # Dialog height
+ SS_STD = win32con.WS_CHILD | win32con.WS_VISIBLE
+ FRAMEDLG_STD = win32con.WS_CAPTION | win32con.WS_SYSMENU
+ style = FRAMEDLG_STD | win32con.WS_VISIBLE | win32con.DS_SETFONT | win32con.WS_MINIMIZEBOX
+ template = [[self.caption, (0, 0, w, h), style, None, (8, 'Helv')], ]
+ lvStyle = SS_STD | commctrl.LVS_EDITLABELS | commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE | commctrl.LVS_ALIGNLEFT | win32con.WS_BORDER | win32con.WS_TABSTOP
+ template.append(["SysListView32", "", self.IDC_LISTVIEW, (10, 10, 185, 100), lvStyle])
+ return template
+
+class RegistryPage(RegEditPropertyPage):
+ def __init__(self):
+ self.caption="Path"
+ RegEditPropertyPage.__init__(self, self.GetTemplate())
+ def OnInitDialog(self):
+ self.listview = self.GetDlgItem(self.IDC_LISTVIEW)
+ RegEditPropertyPage.OnInitDialog(self)
+ # Setup the listview columns
+ itemDetails = (commctrl.LVCFMT_LEFT, 100, "App", 0)
+ self.listview.InsertColumn(0, itemDetails)
+ itemDetails = (commctrl.LVCFMT_LEFT, 1024, "Paths", 0)
+ self.listview.InsertColumn(1, itemDetails)
+
+ index = self.listview.InsertItem(0,"App")
+ self.listview.SetItemText(index, 1, "Path")
+
+
+class RegistrySheet(dialog.PropertySheet):
+ def __init__(self, title):
+ dialog.PropertySheet.__init__(self, title)
+ self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
+ def OnActivate(self, msg):
+ print "OnAcivate"
+
+def t():
+ ps=RegistrySheet('Registry Settings')
+ ps.AddPage(RegistryPage())
+ ps.DoModal()
diff --git a/Pythonwin/readme.html b/Pythonwin/readme.html
new file mode 100644
index 0000000000..9594a247c6
--- /dev/null
+++ b/Pythonwin/readme.html
@@ -0,0 +1,47 @@
+
+
+
+
+Pythonwin Readme
+
+
+
+
+Pythonwin Readme.
+Introduction
+Pythonwin is implemented as a 'wrapper' for the Microsoft Foundation Class library. With it, you can use MFC in an interactive, interpreted environment, or write full blown stand-alone applications tightly coupled with the Windows environment. Over 30 MFC objects are exposed, including Common Controls, Property Pages/Sheets, Control/Toolbars, Threads, etc.
+Pythonwin could almost be considered a sample program for the MFC UI environment. This Python UI environment can be embedded in almost any other application - such as OLE clients/servers, Netscape plugins, as a Macro language etc.
+Pythonwin now also comes with a built-in debugger. This debugger was previously distributed separately as the win32dbg package, but has recently been upgraded to use the new Scintilla color editor, and rolled into the main Pythonwin distribution. Please check out the debugger documentation.
+Recent changes can be found at the end of this document.
+The log from the source control system is also included.
+Demos
+There are many demos in the pywin\demos directory. To see a list of all the demos, run the program "pywin\demos\guidemo.py" from inside Pythonwin.
+Documentation
+Almost all win32ui methods are document in the Pythonwin Help file. This is available from the Help Menu in the Pythonwin enviroment.
+Below is a list of external Pythonwin specific documentation.
+
+
+
+Known Problems
+
+
+- The new Scintilla control has a few issues regarding scrolling and caret positioning which are quite minor. VSS integration currently loses the selection when checking out the file.
+- Pythonwin will occasionally crash upon exiting under Windows 95. The crash is in "user.exe" (Windows 95 16 bit code) which makes debugging difficult. It does not happen on Windows NT, so I'm stumped on this one. If someone could even just narrow it down to exactly what code will cause the crash at exit I would really appreciate it.
+- Many of the menu items are always grey (eg, Print, Print Preview, Set Tab Stops, etc). This functionality is simply not yet implemented in Pythonwin.
+- Triple quoting of strings, and the 'underscore feature' of Python.exe are not implemented in the interactive window.
+- Printing strings will NULL bytes will cause problems.
+
+
+New, improved color editor, using the Scintilla control by Neil Hodgson (see http://hare.net.au/~neilh/ScintillaTide.html). The debugger now requires use of this editor.
+Much better printing support from Roger Burnham. Pythonwin itself still can't print anything, but the framework can (meaning some kind soul could now add the support to Pythonwin :-)
+DDE support is complete.
+Reference helpfile is far more complete.
+Lots of new methods from Kleanthis Kleanthous.
+Better tool-tip and region support
+
+
diff --git a/Pythonwin/res/BROWSER.BMP b/Pythonwin/res/BROWSER.BMP
new file mode 100644
index 0000000000..fed05c6460
Binary files /dev/null and b/Pythonwin/res/BROWSER.BMP differ
diff --git a/Pythonwin/res/HIERFOLD.BMP b/Pythonwin/res/HIERFOLD.BMP
new file mode 100644
index 0000000000..c14673bf11
Binary files /dev/null and b/Pythonwin/res/HIERFOLD.BMP differ
diff --git a/Pythonwin/res/ICO00002.ICO b/Pythonwin/res/ICO00002.ICO
new file mode 100644
index 0000000000..b8f1072e36
Binary files /dev/null and b/Pythonwin/res/ICO00002.ICO differ
diff --git a/Pythonwin/res/IDR_MAIN.ICO b/Pythonwin/res/IDR_MAIN.ICO
new file mode 100644
index 0000000000..f714eea446
Binary files /dev/null and b/Pythonwin/res/IDR_MAIN.ICO differ
diff --git a/Pythonwin/res/IDR_PYTH.ICO b/Pythonwin/res/IDR_PYTH.ICO
new file mode 100644
index 0000000000..f714eea446
Binary files /dev/null and b/Pythonwin/res/IDR_PYTH.ICO differ
diff --git a/Pythonwin/res/PADDOC.ICO b/Pythonwin/res/PADDOC.ICO
new file mode 100644
index 0000000000..db4de224c5
Binary files /dev/null and b/Pythonwin/res/PADDOC.ICO differ
diff --git a/Pythonwin/res/debugger.ico b/Pythonwin/res/debugger.ico
new file mode 100644
index 0000000000..b8f1072e36
Binary files /dev/null and b/Pythonwin/res/debugger.ico differ
diff --git a/Pythonwin/res/debugger_stack.bmp b/Pythonwin/res/debugger_stack.bmp
new file mode 100644
index 0000000000..0a92bbbac6
Binary files /dev/null and b/Pythonwin/res/debugger_stack.bmp differ
diff --git a/Pythonwin/res/pyc.ico b/Pythonwin/res/pyc.ico
new file mode 100644
index 0000000000..6dc29fbe2a
Binary files /dev/null and b/Pythonwin/res/pyc.ico differ
diff --git a/Pythonwin/res/pycon.ico b/Pythonwin/res/pycon.ico
new file mode 100644
index 0000000000..a54682d91c
Binary files /dev/null and b/Pythonwin/res/pycon.ico differ
diff --git a/Pythonwin/res/temp.BMP b/Pythonwin/res/temp.BMP
new file mode 100644
index 0000000000..499ae53698
Binary files /dev/null and b/Pythonwin/res/temp.BMP differ
diff --git a/Pythonwin/res/toolbar.bmp b/Pythonwin/res/toolbar.bmp
new file mode 100644
index 0000000000..c9fb84d304
Binary files /dev/null and b/Pythonwin/res/toolbar.bmp differ
diff --git a/Pythonwin/res/toolbar_debugger.bmp b/Pythonwin/res/toolbar_debugger.bmp
new file mode 100644
index 0000000000..16cd00f291
Binary files /dev/null and b/Pythonwin/res/toolbar_debugger.bmp differ
diff --git a/Pythonwin/respw.h b/Pythonwin/respw.h
new file mode 100644
index 0000000000..ff877b2dfb
--- /dev/null
+++ b/Pythonwin/respw.h
@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by pythonwin.rc
+//
+#define IDR_MAINFRAME 11128
+#define IDS_STARTUP_SCRIPT 57346
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Pythonwin/reswin32ui.h b/Pythonwin/reswin32ui.h
new file mode 100644
index 0000000000..b26fc15ce9
--- /dev/null
+++ b/Pythonwin/reswin32ui.h
@@ -0,0 +1,133 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by win32ui.rc
+//
+#define IDR_CNTR_INPLACE 6
+#define IDC_ZZ_PRINT_MAG_EDIT 1010
+#define IDP_ERR_GET_PRINTER_DC 4000
+#define IDP_ERR_GET_DEVICE_DEFAULTS 4001
+#define IDR_MAINFRAME 11128
+#define IDR_PYTHONTYPE 11129
+#define IDR_TEXTTYPE 11130
+#define IDR_PYTHONTYPE_CNTR_IP 11131
+#define IDR_PYTHONCONTYPE 11132
+#define IDR_DEBUGGER 11133
+#define IDR_PYTHONREMOVE 11134
+#define IDR_PYC 11135
+#define IDD_ABOUTBOX 11139
+#define IDD_PAGE_SETUP 11141
+#define IDB_HIERFOLDERS 11142
+#define IDB_BROWSER_HIER 11145
+#define IDD_LARGE_EDIT 11147
+#define IDD_DUMMYPROPPAGE 11148
+#define IDD_GENERAL_STATUS 11149
+#define IDC_CHECK3 11151
+#define IDR_SHELLTRAY 11152
+#define IDD_PP_EDITOR 11153
+#define IDD_PP_IDE 11155
+#define IDD_PP_TOOLMENU 11156
+#define ID_HELP_OTHER 14950
+#define IDC_HEADER 15000
+#define IDC_FOOTER 15001
+#define IDC_BUTTON1 15004
+#define IDC_BUTTON2 15005
+#define IDC_BUTTON3 15006
+#define IDC_EDIT1 15008
+#define IDC_EDIT2 15009
+#define IDC_EDIT3 15010
+#define IDC_EDIT4 15011
+#define IDC_LIST1 15013
+#define IDC_PROMPT1 15014
+#define IDC_PROMPT2 15015
+#define IDC_PROMPT3 15016
+#define IDC_PROMPT4 15017
+#define IDD_PROPDEMO2 15018
+#define IDD_PROPDEMO1 15019
+#define IDC_DBG_STEPOUT 15020
+#define IDC_DBG_STEPOVER 15021
+#define IDC_DBG_GO 15022
+#define IDC_DBG_LIST1 15025
+#define ID_CHOOSE_FONT 15026
+#define IDC_HEADER_FILE 15027
+#define IDC_EDIT_TABS 15028
+#define IDC_DEMO1 15029
+#define ID_SET_TABSTOPS 15030
+#define IDC_HEADER_SYSTEM 15031
+#define ID_VIEW_FIXED_FONT 15031
+#define IDC_DEMO2 15032
+#define ID_WORD_WRAP 15033
+#define IDC_FOOTER_FILE 15034
+#define IDC_DEMO3 15035
+#define IDC_FOOTER_SYSTEM 15036
+#define ID_INDICATOR_COLNUM 15038
+#define IDC_PROMPT_TABS 15039
+#define ID_INDICATOR_LINENUM 15040
+#define IDD_SET_TABSTOPS 15041
+#define IDD_TREE_MB 15042
+#define IDD_TREE 15043
+#define IDD_SIMPLE_INPUT 15044
+#define IDD_RUN_SCRIPT 15045
+#define IDD_PP_FORMAT 15046
+#define IDC_USE_TABS 15053
+#define IDC_TAB_SIZE 15054
+#define IDC_INDENT_SIZE 15055
+#define IDC_SPIN1 15056
+#define IDC_SPIN2 15057
+#define IDC_COMBO1 15058
+#define IDC_AUTO_RELOAD 15059
+#define IDC_RADIO1 15061
+#define IDC_RADIO2 15062
+#define IDC_ABOUT_VERSION 15062
+#define IDC_VSS_INTEGRATE 15063
+#define IDC_USE_SMART_TABS 15064
+#define IDC_CHECK1 15066
+#define IDC_CHECK2 15067
+#define IDC_BUTTON4 15068
+#define IDC_VIEW_WHITESPACE 15070
+#define IDC_AUTOCOMPLETE 15071
+#define IDC_CALLTIPS 15072
+#define IDC_EDITOR_COLOR 15073
+#define IDC_KEYBOARD_CONFIG 15074
+#define IDC_COMBO2 15076
+#define IDC_DBG_ADD 16004
+#define IDC_DBG_CLEAR 16006
+#define IDC_DBG_CLOSE 16010
+#define IDD_PP_DEBUGGER 16012
+#define IDC_DBG_STEP 16013
+#define IDB_DEBUGGER_HIER 16018
+#define ID_FILE_RUN 36864
+#define ID_PAGE_SETUP 36865
+#define ID_CHOOSE_PRINT_FONT 36866
+#define ID_FILE_IMPORT 36867
+#define ID_FILE_LOCATE 36868
+#define ID_VIEW_BROWSE 36869
+#define ID_HELP_GUI_REF 36870
+#define ID_MIRROR_DISPLAY_FONT 36871
+#define ID_HELP_PYTHON 36872
+#define ID_VIEW_INTERACTIVE 36873
+#define ID_VIEW_WHITESPACE 36874
+#define ID_EDIT_SELECT_BLOCK 36875
+#define ID_DEBUGGER_STOP 36876
+#define ID_SHELL_ACTIVATE 36877
+#define ID_SHELL_BREAK 36878
+#define ID_VIEW_OPTIONS 36879
+#define ID_FILE_CHECK 36881
+#define ID_FILE_SAVE_ALL 36882
+#define IDC_DBG_STACK 36888
+#define IDC_DBG_BREAKPOINTS 36889
+#define IDC_DBG_WATCH 40002
+#define IDS_DEBUG_RELEASE_MISMATCH 57346
+#define ID_EDIT_GOTO_LINE 57638
+#define ID_VIEW_TOOLBAR_DBG 59424
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 11160
+#define _APS_NEXT_COMMAND_VALUE 36890
+#define _APS_NEXT_CONTROL_VALUE 15077
+#define _APS_NEXT_SYMED_VALUE 11152
+#endif
+#endif
diff --git a/Pythonwin/scintilla_src/CallTip.cc b/Pythonwin/scintilla_src/CallTip.cc
new file mode 100644
index 0000000000..a389ed2dea
--- /dev/null
+++ b/Pythonwin/scintilla_src/CallTip.cc
@@ -0,0 +1,175 @@
+// Scintilla source code edit control
+// CallTip.cc - code for displaying call tips
+// Copyright 1998-1999 by Neil Hodgson
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include
+
+#include
+#include
+
+#include "Scintilla.h"
+#include "CallTip.h"
+
+class CallTip {
+ static HINSTANCE hInstance;
+ char *val;
+ HFONT font;
+ int startHighlight;
+ int endHighlight;
+ HWND hwnd;
+ CallTip();
+ void Paint();
+ long WndProc(WORD iMessage,WPARAM wParam,LPARAM lParam);
+ static LRESULT PASCAL CWndProc(
+ HWND hWnd,UINT iMessage,WPARAM wParam, LPARAM lParam);
+public:
+ ~CallTip();
+ static void Register(HINSTANCE hInstance_);
+};
+
+HINSTANCE CallTip::hInstance = 0;
+
+void CallTip::Register(HINSTANCE hInstance_) {
+ hInstance = hInstance_;
+
+ WNDCLASS wndclass; // Structure used to register Windows class.
+
+ wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = (WNDPROC)CallTip::CWndProc;
+ wndclass.cbClsExtra = 0;
+ // Reserve extra bytes for each instance of the window;
+ // we will use these bytes to store a pointer to the C++
+ // (Scintilla) object corresponding to the window.
+ wndclass.cbWndExtra = sizeof(CallTip*);
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL,IDC_IBEAM);
+ wndclass.hbrBackground = NULL;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = callClassName;
+
+ if (!RegisterClass(&wndclass))
+ exit(FALSE);
+}
+
+CallTip::CallTip() {
+ startHighlight = 0;
+ endHighlight = 0;
+ val = 0;
+}
+
+CallTip::~CallTip() {
+ if (val)
+ free(val);
+ val = 0;
+}
+
+void CallTip::Paint() {
+ RECT rcClient = {0,0,0,0};
+ GetClientRect(hwnd,&rcClient);
+
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd,&ps);
+
+ HFONT fontOld = (HFONT)SelectObject(ps.hdc, font);
+
+ SIZE sizeText = {100, 100};
+ if (val)
+ GetTextExtentPoint32(ps.hdc, val, strlen(val), &sizeText);
+
+ if ((sizeText.cx + 10) > (rcClient.right - rcClient.left)) {
+ SetWindowPos(hwnd, 0, 0, 0, sizeText.cx + 10, rcClient.bottom - rcClient.top, SWP_NOMOVE);
+ } else {
+ FillRect(ps.hdc, &rcClient, (HBRUSH)GetStockObject(WHITE_BRUSH));
+
+ DrawEdge(ps.hdc, &rcClient, EDGE_RAISED, BF_RECT);
+
+ if (val && strlen(val)) {
+ InflateRect(&rcClient, -1, -1);
+ int x = 5;
+ SetTextColor(ps.hdc, RGB(0x80,0x80,0x80));
+ GetTextExtentPoint32(ps.hdc, val, startHighlight, &sizeText);
+ ExtTextOut(ps.hdc, x, 1, 0, &rcClient, val, startHighlight, NULL);
+ x += sizeText.cx;
+ SetTextColor(ps.hdc, RGB(0,0,0x80));
+ GetTextExtentPoint32(ps.hdc, val + startHighlight, endHighlight - startHighlight, &sizeText);
+ ExtTextOut(ps.hdc, x, 1, 0, &rcClient, val + startHighlight, endHighlight - startHighlight, NULL);
+ x += sizeText.cx;
+ SetTextColor(ps.hdc, RGB(0x80,0x80,0x80));
+ ExtTextOut(ps.hdc, x, 1, 0, &rcClient, val + endHighlight, strlen(val) - endHighlight, NULL);
+ }
+ }
+
+ SelectObject(ps.hdc, fontOld);
+ EndPaint(hwnd,&ps);
+}
+
+long CallTip::WndProc(WORD iMessage,WPARAM wParam,LPARAM lParam) {
+ //dprintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
+ switch (iMessage) {
+
+ case WM_CREATE:
+ break;
+
+ case WM_PAINT:
+ Paint();
+ break;
+
+ case WM_SETFONT:
+ font = (HFONT)wParam;
+ break;
+
+ case WM_SETTEXT:
+ free(val);
+ val = strdup((char *)lParam);
+ startHighlight = 0;
+ endHighlight = 0;
+ InvalidateRect(hwnd,(LPRECT)NULL,FALSE);
+ break;
+
+ case SCI_CALLTIPSETHLT:
+ startHighlight = wParam;
+ endHighlight = lParam;
+ InvalidateRect(hwnd,(LPRECT)NULL,FALSE);
+ break;
+
+ default:
+ return DefWindowProc(hwnd,iMessage,wParam,lParam);
+ }
+
+ //dprintf("end wnd proc\n");
+ return 0l;
+}
+
+LRESULT PASCAL CallTip::CWndProc(
+ HWND hWnd,UINT iMessage,WPARAM wParam, LPARAM lParam) {
+ //dprintf("C W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam);
+
+ // Find C++ object associated with window.
+ CallTip *ct = reinterpret_cast(GetWindowLong(hWnd,0));
+ // ct will be zero if WM_CREATE not seen yet
+ if (ct == 0) {
+ if (iMessage == WM_CREATE) {
+ // Create C++ object associated with window
+ ct = new CallTip();
+ ct->hwnd = hWnd;
+ SetWindowLong(hWnd, 0, reinterpret_cast(ct));
+ return ct->WndProc(iMessage, wParam, lParam);
+ } else {
+ return DefWindowProc(hWnd, iMessage, wParam, lParam);
+ }
+ } else {
+ if (iMessage == WM_DESTROY) {
+ delete ct;
+ SetWindowLong(hWnd, 0, 0);
+ return DefWindowProc(hWnd, iMessage, wParam, lParam);
+ } else {
+ return ct->WndProc(iMessage, wParam, lParam);
+ }
+ }
+}
+
+void CallTip_Register(HINSTANCE hInstance_) {
+ CallTip::Register(hInstance_);
+}
diff --git a/Pythonwin/scintilla_src/CallTip.h b/Pythonwin/scintilla_src/CallTip.h
new file mode 100644
index 0000000000..01f05a63b3
--- /dev/null
+++ b/Pythonwin/scintilla_src/CallTip.h
@@ -0,0 +1,7 @@
+// Scintilla source code edit control
+// CallTip.h - interface to the call tip control
+// Copyright 1998-1999 by Neil Hodgson
+// The License.txt file describes the conditions under which this software may be distributed.
+
+const char callClassName[] = "CallTip";
+void CallTip_Register(HINSTANCE hInstance_);
diff --git a/Pythonwin/scintilla_src/Document.cc b/Pythonwin/scintilla_src/Document.cc
new file mode 100644
index 0000000000..521287967d
--- /dev/null
+++ b/Pythonwin/scintilla_src/Document.cc
@@ -0,0 +1,622 @@
+// Scintilla source code edit control
+// Document.cc - manages the text of the document
+// Copyright 1998-1999 by Neil Hodgson
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef GTK
+#include
+#endif
+
+#include
+#include
+#include
+
+#include "Document.h"
+
+//#define TRACE
+
+static void dprintf(char *format, ...) {
+#ifdef TRACE
+ char buffer[2000];
+ char *pArguments = (char *) & format + sizeof(format);
+ vsprintf(buffer,format,pArguments);
+#ifdef GTK
+ printf("%s",buffer);
+#else
+ OutputDebugString(buffer);
+#endif
+#endif
+}
+
+LineCache::LineCache() {
+ linesData = 0;
+ Init();
+}
+
+LineCache::~LineCache() {
+ delete []linesData;
+ linesData = 0;
+}
+
+void LineCache::Init() {
+ delete []linesData;
+ linesData = new LineData[growSize];
+ size = growSize;
+ lines = 1;
+}
+
+void LineCache::Expand(int sizeNew) {
+ LineData *linesDataNew = new LineData[sizeNew];
+ if (linesDataNew) {
+ for (int i=0;i= size) {
+ Expand(size + growSize);
+ }
+ lines++;
+ for (int i=lines+1;i>pos;i--) {
+ linesData[i] = linesData[i-1];
+ }
+ linesData[pos].startPosition = value;
+ linesData[pos].marker = 0;
+}
+
+void LineCache::SetValue(int pos, int value) {
+ dprintf("SetValue[%d] = %d\n", pos, value);
+ if ((pos+2) >= size) {
+ //dprintf("Resize %d %d\n", size,pos);
+ Expand(pos + growSize);
+ //dprintf("end Resize %d %d\n", size,pos);
+ lines = pos;
+ }
+ linesData[pos].startPosition = value;
+}
+
+void LineCache::Remove(int pos) {
+ dprintf("Remove %d\n", pos);
+ // Retain the markers from the deleted line by oring them into the previous line
+ if (pos > 0) {
+ linesData[pos-1].marker |= linesData[pos].marker;
+ }
+ for (int i=pos;i= linesData[lines].startPosition)
+ return lines - 1;
+ int lower = 0;
+ int upper = lines;
+ int middle = 0;
+ do {
+ middle = (upper + lower + 1) / 2; // Round high
+ if (pos < linesData[middle].startPosition) {
+ upper = middle - 1;
+ } else {
+ lower = middle;
+ }
+ } while (lower < upper);
+//dprintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition);
+ return lower;
+}
+
+Action::Action() {
+ at = startAction;
+ position = 0;
+ data = 0;
+ lenData = 0;
+}
+
+Action::~Action() {
+}
+
+void Action::Destroy() {
+ delete []data;
+ data = 0;
+}
+
+void Action::Create(actionType at_, int position_, char *data_, int lenData_) {
+ delete []data;
+ position = position_;
+ at = at_;
+ data = data_;
+ lenData = lenData_;
+}
+
+Document::Document(int initialLength) {
+ body = new char[initialLength];
+ size = initialLength;
+ length = 0;
+ part1len = 0;
+ gaplen = initialLength;
+ part2body = body + gaplen;
+
+ actions = new Action[30000];
+ lenActions = 30000;
+ maxAction = 0;
+ currentAction = 0;
+ actions[currentAction].Create(startAction);
+
+ readOnly = false;
+}
+
+Document::~Document() {
+ delete []body;
+ body = 0;
+
+#ifdef NEED_UNDO_LOG
+ FILE *fp = fopen("Log.log", "wt");
+ fprintf(fp,"Max = %3d\n", maxAction);
+ fprintf(fp,"Current = %3d\n", currentAction);
+ for (int i=0;i part1len
+ int diff = position - part1len;
+ //dprintf("Move gap forwards to %d diff =%d\n", position,diff);
+ for (int i=0;i= length + 10) {
+ // dprintf("Very Bad position %d of %d\n",position,length);
+ //char sz[30];
+ //gets(sz);
+ // exit(3);
+ //}
+ if (position >= length) {
+ //dprintf("Bad position %d of %d\n",position,length);
+ //char sz[30];
+ //gets(sz);
+ return '\0';
+ }
+
+ if (position < part1len) {
+ return body[position];
+ } else {
+ return part2body[position];
+ }
+}
+
+void Document::SetByteAt(int position, char ch) {
+
+ if (position < 0) {
+ dprintf("Bad position %d\n",position);
+ return;
+ }
+ if (position >= length + 11) {
+ dprintf("Very Bad position %d of %d\n",position,length);
+ //exit(2);
+ return;
+ }
+ if (position >= length) {
+ dprintf("Bad position %d of %d\n",position,length);
+ return;
+ }
+
+ if (position < part1len) {
+ body[position] = ch;
+ } else {
+ part2body[position] = ch;
+ }
+}
+
+char Document::CharAt(int position) {
+ return ByteAt(position*2);
+}
+
+char Document::StyleAt(int position) {
+ return ByteAt(position*2 + 1);
+}
+
+void Document::InsertString(int position, char *s, int insertLength) {
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ // This takes up about half load time
+ char *data = new char[insertLength/2];
+ for (int i=0;i= 2) {
+ // See if current action can be coalesced into previous action
+ // Will work if both are inserts or deletes and position is same or two different
+ if ((at != actions[currentAction-1].at) || (abs(position-actions[currentAction-1].position) > 2)) {
+ currentAction++;
+ } else if (currentAction == savePoint) {
+ currentAction++;
+ }
+ } else {
+ currentAction++;
+ }
+ actions[currentAction].Create(at, position, data, length);
+ if (collectingUndo==undoCollectAutoStart) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ }
+ maxAction = currentAction;
+}
+
+void Document::DeleteChars(int position, int deleteLength) {
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ char *data = new char[deleteLength/2];
+ for (int i=0;i= 0) && (line < lc.lines))
+ lc.linesData[line].marker = marker;
+}
+
+int Document::GetMark(int line) {
+ if ((line >= 0) && (line < lc.lines))
+ return lc.linesData[line].marker;
+ return 0;
+}
+
+void Document::DeleteAllMarks(int markerNum) {
+ for (int line=0; line= 0)
+ chPrev = ByteAt(position - 2);
+ char chAfter = ' ';
+ if ((position + insertLength) < length)
+ chAfter = ByteAt(position + insertLength);
+ if (chPrev == '\r' && chAfter == '\n') {
+//dprintf("Splitting a crlf pair at %d\n", lineInsert);
+ // Splitting up a crlf pair at position
+ lc.InsertValue(lineInsert,position/2);
+ lineInsert++;
+ }
+ char ch = ' ';
+ for (int i=0;i= 2)
+ chPrev = ByteAt(position - 2);
+ char chBefore = chPrev;
+ char chNext = ' ';
+ if (position < length)
+ chNext = ByteAt(position);
+ bool ignoreNL = false;
+ if (chPrev == '\r' && chNext == '\n') {
+ //dprintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove);
+ // Move back one
+ lc.SetValue(lineRemove,position/2);
+ lineRemove++;
+ ignoreNL = true; // First \n is not real deletion
+ }
+ char ch = chNext;
+ for (int i=0;i