diff --git a/Pythonwin/Win32app.h b/Pythonwin/Win32app.h new file mode 100644 index 0000000000..d66fb22109 --- /dev/null +++ b/Pythonwin/Win32app.h @@ -0,0 +1,76 @@ + +// +// Python Thread object +// +class PyCWinThread : public PyCCmdTarget { +protected: + PyCWinThread(); + ~PyCWinThread(); +public: + static PyObject *create(PyObject *self, PyObject *args); + static ui_type_CObject type; + MAKE_PY_CTOR(PyCWinThread) +}; + +// +// Application Object. +// + +class PyCWinApp : public PyCWinThread { +protected: + PyCWinApp(); + ~PyCWinApp(); +public: + static PyObject *pExistingAppObject; + + static ui_type_CObject type; + MAKE_PY_CTOR(PyCWinApp) + static void cleanup(); +}; + + +///////////////////////////////////////////////////////////////////// +// +// Hack Application objects +// +// These objects are purely to get access to protected members. +// It is never instantiated. Therefore, it must not have virtual +// functions or data items. +// It is used purely so C++ casts can override protection. +class CProtectedDocManager : public CDocManager +{ +public: + CPtrList &GetTemplateList() {return m_templateList;} +}; + +class PYW_EXPORT CProtectedWinApp : public CWinApp { +public: + // how do I change from protected to public? + CString GetRecentFileName(int index); + void RemoveRecentFile(int index); + // Get main window - usually (but not always!) a CMDIFrameWnd + CWnd *GetMainFrame () {return m_pMainWnd;} + void SetMainFrame (CWnd *pWnd) {m_pMainWnd = pWnd;} + CDocument *FindOpenDocument (const char *lpszFileName); + BOOL Enable3dControls() {return CWinApp::Enable3dControls();} + void SetDialogBkColor(COLORREF clrCtlBk, COLORREF clrCtlText) { CWinApp::SetDialogBkColor(clrCtlBk, clrCtlText);} + BOOL HaveLoadStdProfileSettings() {return m_pRecentFileList!=NULL;} + void LoadStdProfileSettings(UINT max) {CWinApp::LoadStdProfileSettings(max);} + void SetRegistryKey(LPCTSTR key) {CWinApp::SetRegistryKey(key);} + void OnFileNew(void) {CWinApp::OnFileNew();} + void OnFileOpen(void) {CWinApp::OnFileOpen();} + CProtectedDocManager *GetDocManager(); + PyObject *MakePyDocTemplateList(void); +}; + +class CProtectedWinThread : public CWinThread { +public: + void PumpIdle(); + void PumpWaitingMessages(UINT firstMsg, UINT lastMsg); + void PumpMessages(); +}; + +inline CWinApp *GetApp() {CWinApp *ret = AfxGetApp(); if (ret==NULL) RETURN_ERR("There is no application object"); return ret;} +inline CProtectedWinApp *GetProtectedApp() {return (CProtectedWinApp *)GetApp();} +inline CProtectedWinThread *GetProtectedThread() {return (CProtectedWinThread *)GetApp();} +////////////////////////////////////////////////////////////////////// diff --git a/Pythonwin/Win32uiHostGlue.h b/Pythonwin/Win32uiHostGlue.h new file mode 100644 index 0000000000..fa60d697f3 --- /dev/null +++ b/Pythonwin/Win32uiHostGlue.h @@ -0,0 +1,249 @@ +// Win32uiHostGlue.h : Defines a connection between win32ui and its +// application object. + +// Sometimes I break this at the binary level - ie, all components must +// be in synch! Use a version number to check this. +#define WIN32UIHOSTGLUE_VERSION 3 + +class Win32uiHostGlue : public CObject { +public: + Win32uiHostGlue(); + ~Win32uiHostGlue(); + +#ifndef LINK_WITH_WIN32UI + // This will dynamically attach to win32ui.pyd. + BOOL DynamicApplicationInit(const char *cmd = NULL, const char *additionalPaths = NULL); +#else + BOOL ApplicationInit(const char *cmd = NULL, const char *additionalPaths = NULL); +#endif + // placeholder in case application want to provide custom status text. + virtual void SetStatusText(const char * /*cmd*/, int /*bForce*/) {return;} + // Helper class, to register _any_ HMODULE as a module name. + // This allows modules built into .EXE's, or in differently + // named DLL's etc. This requires admin priveliges on some machines, so + // a program should not refuse to start if this fails, but calling it + // each time means the app is guaranteed to work when moved. + // REMOVED - See below!!! +// BOOL RegisterModule(HMODULE hModule, const char *moduleName); + // or if you know the file name +// BOOL RegisterModule(const char *fileName, const char *moduleName); + + // These must be called by Host Application at the relevant time. + BOOL InitInstance() + {return pfnInitInstance ? (*pfnInitInstance)() : FALSE;} + int ExitInstance(void) \ + {return pfnExitInstance ? (*pfnExitInstance)() : -1;} + BOOL OnCmdMsg(CCmdTarget *pT, UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo ) \ + {return pfnOnCmdMsg ? (*pfnOnCmdMsg)(pT, nID, nCode, pExtra, pHandlerInfo) : FALSE;} + BOOL PreTranslateMessage(MSG *pMsg) \ + {return pfnPreTranslateMessage ? (*pfnPreTranslateMessage)(pMsg) : FALSE;} + BOOL OnIdle( LONG lCount ) \ + {return pfnOnIdle ? (*pfnOnIdle)(lCount) : FALSE;} + + // This can be used as the main application "Run" method + // if you want Python to have this level of control. + int Run() + { return pfnRun ? (*pfnRun)() : -1;} + + // Must be the last thing called, ever! + void ApplicationFinalize() + {if (pfnFinalize) (*pfnFinalize)();} + + + // some helpers for this class. + HKEY GetRegistryRootKey(); + + // function pointers. + BOOL (*pfnInitInstance)(); + int (*pfnExitInstance)(void); + BOOL (*pfnOnCmdMsg)(CCmdTarget *, UINT, int, void*, AFX_CMDHANDLERINFO* ); + BOOL (*pfnPreTranslateMessage)(MSG *pMsg); + BOOL (*pfnOnIdle)( LONG lCount ); + int (*pfnRun)(); + void (*pfnFinalize)(); + bool bShouldFinalizePython; // Should win32ui shut down Python? + bool bShouldAbandonThreadState; // Should win32ui abandon the thread state as it initializes? + int versionNo; // version ID of the creator of the structure. + bool bDebugBuild; // If the creator of the structure in a debug build? + bool bWantStatusBarText; // The app should want this if it wants to override the status bar. + +}; + +inline Win32uiHostGlue::Win32uiHostGlue() +{ + versionNo = WIN32UIHOSTGLUE_VERSION; + pfnInitInstance = NULL; + pfnExitInstance = NULL; + pfnOnCmdMsg = NULL; + pfnPreTranslateMessage = NULL; + pfnOnIdle = NULL; + pfnRun = NULL; + pfnFinalize = NULL; + bShouldFinalizePython = false; + bShouldAbandonThreadState = true; // Depends on how embedded. + bWantStatusBarText = false; // We can handle it by default. + bDebugBuild = +#ifdef _DEBUG + true; +#else + false; +#endif +} +inline Win32uiHostGlue::~Win32uiHostGlue() +{ +} + +inline HKEY Win32uiHostGlue::GetRegistryRootKey() +{ + // different for win32s. + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + return ver.dwPlatformId == VER_PLATFORM_WIN32s ? HKEY_CLASSES_ROOT : HKEY_LOCAL_MACHINE; +} + +/****** +If we really need to reinstate this, we should get the current version of Python +by calling "LoadLibrary" for Python15.dll, and get the version from that! + +inline BOOL Win32uiHostGlue::RegisterModule(HMODULE hModule, const char *moduleName) +{ + char fname[MAX_PATH]; + if (GetModuleFileName(hModule, fname, sizeof(fname))==0) { + TRACE("RegisterModule failed due to GetModuleFileName() error %d\n", GetLastError()); + return FALSE; + } + return RegisterModule(fname, moduleName); +} + +inline BOOL Win32uiHostGlue::RegisterModule(const char *fname, const char *moduleName) +{ + const char *keyRootName = "Software\\Python\\PythonCore\\"; + char keyBuf[256]; + char version[10]; + strcpy(keyBuf, keyRootName); + // Indirect through "CurrentVersion" - must read it. + strcat(keyBuf, "CurrentVersion"); + long retSize = sizeof(version); + HKEY hkey = GetRegistryRootKey(); + if (RegQueryValue(hkey, keyBuf, version, &retSize)!=ERROR_SUCCESS) { + TRACE("RegisterModule failed due to RegQueryValue() error %d\n", GetLastError()); + return FALSE; + } + // Build key to write. + strcpy(keyBuf, keyRootName); + strcat(keyBuf, version); + strcat(keyBuf, "\\Modules\\"); + strcat(keyBuf, moduleName); +#ifdef _DEBUG + strcat(keyBuf, "\\Debug"); +#endif + if (RegSetValue(hkey, + keyBuf, REG_SZ, + fname, strlen(fname))!=ERROR_SUCCESS) { + TRACE("RegisterModule failed due to RegSetValue() error %d\n", GetLastError()); + return FALSE; + } + return TRUE; +} +****/ +#ifndef LINK_WITH_WIN32UI +inline BOOL Win32uiHostGlue::DynamicApplicationInit(const char *cmd, const char *additionalPaths) +{ + char keyName[256]; + char version[32] = ""; + + const char *keyRootName = "Software\\Python\\PythonCore\\"; + HKEY hkey = GetRegistryRootKey(); + HMODULE hModWin32ui = NULL; + +#ifdef _DEBUG + HMODULE hModCore = LoadLibrary("Python15_d.dll"); // ?? variable? + char *szWinui_Name = "win32ui_d.pyd"; +#else + HMODULE hModCore = LoadLibrary("Python15.dll"); // ?? variable? + char *szWinui_Name = "win32ui.pyd"; +#endif + // Get the version string from the DLL. + LoadString(hModCore, 1000, version, sizeof(version)); + if (GetModuleHandle(szWinui_Name)==0) { // win32ui is not yet loaded - load it. + strcpy(keyName, keyRootName); + strcat(keyName, version); + strcat(keyName, "\\Modules\\win32ui"); +#ifdef _DEBUG + strcat(keyName, "\\Debug"); +#endif + char path[_MAX_PATH]; + long retSize = sizeof(path); + if (RegQueryValue(hkey, keyName, path, &retSize)==ERROR_SUCCESS) { + TRACE("Located registered win32ui at %s.\n", path); + hModWin32ui = LoadLibrary(path); + if (hModWin32ui==NULL) { + char buf[80]; + wsprintf(buf,"Warning - win32ui registered, but module load failed (%d)\n", GetLastError()); + OutputDebugString(buf); + } + } + } + // Now the modules are loaded, call the Python init functions. + int (__cdecl *pfnIsInit)(void); + pfnIsInit = (int (__cdecl *)(void))GetProcAddress(hModCore, "Py_IsInitialized"); + BOOL bShouldInitPython; + if (pfnIsInit) + bShouldFinalizePython = bShouldInitPython = !(*pfnIsInit)(); + else { + bShouldFinalizePython = FALSE; // Dont cleanup if we cant tell (this wont happen - Im paranoid :-) + bShouldInitPython = TRUE; + } + + void (__cdecl *pfnPyInit)(void); + pfnPyInit = (void (__cdecl *)(void))GetProcAddress(hModCore, "Py_Initialize"); + if (pfnPyInit && bShouldInitPython) { + (*pfnPyInit)(); + } + + if (hModWin32ui==NULL) + hModWin32ui = LoadLibrary(szWinui_Name); + if (hModCore) // free my temp instance - must be after win32ui, else it is unloaded! + FreeLibrary(hModCore); + if (hModWin32ui==NULL) { + char buf[256]; + sprintf(buf,"The application can not locate %s (%d)\n", szWinui_Name, GetLastError()); + int len = strlen(buf); + int bufLeft = sizeof(buf) - len; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL), buf+len, bufLeft, NULL); + AfxMessageBox(buf); + return FALSE; + } + BOOL (__cdecl *pfnWin32uiInit)(Win32uiHostGlue *, char *, const char *); + + pfnWin32uiInit = (BOOL (__cdecl *)(Win32uiHostGlue *, char *, const char *))GetProcAddress(hModWin32ui, "Win32uiApplicationInit"); + BOOL rc; + if (pfnWin32uiInit) + rc = (*pfnWin32uiInit)(this, (char *)cmd, additionalPaths); + else { + OutputDebugString("WARNING - win32uiHostGlue could not load the entry point for ApplicationInit\n"); + rc = FALSE; + } + FreeLibrary(hModWin32ui); + return rc; +} +#else + +extern "C" __declspec(dllimport) BOOL Win32uiApplicationInit(Win32uiHostGlue *pGlue, char *cmd, const char *addnPaths); +extern "C" void initwin32ui(); + +inline BOOL Win32uiHostGlue::ApplicationInit(const char *cmd, const char *additionalPaths) +{ + if (!Py_IsInitialized()) { + bShouldFinalizePython = TRUE; + Py_Initialize(); + } + // Make sure the statically linked win32ui is the one Python sees + // (and doesnt go searching for a new one) + initwin32ui(); + return Win32uiApplicationInit(this, (char *)cmd,additionalPaths); +} + +#endif \ No newline at end of file diff --git a/Pythonwin/contents.d b/Pythonwin/contents.d new file mode 100644 index 0000000000..78784db509 --- /dev/null +++ b/Pythonwin/contents.d @@ -0,0 +1,74 @@ +/* +Documentation File for Pythonwin + +@doc + +@topic Keyboard Bindings|Pythonwin has a new, flexible keyboard +binding mechanism. Bindings (and even code) can be defined in a +configuration file, stored in the pywin directory. + +The default configuration file is named default.cfg. You can view +this file with a text editor (eg, Pythonwin) This file has extensive +comments, including how to create your own configuration based on the +default. An example configuration which provides keyboard bindings +similar to IDLE exists in IDLE.cfg, and this makes a good example of a +custom configuration. + +Please see default.cfg for a complete list, but the default bindings +provided with Pythonwin are: +@flagh Common Keystrokes|Description +@flag Alt+Q|Reformat the current paragraph/comment block. Note this does NOT reformat code correctly - use only within comment blocks! +@flag Alt+/|Expand the word at the cursor. Eg, pressing "st\" +will complete based on all words in the current file - eg, "string" +would be likely to result assuming the code has an "import string" +statement. Pressing the key again expands to the next match. +@flag .|Auto expand the attribute. Eg, typing "string." will display a listbox with the contents of the string module. +@flag Alt+I|Toggle focus to/from the interactive window. + +@flagh Editor Specific Keystrokes|Description +@flag F2|Move to the next bookmark. +@flag Ctrl+F2|Add or remove a bookmark on the current line. +@flag Ctrl+G|Prompt for and goto a specific line number. +@flag Alt+B|Adds a simple comment banner at the current location. +@flag Alt+3|Block comment the selected region. +@flag Shift+Alt+3|Uncomment the selected region. +@flag Alt+4|Uncomment the selected region (IDLE default keystroke) +@flag Alt+5|Tabify the selected region. +@flag Alt+6|Untabify the selected region. +@flag BackSpace|Remove one indent to the left. +@flag Ctrl+T|Toggle the use of tabs for the current file (after confirmation) +@flag Alt+U|Change the indent width for the current file. +@flag Enter|Insert a newline and indent. +@flag Tab|Insert an indent, or perform a block indent is a selection +exists. +@flag Shift-Tab|Block dedent the selection + +@flagh Interactive Window Specific Keystrokes|Description +@flag Ctrl+Up|Recall the previous command in the history list. +@flag Ctrl+Down|Recall the next command in the history list. + +@topic Source Safe Integration| +Note you will need to restart Pythonwin for this option to take effect. + +Before using the VSS integration, you must create a "mssccprj.scc" file +in the directory, or a parent directory, of the files you wish to +integrate. There are no limits on how many of these files exist. This is +the same name and format as VB uses for VSS integration - a Windows INI +file. + +This file must have a section [Python] with entry "Project=ProjectName". +The project name is the name of the VSS project used to check the out +the file. If the .scc file is in a parent directory, the correct +relative VSS path is built - so if your file system matches your VSS +structure, you only need a single .scc file in the VSS "root" directory. + +For example, assuming you have the file c:\src\mssccprj.scc with the contents: +[Python] +Project=OurProject +-eof- +The file c:\src\source1.py will be checked out from project OurProject, +c:\src\sub\source2.py will be checked out from project OurProject\sub, +etc. + + +*/ diff --git a/Pythonwin/dbgthread.cpp b/Pythonwin/dbgthread.cpp new file mode 100644 index 0000000000..eb464a47bf --- /dev/null +++ b/Pythonwin/dbgthread.cpp @@ -0,0 +1,135 @@ +/* Debugger code + + This creates a window with debugging options in a secondary thread. + +*/ +#include "stdafx.h" +#include "reswin32ui.h" + +HWND hwndDebug = NULL; // The HWND of the main window for the thread. + +void ProcessShellMessage( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + UINT cmdId = 0; + switch (lParam) { + case WM_LBUTTONUP: + cmdId = ID_SHELL_ACTIVATE; + break; + case WM_RBUTTONUP: + { + + CMenu menu; + menu.LoadMenu(MAKEINTRESOURCE(IDR_SHELLTRAY)); +// HMENU hMenu = AfxGetApp()->LoadMenu(MAKEINTRESOURCE(IDR_SHELLTRAY)); +// HWND hWndMain = AfxGetMainWnd()->GetSafeHwnd(); + HMENU hMenuTrackPopup = *menu.GetSubMenu (0); // convert to a HMENU + POINT pt; + GetCursorPos(&pt); + // This is required when using a notify icon -- see KB article + // PRB: Menus for Notification Icons Don't Work Correctly + SetForegroundWindow (hWnd); + SetMenuDefaultItem(hMenuTrackPopup, 0, MF_BYPOSITION); + cmdId = TrackPopupMenu(hMenuTrackPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL); + + // This is required when using a notify icon -- see KB article + // PRB: Menus for Notification Icons Don't Work Correctly + ::PostMessage (hWnd, WM_USER, 0, 0); + break; + } + break; + } + + switch (cmdId) { + case ID_SHELL_ACTIVATE: + { + HWND hwndMain = AfxGetMainWnd()->GetSafeHwnd(); + BOOL ok = (hwndMain != NULL); + if (ok) + ::SetForegroundWindow(hwndMain); + if (ok) { + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + ok = ::GetWindowPlacement(hwndMain, &wp); + if (ok && wp.showCmd==SW_SHOWMINIMIZED) + ::ShowWindow(hwndMain, SW_RESTORE); + } + break; + } + case ID_SHELL_BREAK: + // set a flag to tell the process to break; + PyErr_SetInterrupt(); + break; + default: + break; + } +} +LRESULT CALLBACK DebuggerWndProc( HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam ) +{ + + switch( msg ) { + case WM_COMMAND: + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + case WM_USER+20: + ProcessShellMessage(hWnd, msg, wParam, lParam); + break; + case WM_USER+21: + DestroyWindow(hWnd); + break; +/**************************************************************\ +* Let the default window proc handle all other messages * +\**************************************************************/ + default: + return( DefWindowProc( hWnd, msg, wParam, lParam )); + } + return 0; +} + + +void StopDebuggerThread() +{ + NOTIFYICONDATA nid = { sizeof(NOTIFYICONDATA), hwndDebug, 0 }; + Shell_NotifyIcon(NIM_DELETE, &nid); + ::PostMessage(hwndDebug, WM_USER+21, 0, 0); +} + + +DWORD DebuggerThreadFunc( LPDWORD lpdwWhatever ) +{ + MSG msg; + + LPCTSTR cls = AfxRegisterWndClass( 0 ); + + WNDCLASS wc; + const char *className = "PythonDebugThreadClass"; + wc.lpszClassName = className; + wc.lpfnWndProc = DebuggerWndProc; + wc.style = /*CS_OWNDC |*/ CS_VREDRAW | CS_HREDRAW; + wc.hInstance = AfxGetInstanceHandle(); + wc.hIcon = NULL; + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = (HBRUSH)( COLOR_WINDOW ); + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + RegisterClass( &wc ); + hwndDebug = ::CreateWindowEx( 0, className, "Python", WS_OVERLAPPEDWINDOW, + 14, 8, 70, 60, + NULL, NULL, AfxGetInstanceHandle(), NULL ); + + HICON hIcon = AfxGetApp()->LoadIcon( MAKEINTRESOURCE(IDR_MAINFRAME) ); + DWORD flags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + NOTIFYICONDATA nid = { sizeof(NOTIFYICONDATA), hwndDebug, 0, flags, WM_USER+20, hIcon }; + _tcscpy(nid.szTip, "Pythonwin"); + Shell_NotifyIcon(NIM_ADD, &nid); + + while (GetMessage(&msg, 0, 0, NULL)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} \ No newline at end of file diff --git a/Pythonwin/dibapi.cpp b/Pythonwin/dibapi.cpp new file mode 100644 index 0000000000..5a30635cb3 --- /dev/null +++ b/Pythonwin/dibapi.cpp @@ -0,0 +1,624 @@ +// dibapi.cpp +// +// stolen from diblook MFC sample, and modified to kill off the Alloc/Lock style +// code. +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// PaintDIB() - Painting routine for a DIB +// CreateDIBPalette() - Creates a palette from a DIB +// FindDIBBits() - Returns a pointer to the DIB bits +// DIBWidth() - Gets the width of the DIB +// DIBHeight() - Gets the height of the DIB +// PaletteSize() - Gets the size required to store the DIB's palette +// DIBNumColors() - Calculates the number of colors +// in the DIB's color table +// CopyHandle() - Makes a copy of the given global memory block +// +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and Microsoft +// QuickHelp and/or WinHelp documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#include "stdafx.h" +#include "dibapi.h" + +/************************************************************************* + * + * PaintDIB() + * + * Parameters: + * + * HDC hDC - DC to do output to + * + * LPRECT lpDCRect - rectangle on DC to do output to + * + * LPSTR lpDIBHdr - pointer to packed-DIB memory block + * + * LPRECT lpDIBRect - rectangle of DIB to output into lpDCRect + * + * CPalette* pPal - pointer to CPalette containing DIB's palette + * + * Return Value: + * + * BOOL - TRUE if DIB was drawn, FALSE otherwise + * + * Description: + * Painting routine for a DIB. Calls StretchDIBits() or + * SetDIBitsToDevice() to paint the DIB. The DIB is + * output to the specified DC, at the coordinates given + * in lpDCRect. The area of the DIB to be output is + * given by lpDIBRect. + * + ************************************************************************/ + +BOOL WINAPI PaintDIB(HDC hDC, + LPRECT lpDCRect, + LPSTR lpDIBHdr, + LPRECT lpDIBRect, + CPalette* pPal, + DWORD dwRop) +{ + LPSTR lpDIBBits; // Pointer to DIB bits + BOOL bSuccess=FALSE; // Success/fail flag + HPALETTE hPal=NULL; // Our DIB's palette + HPALETTE hOldPal=NULL; // Previous palette + + /* Check for valid DIB */ + if (lpDIBHdr == NULL) + return FALSE; + + lpDIBBits = ::FindDIBBits(lpDIBHdr); + + // Get the DIB's palette, then select it into DC + if (pPal != NULL) + { + hPal = (HPALETTE) pPal->m_hObject; + + // Select as background since we have + // already realized in forground if needed + hOldPal = ::SelectPalette(hDC, hPal, TRUE); + RealizePalette (hDC); + } + + /* Make sure to use the stretching mode best for color pictures */ + ::SetStretchBltMode(hDC, COLORONCOLOR); + + /* Determine whether to call StretchDIBits() or SetDIBitsToDevice() */ + if ((RECTWIDTH(lpDCRect) == RECTWIDTH(lpDIBRect)) && + (RECTHEIGHT(lpDCRect) == RECTHEIGHT(lpDIBRect))) + bSuccess = ::SetDIBitsToDevice(hDC, // hDC + lpDCRect->left, // DestX + lpDCRect->top, // DestY + RECTWIDTH(lpDCRect), // nDestWidth + RECTHEIGHT(lpDCRect), // nDestHeight + lpDIBRect->left, // SrcX + (int)DIBHeight(lpDIBHdr) - + lpDIBRect->top - + RECTHEIGHT(lpDIBRect), // SrcY + 0, // nStartScan + (WORD)DIBHeight(lpDIBHdr), // nNumScans + lpDIBBits, // lpBits + (LPBITMAPINFO)lpDIBHdr, // lpBitsInfo + DIB_RGB_COLORS); // wUsage + else + bSuccess = ::StretchDIBits(hDC, // hDC + lpDCRect->left, // DestX + lpDCRect->top, // DestY + RECTWIDTH(lpDCRect), // nDestWidth + RECTHEIGHT(lpDCRect), // nDestHeight + lpDIBRect->left, // SrcX + lpDIBRect->top, // SrcY + RECTWIDTH(lpDIBRect), // wSrcWidth + RECTHEIGHT(lpDIBRect), // wSrcHeight + lpDIBBits, // lpBits + (LPBITMAPINFO)lpDIBHdr, // lpBitsInfo + DIB_RGB_COLORS, // wUsage + dwRop); // dwROP + + /* Reselect old palette */ + if (hOldPal != NULL) + { + ::SelectPalette(hDC, hOldPal, TRUE); + } + + return bSuccess; +} + +//--------------------------------------------------------------------- +// +// Function: PaintDDB +// +// stolen from DIBLook sample. +// +// Purpose: Painting routine for a DDB. Calls BitBlt() or +// StretchBlt() to paint the DDB. The DDB is +// output to the specified DC, at the coordinates given +// in lpDCRect. The area of the DDB to be output is +// given by lpDDBRect. The specified palette is used. +// +// IMPORTANT assumption: The palette has been realized +// elsewhere... We won't bother figuring out whether it +// should be realized as a foreground or background palette +// here. +// +// Parms: hDC == DC to do output to. +// lpDCRect == Rectangle on DC to do output to. +// hDDB == Handle to the device dependent bitmap (DDB). +// lpDDBRect == Rect of DDB to output into lpDCRect. +// hPal == Palette to be used. +// +// History: Date Reason +// 6/01/91 Created +// +//--------------------------------------------------------------------- + +BOOL WINAPI PaintDDB (HDC hDC, + LPRECT lpDCRect, + HBITMAP hDDB, + LPRECT lpDDBRect, + CPalette *pPal, + DWORD dwRop) +{ + HDC hMemDC; + HBITMAP hOldBitmap; + HPALETTE hOldPal1 = NULL; + HPALETTE hOldPal2 = NULL; + + HPALETTE hPal = (HPALETTE)pPal->GetSafeHandle(); + hMemDC = CreateCompatibleDC (hDC); + + if (!hMemDC) + return FALSE; + + if (hPal) + { + hOldPal1 = SelectPalette (hMemDC, hPal, FALSE); + hOldPal2 = SelectPalette (hDC, hPal, FALSE); + // Assume the palette's already been realized (no need to + // call RealizePalette(). It should have been realized in + // our WM_QUERYNEWPALETTE or WM_PALETTECHANGED messages... + } + + hOldBitmap = (HBITMAP)SelectObject (hMemDC, hDDB); + + SetStretchBltMode (hDC, COLORONCOLOR); + + if ((RECTWIDTH (lpDCRect) == RECTWIDTH (lpDDBRect)) && + (RECTHEIGHT (lpDCRect) == RECTHEIGHT (lpDDBRect))) + { + BitBlt (hDC, + lpDCRect->left, + lpDCRect->top, + lpDCRect->right - lpDCRect->left, + lpDCRect->bottom - lpDCRect->top, + hMemDC, + lpDDBRect->left, + lpDDBRect->top, + dwRop); + } + else + StretchBlt (hDC, + lpDCRect->left, + lpDCRect->top, + lpDCRect->right - lpDCRect->left, + lpDCRect->bottom - lpDCRect->top, + hMemDC, + lpDDBRect->left, + lpDDBRect->top, + lpDDBRect->right - lpDDBRect->left, + lpDDBRect->bottom - lpDDBRect->top, + dwRop); + + SelectObject (hMemDC, hOldBitmap); + + if (hOldPal1) + SelectPalette (hMemDC, hOldPal1, FALSE); + + if (hOldPal2) + SelectPalette (hDC, hOldPal2, FALSE); + + DeleteDC (hMemDC); + return TRUE; +} + +//--------------------------------------------------------------------- +// +// Function: DIBToBitmap +// +// stolen from DIBLook sample. +// +// Purpose: Given a handle to global memory with a DIB spec in it, +// and a palette, returns a device dependent bitmap. The +// The DDB will be rendered with the specified palette. +// +// Parms: hDIB == HANDLE to global memory containing a DIB spec +// (either BITMAPINFOHEADER or BITMAPCOREHEADER) +// hPal == Palette to render the DDB with. If it's NULL, +// use the default palette. +// +// History: Date Reason +// 6/01/91 Created +// +//--------------------------------------------------------------------- + +HBITMAP WINAPI DIBToBitmap (LPSTR lpDIBHdr, CPalette *pPal) +{ + LPSTR lpDIBBits; + HBITMAP hBitmap; + HDC hDC; + HPALETTE hOldPal = NULL; + + HPALETTE hPal = (HPALETTE)pPal->GetSafeHandle(); + lpDIBBits = FindDIBBits (lpDIBHdr); + hDC = GetDC (NULL); + + if (!hDC) + return NULL; + + if (hPal) + hOldPal = SelectPalette (hDC, hPal, FALSE); + + RealizePalette (hDC); + + hBitmap = CreateDIBitmap (hDC, + (LPBITMAPINFOHEADER) lpDIBHdr, + CBM_INIT, + lpDIBBits, + (LPBITMAPINFO) lpDIBHdr, + DIB_RGB_COLORS); + + if (!hBitmap) + return NULL; + + if (hOldPal) + SelectPalette (hDC, hOldPal, FALSE); + + ReleaseDC (NULL, hDC); + + return hBitmap; +} + + + + +/************************************************************************* + * + * CreateDIBPalette() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * HPALETTE - specifies the palette + * + * Description: + * + * This function creates a palette from a DIB by allocating memory for the + * logical palette, reading and storing the colors from the DIB's color table + * into the logical palette, creating a palette from this logical palette, + * and then returning the palette's handle. This allows the DIB to be + * displayed using the best possible colors (important for DIBs with 256 or + * more colors). + * + ************************************************************************/ + + +BOOL WINAPI CreateDIBPalette(LPSTR lpbi, CPalette* pPal) +{ + LPLOGPALETTE lpPal; // pointer to a logical palette + int i; // loop index + WORD wNumColors; // number of colors in color table + LPBITMAPINFO lpbmi; // pointer to BITMAPCOREINFO structure (win3) + LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (old) + BOOL bWinStyleDIB; // flag which signifies whether this is a Win3.0 DIB + BOOL bResult = FALSE; + + /* if DIB is invalid, return FALSE */ + if (lpbi == NULL) + return FALSE; + lpbmi = (LPBITMAPINFO)lpbi; + /* get pointer to BITMAPCOREINFO (old 1.x) */ + lpbmc = (LPBITMAPCOREINFO)lpbmi; + + /* get the number of colors in the DIB */ + wNumColors = ::DIBNumColors(lpbi); + + if (wNumColors != 0) + { + /* allocate memory block for logical palette */ + lpPal = (LPLOGPALETTE)new char[sizeof(LOGPALETTE) + + sizeof(PALETTEENTRY) + * wNumColors]; + + /* if not enough memory, clean up and return NULL */ + if (lpPal == 0) + return FALSE; + + /* set version and number of palette entries */ + lpPal->palVersion = PALVERSION; + lpPal->palNumEntries = (WORD)wNumColors; + + /* is this a Win 3.0 DIB? */ + bWinStyleDIB = IS_WIN30_DIB(lpbi); + for (i = 0; i < (int)wNumColors; i++) + { + if (bWinStyleDIB) + { + lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; + lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; + lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + else + { + lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed; + lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen; + lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + } + + /* create the palette and get handle to it */ + bResult = pPal->CreatePalette(lpPal); + delete lpPal; + } + return bResult; +} + +/************************************************************************* + * + * FindDIBBits() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * LPSTR - pointer to the DIB bits + * + * Description: + * + * This function calculates the address of the DIB's bits and returns a + * pointer to the DIB bits. + * + ************************************************************************/ + + +LPSTR WINAPI FindDIBBits(LPSTR lpbi) +{ + return (lpbi + *(LPDWORD)lpbi + ::PaletteSize(lpbi)); +} + + +/************************************************************************* + * + * DIBWidth() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - width of the DIB + * + * Description: + * + * This function gets the width of the DIB from the BITMAPINFOHEADER + * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * width field if it is an other-style DIB. + * + ************************************************************************/ + + +DWORD WINAPI DIBWidth(LPSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an other-style DIB + + /* point to the header (whether Win 3.0 and old) */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB width if it is a Win 3.0 DIB */ + if (IS_WIN30_DIB(lpDIB)) + return lpbmi->biWidth; + else /* it is an other-style DIB, so return its width */ + return (DWORD)lpbmc->bcWidth; +} + + +/************************************************************************* + * + * DIBHeight() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - height of the DIB + * + * Description: + * + * This function gets the height of the DIB from the BITMAPINFOHEADER + * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * height field if it is an other-style DIB. + * + ************************************************************************/ + + +DWORD WINAPI DIBHeight(LPSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an other-style DIB + + /* point to the header (whether old or Win 3.0 */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB height if it is a Win 3.0 DIB */ + if (IS_WIN30_DIB(lpDIB)) + return lpbmi->biHeight; + else /* it is an other-style DIB, so return its height */ + return (DWORD)lpbmc->bcHeight; +} + + +/************************************************************************* + * + * PaletteSize() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - size of the color palette of the DIB + * + * Description: + * + * This function gets the size required to store the DIB's palette by + * multiplying the number of colors by the size of an RGBQUAD (for a + * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an other- + * style DIB). + * + ************************************************************************/ + + +WORD WINAPI PaletteSize(LPSTR lpbi) +{ + /* calculate the size required by the palette */ + if (IS_WIN30_DIB (lpbi)) + return (WORD)(::DIBNumColors(lpbi) * sizeof(RGBQUAD)); + else + return (WORD)(::DIBNumColors(lpbi) * sizeof(RGBTRIPLE)); +} + + +/************************************************************************* + * + * DIBNumColors() + * + * Parameter: + * + * LPSTR lpbi - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - number of colors in the color table + * + * Description: + * + * This function calculates the number of colors in the DIB's color table + * by finding the bits per pixel for the DIB (whether Win3.0 or other-style + * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256, + * if 24, no colors in color table. + * + ************************************************************************/ + + +WORD WINAPI DIBNumColors(LPSTR lpbi) +{ + WORD wBitCount; // DIB bit count + + /* If this is a Windows-style DIB, the number of colors in the + * color table can be less than the number of bits per pixel + * allows for (i.e. lpbi->biClrUsed can be set to some value). + * If this is the case, return the appropriate value. + */ + + if (IS_WIN30_DIB(lpbi)) + { + DWORD dwClrUsed; + + dwClrUsed = ((LPBITMAPINFOHEADER)lpbi)->biClrUsed; + if (dwClrUsed != 0) + return (WORD)dwClrUsed; + } + + /* Calculate the number of colors in the color table based on + * the number of bits per pixel for the DIB. + */ + if (IS_WIN30_DIB(lpbi)) + wBitCount = ((LPBITMAPINFOHEADER)lpbi)->biBitCount; + else + wBitCount = ((LPBITMAPCOREHEADER)lpbi)->bcBitCount; + + /* return number of colors based on bits per pixel */ + switch (wBitCount) + { + case 1: + return 2; + + case 4: + return 16; + + case 8: + return 256; + + default: + return 0; + } +} + + +////////////////////////////////////////////////////////////////////////// +//// Clipboard support + +//--------------------------------------------------------------------- +// +// Function: CopyHandle (from SDK DibView sample clipbrd.c) +// +// Purpose: Makes a copy of the given global memory block. Returns +// a handle to the new memory block (NULL on error). +// +// Routine stolen verbatim out of ShowDIB. +// +// Parms: h == Handle to global memory to duplicate. +// +// Returns: Handle to new global memory block. +// +//--------------------------------------------------------------------- + +HANDLE WINAPI CopyHandle (HANDLE h) +{ + BYTE *lpCopy; + BYTE *lp; + HANDLE hCopy; + DWORD dwLen; + + if (h == NULL) + return NULL; + + dwLen = ::GlobalSize((HGLOBAL) h); + + if ((hCopy = (HANDLE) ::GlobalAlloc (GHND, dwLen)) != NULL) + { + lpCopy = (BYTE *) ::GlobalLock((HGLOBAL) hCopy); + lp = (BYTE *) ::GlobalLock((HGLOBAL) h); + + while (dwLen--) + *lpCopy++ = *lp++; + + ::GlobalUnlock((HGLOBAL) hCopy); + ::GlobalUnlock((HGLOBAL) h); + } + + return hCopy; +} + diff --git a/Pythonwin/dibapi.h b/Pythonwin/dibapi.h new file mode 100644 index 0000000000..081c686d4a --- /dev/null +++ b/Pythonwin/dibapi.h @@ -0,0 +1,45 @@ +// dibapi.h + +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and Microsoft +// QuickHelp and/or WinHelp documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + + +#ifndef _INC_DIBAPI +#define _INC_DIBAPI + +/* DIB constants */ +#define PALVERSION 0x300 + +/* DIB Macros*/ + +#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER)) +#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left) +#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top) + +// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits" +// parameter is the bit count for the scanline (biWidth * biBitCount), +// and this macro returns the number of DWORD-aligned bytes needed +// to hold those bits. + +#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + +/* Function prototypes */ +BOOL WINAPI PaintDIB (HDC, LPRECT, LPSTR, LPRECT, CPalette*, DWORD); +BOOL WINAPI PaintDDB (HDC, LPRECT, HBITMAP, LPRECT, CPalette*, DWORD); +HBITMAP WINAPI DIBToBitmap (LPSTR lpDIBHdr, CPalette *pPal); +BOOL WINAPI CreateDIBPalette(LPSTR lpbi, CPalette* cPal); +LPSTR WINAPI FindDIBBits (LPSTR lpbi); +DWORD WINAPI DIBWidth (LPSTR lpDIB); +DWORD WINAPI DIBHeight (LPSTR lpDIB); +WORD WINAPI PaletteSize (LPSTR lpbi); +WORD WINAPI DIBNumColors (LPSTR lpbi); +HANDLE WINAPI CopyHandle (HANDLE h); + +#endif //!_INC_DIBAPI diff --git a/Pythonwin/dllmain.cpp b/Pythonwin/dllmain.cpp new file mode 100644 index 0000000000..ac4fe28bcc --- /dev/null +++ b/Pythonwin/dllmain.cpp @@ -0,0 +1,248 @@ +// dllmain. + +#include "stdafx.h" +#include "afxdllx.h" + +#include "win32uiHostGlue.h" + +static HWND GetConsoleHwnd(void); + +HINSTANCE hWin32uiDll; // Handle to this DLL. +static AFX_EXTENSION_MODULE extensionDLL; +static CDynLinkLibrary *pDLL = NULL; + +//BOOL PyWin_bIsWin32s; // global, and aint gunna change over 1 app lifetime! +BOOL PyWin_bHaveMFCHost = TRUE; // indicates if the CWinApp was locally created. + +extern BOOL bInFatalShutdown; +extern void Win32uiFinalize(); + +class CInProcApp : public CWinApp +{ +public: + CInProcApp(LPCTSTR lpszAppName); + Win32uiHostGlue glue; + void CleanupMainWindow(); + +public: + virtual BOOL InitInstance(); + virtual int ExitInstance() { + glue.ExitInstance(); // ignore errors + return CWinApp::ExitInstance(); + } + DECLARE_MESSAGE_MAP() +private: + virtual BOOL PreTranslateMessage(MSG *pMsg) { + if (glue.PreTranslateMessage(pMsg)) + return TRUE; + else + return CWinApp::PreTranslateMessage(pMsg); + } + + virtual BOOL OnIdle( LONG lCount) { + // call base class idle first + if (CWinApp::OnIdle(lCount)) + return TRUE; + return glue.OnIdle(lCount); + } + BOOL 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 m_bIsConsoleWindow; + +}; + +static CInProcApp *pCreatedApp = NULL; + +///////////////////////////////////////////////////////////////////////////// +// CInProcApp + +BEGIN_MESSAGE_MAP(CInProcApp, CWinApp) + //{{AFX_MSG_MAP(CInProcApp) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CInProcApp construction +CInProcApp::CInProcApp(LPCTSTR lpszAppName) : + CWinApp(lpszAppName) +{ + PyWin_bHaveMFCHost = FALSE; + m_bIsConsoleWindow = FALSE; // Assume not until we find otherwise. + glue.bShouldAbandonThreadState = FALSE; + // Place all significant initialization in InitInstance + // if I have a console window, make it the main window. + HWND main = GetConsoleHwnd(); + if (main) { + CWnd *pWndMain = new CWnd(); + pWndMain->Attach(main); + m_pMainWnd = pWndMain; + m_bIsConsoleWindow = TRUE; + } +} + +///////////////////////////////////////////////////////////////////////////// +// CInProcApp initialization + +BOOL CInProcApp::InitInstance() +{ + if (!glue.DynamicApplicationInit()) + return FALSE; + return glue.InitInstance(); +} + +// Check that we have a valid CWinApp object to use. +bool CheckGoodWinApp() +{ +// Shouldnt need special symbols now that we delay the creation. + // If the host exports a special symbol, then + // dont create a host app. +// HMODULE hModule = GetModuleHandle(NULL); +// BOOL hasSymbol = (GetProcAddress(hModule, "NoCreateWinApp") != NULL); + if (AfxGetApp()==NULL) { // && !hasSymbol) { + // shared initialization + pCreatedApp = new CInProcApp("win32ui module"); + + // As we are looking for a WinApp, we are likely to be creating the + // application object itself. Trick MFC into thinking we are not + // a DLL extension, but the app itself. + AfxGetModuleState()->m_bDLL = 0; // XXX - todo - expose this to Python??? + + // Do the WinMain thang... + // AFX internal initialization + if (!AfxWinInit(hWin32uiDll, NULL, "", SW_NORMAL)) + return 0; + + // App global initializations (rare) + ASSERT_VALID(pCreatedApp); + if (!pCreatedApp->InitApplication()) + return 0; + + // Perform specific initializations + if (!pCreatedApp->InitInstance()) + { + pCreatedApp->CleanupMainWindow(); + pCreatedApp->ExitInstance(); + return 0; + } + ASSERT_VALID(pCreatedApp); + if (AfxGetApp()==NULL) + OutputDebugString("Warning - still no CWinApp I can use!"); + } + return TRUE; +} + +void CInProcApp::CleanupMainWindow() +{ + if (m_pMainWnd==NULL) return; + + if (m_bIsConsoleWindow) { + Python_delete_assoc(m_pMainWnd); + m_pMainWnd->Detach(); + delete m_pMainWnd; + m_pMainWnd = NULL; + m_bIsConsoleWindow = FALSE; + } else { + TRACE0("Warning: Destroying non-NULL m_pMainWnd\n"); + m_pMainWnd->DestroyWindow(); + } +} + +#ifndef FREEZE_WIN32UI +extern "C" int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) +#else +extern "C" __declspec(dllexport) int __stdcall DllMainwin32ui(HINSTANCE hInstance, DWORD dwReason, LPVOID) +#endif + +{ + if (dwReason == DLL_PROCESS_ATTACH) { + hWin32uiDll = hInstance; + // Get Win32s version, etc +// OSVERSIONINFO ver; +// ver.dwOSVersionInfoSize = sizeof(ver); +// GetVersionEx(&ver); +// PyWin_bIsWin32s = ver.dwPlatformId == VER_PLATFORM_WIN32s; + + char path[_MAX_PATH]; + GetModuleFileName(hInstance, path, sizeof(path)); +#ifndef FREEZE_WIN32UI + // Normal win32ui.pyd initialization +#ifdef _DEBUG + TRACE("Extension module %s initialising.\n", path); +#endif + // Extension DLL one-time initialization + if (!AfxInitExtensionModule(extensionDLL, hInstance)) + return 0; + // insert into resource chain. + pDLL = new CDynLinkLibrary(extensionDLL); + +#else // Frozen .EXE that embedds win32ui is initializing + TRACE("win32ui in frozen %s initializing.\n", path); +#endif + } else if (dwReason == DLL_PROCESS_DETACH) { +// Py_Cleanup(); +// NOT safe to cleanup here - other DLLs may have already been unloaded + + // From this point on, trying to do anything would be pretty serious! + // (believe it or not, CoUninitialize() called after this point will + // still manage to call into this DLL!! + bInFatalShutdown = TRUE; + + if (pCreatedApp) { + + pCreatedApp->CleanupMainWindow(); + // We dont call ExitInstance, as the InitInstance we called could + // not have possibly called back to Python, as the Python app object + // could not have been created. Let the Python code manage if it wants! + Win32uiFinalize(); + Python_delete_assoc(pCreatedApp); // so Python wont try and use it. + AfxWinTerm(); + afxCurrentWinApp = NULL; // So AfxGetApp fails from here. + delete pCreatedApp; + pCreatedApp = NULL; + } + delete pDLL; + } + return 1; // ok +} + +// straight from the SDK. +HWND GetConsoleHwnd(void) +{ + #define MY_BUFSIZE 1024 // buffer size for console window titles + HWND hwndFound; // this is what is returned to the caller + char pszNewWindowTitle[MY_BUFSIZE]; // contains fabricated WindowTitle + char pszOldWindowTitle[MY_BUFSIZE]; // contains original WindowTitle + + // fetch current window title + if (GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE)==0) + return NULL; + + + // format a "unique" NewWindowTitle + wsprintf(pszNewWindowTitle,"%d/%d", + GetTickCount(), + GetCurrentProcessId()); + + // change current window title + SetConsoleTitle(pszNewWindowTitle); + + // ensure window title has been updated + Sleep(40); + + // look for NewWindowTitle + hwndFound=FindWindow(NULL, pszNewWindowTitle); + + // restore original window title + SetConsoleTitle(pszOldWindowTitle); + + return(hwndFound); +} + diff --git a/Pythonwin/doc/EmbeddingWin32ui.html b/Pythonwin/doc/EmbeddingWin32ui.html new file mode 100644 index 0000000000..a70bbe9af7 --- /dev/null +++ b/Pythonwin/doc/EmbeddingWin32ui.html @@ -0,0 +1,132 @@ + + + + +Embedding Pythonwin + + + + +

Embedding Pythonwin.

+

Pythonwin is distributed as 2 key components - Pythonwin.exe and win32ui.pyd. Win32ui.pyd contains the raw functionality of Pythonwin - Pythonwin.exe is a simple "wrapper" .exe that hosts win32ui.pyd.

+

This document describes how to embed the win32ui extension module in your own application.

+

Limitations.

+

The biggest limitations you will find are that a host application must

+ +
    +
  • Have some small changes made to the application source to fully support win32ui command handling and routing (although most things work without modification)
  • +
  • Be an MFC application
+ +

Below is some example code needed to embed win32ui in your application..

+

Changes to the application.

+

Subclass a Win32uiHostGlue class

+

You must instantiate a Win32uiHostGlue class. This class is used as a glue between win32ui.pyd and the host .exe, and is defined in Win32uiHostGlue.h

+

Currently, this consists of about 5 methods that must be called by your application. The principle is that the Application must hook certain handlers, and delegate them to the HostGlue class. This will ensure the appropriate Win32ui internals are called.

+

Embedding win32ui Architecture

+

win32uihostglue.h

+

The win32uihostglue.h module defines a class which makes interfacing fairly simple. This Win32uiHostGlue class is used as a glue between win32ui.pyd and the host .exe. In the most simple case, you need to instantiate one of the classes, and at certain key points in your CWinApp derived class, call the appropriate methods. You may choose to provide your own glue class derived from Win32uiHostGlue in certain cases.

+

Below is an example class, which overrides the "SetStatusText" method, so that status information displays in the applications status bar (this is only necessary if your application has a "non standard" status bar - normally you could omit this.).

+
GameApp NEAR theApp; // My existing CWinApp derived class. 
+// HostGlue class. 
+
+class GameHostGlue : public Win32uiHostGlue 
+{ 
+  virtual void SetStatusText(const char *text, BOOL bForce) 
+    // Assuming our GameApp above defines a SetStatusText method 
+    {GetApp()->SetStatusText(text, bForce);} 
+};
+
+// The one and only Glue object. 
+GameHostGlue NEAR glue; 
+

And now we are well on our way.

+

Delegating to win32uiHostGlue

+

You need to either implement, or modify, certain key methods of your Application object. Probably the most important is the call to initialise win32ui. You need to modify your CWinApp::InitInstance method (it is almost certain you already have one). The following code needs to be executed in this method:

+

InitInstance

+
BOOL GameApp::InitInstance() 
+{ 
+... 
+  if (!glue.DynamicApplicationInit("import initscore", csScripts)) { 
+  // Assuming you have a ReportError method - do whatever makes sense! 
+    ReportError("Could not attach to the Python win32ui extensions"); 
+    return FALSE; 
+  } 
+... 
+

Note the following:

+ +
    +
  • The first param - "import initscore" is the Python command that will be executed to kick in the Python side of the world. For Pythonwin, this is "import pywin.framework.startup" - see "initscore.py" below.
  • +
  • The second param may be empty or NULL, and defines an additional PythonPath to be added. This is useful if you do not want your application .py/.pyd files on the main PythonPath. In the example above, code not shown loads csScripts from the applications INI file.
  • +
  • The example above assumes the Python code is not responsible for creating the main application window. In the case of Pythonwin (where the Python code does create the main window) this call actually appears in InitApplication() for the CWinApp class. The example above assumes you have existing C++ code which creates the main frame window. Your Python code could create child windows, as the initscore.py file demonstrates.
+ +

And the Rest

+

Below is the rest of the code you need to implement. You may need to create these methods, as the AppWizard generated MFC application does not have some.

+
BOOL 
+GameApp::OnCmdMsg (UINT nID, int nCode, 
+void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo) 
+{ 
+  // yield to Python first - send to the main frame, as there is no Python app object. 
+  if (glue.OnCmdMsg (m_pMainWnd, nID, nCode, pExtra, pHandlerInfo)) 
+    return TRUE; 
+  else 
+    return CWinApp::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo); 
+} 
+
+BOOL GameApp::PreTranslateMessage(MSG *pMsg) 
+{ 
+  if (glue.PreTranslateMessage(pMsg)) 
+    return TRUE; 
+  else 
+    return CWinApp::PreTranslateMessage(pMsg); 
+} 
+BOOL GameApp::OnIdle( LONG lCount ) 
+{ 
+  // call base class idle first 
+  if (CWinApp::OnIdle(lCount)) 
+    return TRUE; 
+  return glue.OnIdle(lCount); 
+} 
+ +
    +

  • initscore.py
  • +
  • Below is the code for initscore.py. Obviously your code will vary, depending on your requirements.
+ +
import sys 
+import win32ui 
+# First step - redirect python output to the debugging device, until we 
+# can create a window to capture it. 
+
+# Note that this is only useful while debugging, and simply sends any 
+# Python output (ie, exceptions while developing the startup code) is 
+# printed to the MSVC debugger. Note that since this code was written, 
+# the win32trace module has appeared, giving another alternative to 
+# debugging this code. 
+class DebugOutput: 
+  softspace=1 
+  def write(self,message): 
+    win32ui.OutputDebug(message) 
+sys.stderr=sys.stdout=DebugOutput() 
+
+# One of your first priorities should be to set sys.stdout to somewhere useful,
+# depending on what useful means to your application. This code simply creates 
+# the Pythonwin Interactive Window, which handles this automatically. 
+
+# Now here is the code that does the real work. 
+import win32con 
+

from pywin.framework import intpyapp, app

+
+class ScoreApp(intpyapp.InteractivePythonApp):
+  def InitInstance(self):
+    # Call the base class (if you want)
+    intpyapp.InteractivePythonApp.InitInstance(self)
+    # Do domething useful, specific to your app.
+    # Here, we minimise the interactive window.
+    # (after painting the main frame)
+    win32ui.PumpWaitingMessages()
+    interact.edit.currentView.GetParent().ShowWindow(win32con.SW_MINIMIZE)
+
+#  def OnExitInstance(self):
+#    return 0
+    
+app = ScoreApp()
+

And we are done

+ diff --git a/Pythonwin/doc/architecture.html b/Pythonwin/doc/architecture.html new file mode 100644 index 0000000000..1ad7c7a0d5 --- /dev/null +++ b/Pythonwin/doc/architecture.html @@ -0,0 +1,66 @@ + + + + +Pythonwin's architecture + + + + +

Pythonwin's architecture.

+

Introduction

+

This document describes the general architecture of Pythonwin. It describes the general design and interface to MFC.

+

After reading this, you should read the document and view architecture documentation.

+

Objects, types, etc.

+

For each MFC object created, there are usually 3 "objects" floating around. Although this may initially seem confusing, it does provide significant flexibility.

+

In general this complexity is hidden from view. The programmer usually only ever deals with a single object, and this object manages all others. However, an understanding if the implementation will assist in advanced programming techniques.

+

There is a quick example which shows all this in action.

+

C++ objects

+

For every window, toolbar, document etc. that exists in Pythonwin, there is a C++ object. In some cases this object may be a standard MFC object, as found in the MFC DLL's. In other cases, the C++ object will be derived from a standard MFC object, and can be found in win32ui.pyd.

+

Often, these C++ objects will not be aware that there is Python support behind them.

+

win32ui types.

+

The win32ui module provides a large number of types, each one usually associated with an MFC C++ object.. The naming conventions for these objects is of the form "Py{MFC_Name}" - eg, PyCDocument provides the functionality of an MFC CDocument, PyCDialog for a CDialog.

+

There is a form of inheritance used for these objects. Each object provides methods from its derived class. A PyCDialog is derived from a PyCWnd, and therefore has all PyCWnd methods available. However, this is as far as the inheritance extends, as Python types are more restrictive than Python classes.

+

Each method that a win32ui type provides is generally just code for extracting Python arguments and making the call to the associated C++ object.

+

These objects and methods are defined in Pythonwin.hlp (available from the Pythonwin help menu.)

+

"Object" base class

+

In object.py, there is a base class "Object". This provides 2 key functions.

+ +
    +
  • Its constructor (__init__ method) registers the class with a win32ui type object (see below). This allows all virtual methods (see below) to be handled by a class method.
  • +
  • It allows itself to look exactly like the underlying win32ui type. It does this by storing the win32ui object in self._obj_, and providing a __getattr__ method. See the standard Python documentation for information on __getattr__ in a Python class.
+ +

The end result of this is that a class derived from "Object" looks just like a win32ui object, and can be used as such. In fact, there is rarely a reason to use the underlying object directly.

+

Python Classes

+

If you like you can skip this, and jump straight to the example below. It makes more sense in action!

+

Pythonwin provides support for many "virtual" functions. A "virtual" function is defined as a virtual C++ MFC function which has deferred its implementation to a Python object. To explain, I will use the example of a document.

+

In Python, it is possible to create a PyCDialog object by using win32ui.CreateDialog(). This will create both a PyCDialog object, and a C++ CDialog object. For dialogs to be useful to a programmer, it must be possible to define a method to be called when the dialog initialises (i.e., when the C++ OnInitDialog virtual method is called). Normally, a programmer would initialise the values of controls etc. at this time. To support this, the C++ OnInitDialog implementation calls Python to handle the call. In this example, OnInitDialog is a "virtual" function.

+

Each win32ui type supports being "attached" to a Python class instance. When a virtual function is called, the Python class instance associated with the object is checked to see if it has a method with the same name. If so, the method is called.

+

More details on this can be found in the document and view architecture documentation.

+

Example of this in action

+

>>> import win32ui # import the base win32ui module.
+>>> import dialog # import dialog.py
+>>> d=dialog.Dialog(win32ui.IDD_SIMPLE_INPUT)
+>>> d
+<Dialog instance at 886c78>

+

This creates an instance of class Dialog, defined in dialog.py. Dialog is derived from Object (via Wnd). To see the underlying win32ui type:

+

>>> d._obj_
+object 'PyCDialog' - assoc is 002F3278, vf=True, ch=0, mh=2, kh=0
+>>>
+
This output shows the C++ CDialog object is at address 0x002F3278, that there is a virtual function handler (i.e., the class instance "d"), that 2 messages have hooks, and no command or keyboard handlers are installed.

+

Even though the DoModal() method is implemented in the underlying win32ui type, the class object can be used just like the win32ui object. Thus:

+

>>> d.DoModal()
+1
+>>>

+

To see virtual functions in action

+
>>> class MyDialog(dialog.Dialog):
+... def OnInitDialog(self, msg):
+... self.GetDlgItem(win32ui.IDC_PROMPT1).SetWindowText("Hello")
+... return 1
+...
+>>> d=MyDialog(win32ui.IDD_SIMPLE_INPUT)
+>>> d.DoModal()
+1
+

Note the prompt on the dialog box is now "Hello".
+

+ diff --git a/Pythonwin/doc/docview.html b/Pythonwin/doc/docview.html new file mode 100644 index 0000000000..cd263c96a0 --- /dev/null +++ b/Pythonwin/doc/docview.html @@ -0,0 +1,54 @@ + + + + +Documents and Views in Pythonwin + + + + +

Documents and Views in Pythonwin

+

Introduction

+

This document describes the document/view architecture of Pythonwin. It is assumed you have read the Pythonwin architecture documentation and are somewhat familiar with the standard MFC architecture.

+

Wherever possible, Pythonwin and the samples have followed the same architecture as MFC. This has advantages and disadvantages. Often the MFC architecture does not seem to fit with small, short GUI utilities, due to the reliance on "document" based applications. On the positive, Pythonwin can leverage off the full MFC functionality. This means minimal code to get full-blown application support.

+

Document and View - Control Flow

+

[This needs lots more work. There where 2 flow-charts, which were semi-useless!]

+

The creation of the frame and views are typically done via the PyCWnd.LoadFrame method. During this call, the windows are created, and virtual functions pass control back to Python. The LoadFrame flow is shown in the separate diagram.

+

Object Oriented Documents

+

Sam Rushing once asked me "there needs to be a way to create 'object-oriented' documents, by that I mean a document with an object associated with it rather than a string. Currently the only way to open a document forces you to use a string (or None)."

+

The most obvious path to take would seem to override "OpenDocumentFile" or some such, and pass an object rather than a file name. The document could then load itself from the object, and all would be happy. Unfortunately, the tight integration with MFC means this can not work, and led to Sam's frustration above.

+

On the other hand, the "OpenDocumentFile" method is provided for integration with a standard MDI interface, and the Windows explorer. Obviously, it is not possible to select "File/Open", and load an object based document.

+

However, I didn't have an easy answer for him.

+

MFC's OLE Implementation

+

The closest example to the "right" way to do it seemed to be the MFC OLE implementation. When you have an object embedded in another document, and the user wants to edit the object, MFC must load the object without a simple "file name" parameter. MFC's approach is this:

+ +
    +
  • A normal blank document is created (via OnNewDocument)
  • +
  • COLEServerDoc's OLE XPersistStorage::Load() method is called, with an "object" (the OLE storage pointer)
  • +
  • The Documents OnOpenEmbedding() method is called.
  • + +
      +
    • OnOpenDocument is called, with a NULL file name parameter.
    • +
    • Serialize is called.
    +
+ +

Instead of changing the meaning of the existing functions, MFC adds new definitions for derived classes, such as OnOpenEmbedding

+

Back to our example

+

The cleanest thing seems to be to duplicate the functionality of "OpenDocumentFile" - such as "OpenObject". You would do this by:

+ +
    +
  • Check to see if there is already a document representing the document open. If so, switch to it and return.
  • +
  • Create a new document object.
  • +
  • Create a frame for the document
  • +
  • Call OnNewDocument to initialise the document.
  • +
  • Set the documents title
  • +
  • Call InitialUpdateFrame for the initial display.
+ +

"objdoc.py".

+

In the Pythonwin demos directory, there is a file "objdoc.py".

+

This module defines a template, which has an "OpenObject" method, and takes any Python object as an argument. If window with the object is already open, it is switched to. If not, a edit window is created, and the objects "repr" is written to the window.

+

Standard virtual function specification.

+

I'm not sure where to stick this!

+

In general, the C++ virtual function handlers follow the same general logic. If no handler is defined, the base class is called. If a Python handler is defined, the base C++ class is not called. In the latter case, it is up to the Python programmer to ensure the base class is called if necessary. This is normally done by calling the method in the underlying _obj_.
+

+ diff --git a/Pythonwin/doc/guienvironment.html b/Pythonwin/doc/guienvironment.html new file mode 100644 index 0000000000..43a2fdaaa7 --- /dev/null +++ b/Pythonwin/doc/guienvironment.html @@ -0,0 +1,44 @@ + + + + +Pythonwin GUI Environment + + + + +

Pythonwin GUI Environment

+

This document describes the Pythonwin GUI environment. There are quite a few little features hidden away!

+

It has a discussion on the General Environment (including the Interactive Window and Menu and Toolbar), and the Command Line Arguments supported.

+

General Environment

+

Interactive Window

+

The Interactive Window is a little Python program which simulates the built-in Python interpreter. It is implemented in intpyapp.py (which in turn uses winout.py - a general purpose "output window".)

+

Pressing Enter

+

Whenever you press Enter, the interactive window has a look at the context, to try and decide what to do.

+

If the line is recognised as a "block" (ie, the first line starts with ">>> ", and the rest start with "� "), it checks to see if we are at the end of the window. If not, the block is copied to the end of the buffer - nothing is executed - enter must be pressed again to force execution.

+

If we are at the end of the buffer, it always attempts to execute the line. If Python indicates "unexpected end of file", then it assumes the block should continue, and emits the "� " for the next line.

+

If the line is recognised as part of a Python exception, then some special processing is done. If the line contains a filename and line number, it locates that file, and jumps to the offending line.

+

If the line is recognised as being an exception generated by COM (aka OLE), and the exception contains reference to a WinHelp file, the help topic is opened.

+

Menu and Toolbar

+

File Open/Close/Save/Print/etc.

+

All perform the obvious file operations. Pythonwin follows the standard MFC framework, and allows Python programs to plug-in support for other file types.

+

Locate File

+

Allows quick location of a Python scripts. Typing in the base name of a file will force a search of all the directories on sys.path, until a match is found.

+

To locate a module in a package, use a (back) slash instead of a dot - eg, to locate "win32com.client.dynamic", type in "win32com\client/dynamic"

+

Import Script

+

Imports or reloads a script - just like "import scriptname"/"reload(scriptname)" - (Pythonwin determines if the operation is an import or reload, It also handles "ni" modules with some success!)

+

If you select this (or use <Ctrl-I>) when a .py file is open, that file is saved and imported. If a current file can not be located, it will ask for a file to import via a File dialog.

+

If a syntax error occurs, the location will be moved to in the editor.

+

Run Script

+

Runs a script, as if the file was passed on the command line. A dialog is opened, asking the script name and arguments (unless the <Shift> key is held down)

+

Command Line Arguments

+

The following command line arguments are recognised. Note that these are implemented in "intpyapp.py", so should be pretty easy to change!

+

/run scriptname args

+

Runs the named script. Works just like "Python.exe scriptname args", except it is run in the GUI environment, and output goes to the interactive window.

+

/runargs scriptname args

+

As above, except the normal Pythonwin 'Run Script' dialog is shown.

+

/edit filename.py

+

Edits the filename. The /edit is currently optional, but in the future, the "default argument handling" may be changed.

+

/app specialscriptname args

+

Runs a special "application script". The application script can change the entire look and feel of a Pythonwin program - almost as much flexibility as the C++ programmer has when they first start their application). An example of this is "dlgapp.py" in the Pythonwin distribution.

+ diff --git a/Pythonwin/doc/pythonwin.gif b/Pythonwin/doc/pythonwin.gif new file mode 100644 index 0000000000..78992e815e Binary files /dev/null and b/Pythonwin/doc/pythonwin.gif differ diff --git a/Pythonwin/pythonRichEdit.cpp b/Pythonwin/pythonRichEdit.cpp new file mode 100644 index 0000000000..05d915bd14 --- /dev/null +++ b/Pythonwin/pythonRichEdit.cpp @@ -0,0 +1,96 @@ +// pythonRichEdit.cpp : implementation file +// + +#include "stdafx.h" +#include "pythonwin.h" +#include "win32ui.h" +#include "pythonRichEdit.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPythonRichEditView + +CPythonRichEditView::CPythonRichEditView() +{ +} + +CPythonRichEditView::~CPythonRichEditView() +{ +} + +BOOL +CPythonRichEditView::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 CRichEditView::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo); +} + +BOOL +CPythonRichEditView::OnNotify (WPARAM wParam, LPARAM lParam, LRESULT *pResult) +{ + // yield to Python first + if (Python_OnNotify (this, wParam, lParam, pResult)) + return TRUE; + else + return CRichEditView::OnNotify (wParam, lParam, pResult); +} + +#ifdef PYWIN_WITH_WINDOWPROC +LRESULT CPythonRichEditView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) +{ + // @pyvirtual int|PyCRichEditView|WindowProc|Default message handler. + // @xref + LRESULT res; + CVirtualHelper helper( "WindowProc", this ); + if (!helper.HaveHandler() || !helper.call(message, wParam, lParam) || !helper.retval(res)) { + try { + return CRichEditView::WindowProc(message, wParam, lParam); + } catch (...) { + TRACE("RichEditView WindowProc caused access violation!"); + res = 0; + } + } + return res; +} +#endif // PYWIN_WITH_WINDOWPROC + +void +CPythonRichEditView::OnInitialUpdate() +{ + // @pyvirtual tuple|PyCRichEditView|OnInitialUpdate|Called before the first update for a view. + // @xref + CVirtualHelper helper ("OnInitialUpdate", this); + if (helper.HaveHandler()) + helper.call(); + else + CRichEditView::OnInitialUpdate(); +} + +#ifdef _DEBUG +void CPythonRichEditView::Dump( CDumpContext &dc ) const +{ + try { + CRichEditView::Dump(dc); + } catch (...) { + dc << "***** CRichEditView::Dump caused win32 exception"; + } +} +#endif + +BEGIN_MESSAGE_MAP(CPythonRichEditView, CRichEditView) + //{{AFX_MSG_MAP(CPythonRichEditView) + // NOTE - the ClassWizard will add and remove mapping macros here. + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPythonRichEditView message handlers diff --git a/Pythonwin/pythonRichEdit.h b/Pythonwin/pythonRichEdit.h new file mode 100644 index 0000000000..152a9c01ed --- /dev/null +++ b/Pythonwin/pythonRichEdit.h @@ -0,0 +1,47 @@ +// pythonRichEdit.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CPythonRichEditView window + +class CPythonRichEditView : public CRichEditView +{ +// Construction +public: + CPythonRichEditView(); + +// Attributes +public: + +// Operations +public: + void SetDocument( CDocument *pDocument ) {m_pDocument = pDocument;} + +// Overrides + virtual void OnInitialUpdate(); + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPythonRichEditView) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CPythonRichEditView(); + + // Generated message map functions +protected: +#ifdef _DEBUG + void CPythonRichEditView::Dump( CDumpContext &dc ) const; +#endif + + //{{AFX_MSG(CPythonRichEditView) + // NOTE - the ClassWizard will add and remove member functions here. + //}}AFX_MSG + virtual BOOL OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo ); + virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam, LRESULT *pResult); +#ifdef PYWIN_WITH_WINDOWPROC + virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); +#endif + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// diff --git a/Pythonwin/pythonRichEditCntr.cpp b/Pythonwin/pythonRichEditCntr.cpp new file mode 100644 index 0000000000..33a00a52cd --- /dev/null +++ b/Pythonwin/pythonRichEditCntr.cpp @@ -0,0 +1,52 @@ +/* win32RichEditCntr : implementation file + + Created March 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32control.h" +#include "win32RichEdit.h" + +#include "pythonRichEditCntr.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPythonCntrItem implementation + +IMPLEMENT_SERIAL(CPythonCntrItem, CRichEditCntrItem, 0) + +CPythonCntrItem::CPythonCntrItem(REOBJECT *preo, CRichEditDoc* pContainer) + : CRichEditCntrItem(preo, pContainer) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// CPythonCntrItem diagnostics + +#ifdef _DEBUG +void CPythonCntrItem::AssertValid() const +{ + CRichEditCntrItem::AssertValid(); +} + +void CPythonCntrItem::Dump(CDumpContext& dc) const +{ + CRichEditCntrItem::Dump(dc); +} +#endif + +///////////////////////////////////////////////////////////////////////////// diff --git a/Pythonwin/pythonRichEditCntr.h b/Pythonwin/pythonRichEditCntr.h new file mode 100644 index 0000000000..92a3bb5d37 --- /dev/null +++ b/Pythonwin/pythonRichEditCntr.h @@ -0,0 +1,36 @@ +// PythonRichEditCntr - Python container object. + + +// PyCRichEditCntrItem +class CPythonCntrItem : public CRichEditCntrItem +{ + DECLARE_SERIAL(CPythonCntrItem) + +// Constructors +public: + CPythonCntrItem(REOBJECT* preo = NULL, CRichEditDoc* pContainer = NULL); + // Note: pContainer is allowed to be NULL to enable IMPLEMENT_SERIALIZE. + // IMPLEMENT_SERIALIZE requires the class have a constructor with + // zero arguments. Normally, OLE items are constructed with a + // non-NULL document pointer. + +// Attributes +public: + CRichEditDoc* GetDocument() + { return (CRichEditDoc*)COleClientItem::GetDocument(); } + CRichEditView* GetActiveView() + { return (CRichEditView*)COleClientItem::GetActiveView(); } + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CWordPadCntrItem) + public: + protected: + //}}AFX_VIRTUAL + +// Implementation +public: +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif +}; diff --git a/Pythonwin/pythonRichEditDoc.cpp b/Pythonwin/pythonRichEditDoc.cpp new file mode 100644 index 0000000000..de350dd0b3 --- /dev/null +++ b/Pythonwin/pythonRichEditDoc.cpp @@ -0,0 +1,24 @@ +// PythonRichEditDoc.cpp +#include "stdafx.h" +#include "pythondoc.h" +#include "pythonRichEditCntr.h" +#include "pythonRichEditDoc.h" +// @doc + +IMPLEMENT_DYNCREATE(CPythonRichEditDoc, CRichEditDoc ); + +BEGIN_MESSAGE_MAP(CPythonRichEditDoc, CRichEditDoc) + //{{AFX_MSG_MAP(CPythonDoc) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CPythonRichEditDoc::OnUpdateFileSave(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(IsModified()); +} + +CRichEditCntrItem* CPythonRichEditDoc::CreateClientItem( REOBJECT* preo ) const +{ + return new CPythonCntrItem( preo, (CPythonRichEditDoc*)this); +} diff --git a/Pythonwin/pythonRichEditDoc.h b/Pythonwin/pythonRichEditDoc.h new file mode 100644 index 0000000000..89a9bd4a03 --- /dev/null +++ b/Pythonwin/pythonRichEditDoc.h @@ -0,0 +1,12 @@ + +class CPythonRichEditDoc : public CPythonDocTemp +{ + DECLARE_DYNCREATE(CPythonRichEditDoc); + virtual CRichEditCntrItem* CreateClientItem( REOBJECT* preo = NULL ) const; +protected: + //{{AFX_MSG(CPythonRichEditDoc) + afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/Pythonwin/pythoncbar.h b/Pythonwin/pythoncbar.h new file mode 100644 index 0000000000..86f866087c --- /dev/null +++ b/Pythonwin/pythoncbar.h @@ -0,0 +1,63 @@ +// Templated control bar based classes. +// @doc + +#pragma once +template +class CPythonControlBarFramework : public CPythonWndFramework +{ +public: + // ctor hacks! + CPythonControlBarFramework() : CPythonWndFramework() {;} + // End of ctor hacks!!!! + virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz ) { + // @pyvirtual int|PyCControlBar|CalcFixedLayout|Override to augment control-bar size calculations. + // @comm The base implementation is not called if a handler exists. This can be + // done via . + // @xref + PyObject *ob; + CSize result; + CVirtualHelper helper( "CalcFixedLayout", this ); + if (helper.HaveHandler() && helper.call(bStretch, bHorz)) { + helper.retval(ob); + PyArg_ParseTuple(ob, "ii", &result.cx, &result.cy); + } else + result = T::CalcFixedLayout(bStretch, bHorz); + return result; + } + virtual CSize CalcDynamicLayout(int nLength, DWORD dwMode ) { + // @pyvirtual int|PyCControlBar|CalcDynamicLayout|Override to augment control-bar size calculations. + // @comm The base implementation is not called if a handler exists. This can be + // done via . + // @xref + PyObject *ob; + CSize result; + CVirtualHelper helper( "CalcDynamicLayout", this ); + if (helper.HaveHandler() && helper.call(nLength, dwMode)) { + helper.retval(ob); + PyArg_ParseTuple(ob, "ii", &result.cx, &result.cy); + } else + result = T::CalcDynamicLayout(nLength, dwMode); + return result; + } + virtual void OnBarStyleChange(DWORD oldStyle, DWORD newStyle) { + // @pyvirtual int|PyCControlBar|OnBarStyleChange|Override to augment control-bar size calculations. + // @comm The base implementation is not called if a handler exists. This can be + // done via . + CVirtualHelper helper( "OnBarStyleChange", this ); + if (helper.HaveHandler() && helper.call(oldStyle, newStyle)) { + ; + } else { + T::OnBarStyleChange(oldStyle, newStyle); + } + } +}; + +class CPythonControlBar : public CPythonControlBarFramework +{ + virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) { + CVirtualHelper helper( "OnUpdateCmdUI", this ); + helper.call(pTarget, bDisableIfNoHndler); + } +}; +typedef CPythonControlBarFramework CPythonToolBar; +typedef CPythonControlBarFramework CPythonStatusBar; diff --git a/Pythonwin/pythondoc.cpp b/Pythonwin/pythondoc.cpp new file mode 100644 index 0000000000..222a257cb1 --- /dev/null +++ b/Pythonwin/pythondoc.cpp @@ -0,0 +1,33 @@ +// pythondoc.cpp : implementation of the CPythonDoc class +// +// 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 "pythondoc.h" +#include "win32ui.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + + +IMPLEMENT_DYNCREATE(CPythonDoc, CDocument); + +BEGIN_MESSAGE_MAP(CPythonDoc, CDocument) + //{{AFX_MSG_MAP(CPythonDoc) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CPythonDoc::OnUpdateFileSave(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(IsModified()); +} diff --git a/Pythonwin/pythondoc.h b/Pythonwin/pythondoc.h new file mode 100644 index 0000000000..df6f2abf10 --- /dev/null +++ b/Pythonwin/pythondoc.h @@ -0,0 +1,275 @@ +// pythondoc.h : interface of the CPythonDocTemp class +// +// @doc +///////////////////////////////////////////////////////////////////////////// +#ifndef __PYTHONDOC_H__ +#define __PYTHONDOC_H__ + +//////////////////////// + +template class CPythonDocTemp : public P +{ +public: + CPythonDocTemp(); + +// Implementation +public: + virtual ~CPythonDocTemp(); + virtual void Serialize(CArchive& ar); // overridden for document i/o +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + void SetPathName( const char *pathName ); +protected: + virtual BOOL OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo ); + virtual BOOL SaveModified(); + virtual BOOL OnOpenDocument( const char *); + virtual BOOL OnSaveDocument( const char *); + virtual void OnCloseDocument(); + virtual void DeleteContents(); + virtual BOOL OnNewDocument(); + virtual BOOL DoFileSave(); + virtual BOOL DoSave(LPCTSTR lpszPathName, BOOL bReplace=TRUE); + virtual void PreCloseFrame( CFrameWnd *pWnd ); + +// Generated message map functions +protected: +public: + +}; + +template +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.
+ +

Recent Changes

+

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 0) + currentAction--; + while (actions[currentAction].at != startAction && currentAction > 0) { + if (actions[currentAction].at == insertAction) { + BasicDeleteChars(actions[currentAction].position, actions[currentAction].lenData*2); + retPosition = actions[currentAction].position; + } else if (actions[currentAction].at == removeAction) { + char *styledData = new char[actions[currentAction].lenData*2]; + memset(styledData, 0, actions[currentAction].lenData*2); + for (int i=0;i changedPosition)) + *posEarliestChanged = changedPosition; + currentAction--; + } + return retPosition; +} + +int Document::Redo(int *posEarliestChanged) { + int retPosition = 0; // Where the cursor should be after return + int changedPosition = 0; // Earliest byte modified + if (posEarliestChanged) + *posEarliestChanged = length; + if (actions[currentAction].at == startAction && currentAction < maxAction) + currentAction++; + while (actions[currentAction].at != startAction && currentAction < maxAction) { + if (actions[currentAction].at == insertAction) { + char *styledData = new char[actions[currentAction].lenData*2]; + memset(styledData, 0, actions[currentAction].lenData*2); + for (int i=0;i changedPosition)) + *posEarliestChanged = changedPosition; + currentAction++; + } + return retPosition; +} + +bool Document::CanUndo() { + //dprintf("Can Undo?\n"); + return (!readOnly) && ((currentAction > 0) && (maxAction > 0)); +} + +bool Document::CanRedo() { + return (!readOnly) && (maxAction > currentAction); +} diff --git a/Pythonwin/scintilla_src/Document.h b/Pythonwin/scintilla_src/Document.h new file mode 100644 index 0000000000..44610b87f6 --- /dev/null +++ b/Pythonwin/scintilla_src/Document.h @@ -0,0 +1,116 @@ +// Scintilla source code edit control +// Document.h - 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. + +struct LineData { + int startPosition; + int marker; + LineData() : startPosition(0), marker(0) { + } +}; + +class LineCache { +public: + enum { growSize = 4000 }; + int lines; + LineData *linesData; + int size; + + LineCache(); + ~LineCache(); + void Init(); + + void Expand(int sizeNew); + void InsertValue(int pos, int value); + void SetValue(int pos, int value); + void Remove(int pos); + int LineFromPosition(int pos); +}; + +enum actionType { insertAction, removeAction, startAction }; + +class Action { +public: + actionType at; + int position; + char *data; + int lenData; + + Action(); + ~Action(); + void Create(actionType at_, int position_=0, char *data_=0, int lenData_=0); + void Destroy(); +}; + +enum undoCollectionType { undoCollectNone, undoCollectAutoStart, undoCollectManualStart }; + +// Holder for an expandable array of characters +// Based on article "Data Structures in a Bit-Mapped Text Editor" +// by Wilfred J. Hansen, Byte January 1987, page 183 +class Document { +private: + char *body; + int size; + int length; + int part1len; + int gaplen; + char *part2body; + bool readOnly; + + void GapTo(int position); + void RoomFor(int insertionLength); + + Action *actions; + int lenActions; + int maxAction; + int currentAction; + undoCollectionType collectingUndo; + int savePoint; + + void AppendAction(actionType at, int position, char *data, int length); + + char ByteAt(int position); + void SetByteAt(int position, char ch); + +public: + + Document(int initialLength = 4000); + ~Document(); + char CharAt(int position); + char StyleAt(int position); + int ByteLength(); + int Length(); + int Lines(); + int LineStart(int line); + void InsertString(int position, char *s, int insertLength); + void InsertCharStyle(int position, char ch, char style); + void SetStyleAt(int position, char style, char mask=0xff); + void DeleteChars(int position, int deleteLength); + + bool IsReadOnly(); + void SetReadOnly(bool set); + + void SetSavePoint(); + bool IsSavePoint(); + + void SetMark(int line, int marker); + int GetMark(int line); + void DeleteAllMarks(int markerNum); + + // Without undo + void BasicInsertString(int position, char *s, int insertLength); + void BasicDeleteChars(int position, int deleteLength); + + undoCollectionType SetUndoCollection(undoCollectionType collectUndo); + bool IsCollectingUndo(); + void AppendUndoStartAction(); + void DeleteUndoHistory(); + int Undo(int *posEarliestChanged=0); + int Redo(int *posEarliestChanged=0); + bool CanUndo(); + bool CanRedo(); + + LineCache lc; +}; + diff --git a/Pythonwin/scintilla_src/KeyWords.cc b/Pythonwin/scintilla_src/KeyWords.cc new file mode 100644 index 0000000000..0229903550 --- /dev/null +++ b/Pythonwin/scintilla_src/KeyWords.cc @@ -0,0 +1,1273 @@ +// SciTE - Scintilla based Text Editor +// KeyWords.cc +// Colourise for particular languages + +#include +#include +#include + +#ifdef GTK +#include +#define LRESULT long +#define UINT unsigned int +#define WPARAM long +#define LPARAM long +#include "WinDefs.h" +#define stricmp strcasecmp +LRESULT SendMessage(HWND w,UINT msg, WPARAM wParam=0, LPARAM lParam=0); +#else +#include +#endif + +#include "KeyWords.h" +#include "Scintilla.h" + +static void dprintf(char *szFormat, ...) { +#ifdef TRACE + char szBuffer[1000]; + char *pArguments = (char *) & szFormat + sizeof(szFormat); + vsprintf(szBuffer,szFormat,pArguments); + printf("%s",szBuffer); +#endif +} + +bool wordInList(char *word, char **list) { + if (0 == list) + return false; + for (int i=0; list[i][0]; i++) { + // Initial test is to mostly avoid slow function call + if ((list[i][0] == word[0]) && (0 == strcmp(list[i], word))) + return true; + } + return false; +} + +bool wordInListInsensitive(char *word, char **list) { + if (0 == list) + return false; + for (int i=0; list[i][0]; i++) { + // Initial test is to mostly avoid slow function call + if ((toupper(list[i][0]) == toupper(word[0])) && (0 == stricmp(list[i], word))) + return true; + } + return false; +} + +void colourSegHwnd(HWND hwnd, unsigned int start, unsigned int end,char chAttr) { + // Only perform styling if non empty range + if (end != start - 1) { + if (end < start) { + dprintf("Bad colour positions %d - %d\n", start, end); + } + //dprintf("Colour %d %0d-%0d\n", chAttr, start, end); + SendMessage(hwnd, SCI_SETSTYLING, end - start + 1, chAttr); + } +} + +void classifyWord(char *cdoc, unsigned int start, unsigned int end, const char *language, char **keywords, HWND hwnd) { + char s[100]; + bool wordIsNumber = isdigit(cdoc[start]) || (cdoc[start] == '.'); + for (int i=0; i' || ch == ',' || ch == '/' || + ch == '?' || ch == '!' || ch == '.' || ch == '~') + return true; + return false; +} + +enum eState { + eDefault = 0, + eComment = 1, + eLineComment = 2, + eDocComment = 3, + eNumber = 4, + eWord = 5, + eString = 6, + eChar = 7, + ePunct = 8, + ePreProc = 9, + eOperator = 10, + eIdentifier = 11, +}; + +void classifyWordCpp(char *cdoc, unsigned int start, unsigned int end, char **keywords, HWND hwnd) { + char s[100]; + bool wordIsNumber = isdigit(cdoc[start]) || (cdoc[start] == '.'); + for (int i=0; i(initStyle); + char chPrev = ' '; + char chNext = cdoc[0]; + int startSeg = 0; + for (int i=0;i<=lengthDoc;i++) { + eState statePrev = state; + char ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + + if (state == eDefault) { + if (iswordstart(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eWord; + startSeg = i; + } else if (ch == '/' && chNext == '*') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eComment; + startSeg = i; + } else if (ch == '/' && chNext == '/') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eLineComment; + startSeg = i; + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eString; + startSeg = i; + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eChar; + startSeg = i; + } else if (ch == '#') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = ePreProc; + startSeg = i; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + colourSegHwnd(hwnd, i, i, eOperator); + startSeg = i+1; + } + } else if (state == eWord) { + if (!iswordchar(ch)) { + classifyWordCpp(cdoc, startSeg, i - 1, keywords, hwnd); + state = eDefault; + startSeg = i; + if (ch == '/' && chNext == '*') { + state = eComment; + } else if (ch == '/' && chNext == '/') { + state = eLineComment; + } else if (ch == '\"') { + state = eString; + } else if (ch == '\'') { + state = eChar; + } else if (ch == '#') { + state = ePreProc; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i, eOperator); + state = eDefault; + startSeg = i + 1; + } + } + } else { + if (state == ePreProc) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + state = eDefault; + colourSegHwnd(hwnd, startSeg, i-1, ePreProc); + startSeg = i; + } + } else if (state == eComment) { + if (ch == '/' && chPrev == '*' && ((i > startSeg + 2) || ((initStyle == eComment) && (startSeg == 0)))) { + state = eDefault; + colourSegHwnd(hwnd, startSeg, i, eComment); + startSeg = i + 1; + } + } else if (state == eLineComment) { + if (ch == '\r' || ch == '\n') { + colourSegHwnd(hwnd, startSeg, i-1, eLineComment); + state = eDefault; + startSeg = i; + } + } else if (state == eString) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i, eString); + state = eDefault; + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + startSeg = i; + } + } else if (state == eChar) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i, eChar); + state = eDefault; + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + startSeg = i; + } + } + if (state == eDefault) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + state = eComment; + } else if (ch == '/' && chNext == '/') { + state = eLineComment; + } else if (ch == '\"') { + state = eString; + } else if (ch == '\'') { + state = eChar; + } else if (ch == '#') { + state = ePreProc; + } else if (iswordstart(ch)) { + state = eWord; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i, eOperator); + startSeg = i + 1; + } + } + } + chPrev = ch; + } + if (startSeg < lengthDoc) + colourSegHwnd(hwnd, startSeg, lengthDoc-1, state); +} + +static void ColouriseJavaDoc(char *cdoc, int lengthDoc, int initStyle, const char *language, char **keywords, HWND hwnd) { + eState state = static_cast(initStyle); + char chPrev = ' '; + char chNext = cdoc[0]; + char chNext2 = cdoc[0]; + int startSeg = 0; + for (int i=0;i<=lengthDoc;i++) { + eState statePrev = state; + char ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + if (i+2 < lengthDoc) + chNext2 = cdoc[i+2]; + + if (state == eDefault) { + if (iswordstart(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eWord; + startSeg = i; + } else if (ch == '/' && chNext == '*') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + if (chNext2 == '*') + state = eDocComment; + else + state = eComment; + startSeg = i; + } else if (ch == '/' && chNext == '/') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eLineComment; + startSeg = i; + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eString; + startSeg = i; + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + state = eChar; + startSeg = i; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, eDefault); + colourSegHwnd(hwnd, i, i, eOperator); + startSeg = i+1; + } + } else if (state == eWord) { + if (!iswordchar(ch)) { + classifyWord(cdoc, startSeg, i - 1, language, keywords, hwnd); + state = eDefault; + startSeg = i; + if (ch == '/' && chNext == '*') { + if (chNext2 == '*') + state = eDocComment; + else + state = eComment; + } else if (ch == '/' && chNext == '/') { + state = eLineComment; + } else if (ch == '\"') { + state = eString; + } else if (ch == '\'') { + state = eChar; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i, eOperator); + state = eDefault; + startSeg = i + 1; + } + } + } else { + if (state == eComment) { + if (ch == '/' && chPrev == '*' && ((i > startSeg + 2) || ((initStyle == eComment) && (startSeg == 0)))) { + state = eDefault; + colourSegHwnd(hwnd, startSeg, i, eComment); + startSeg = i + 1; + } + } else if (state == eDocComment) { + if (ch == '/' && chPrev == '*' && ((i > startSeg + 2) || ((initStyle == eComment) && (startSeg == 0)))) { + state = eDefault; + colourSegHwnd(hwnd, startSeg, i, eDocComment); + startSeg = i + 1; + } + } else if (state == eLineComment) { + if (ch == '\r' || ch == '\n') { + colourSegHwnd(hwnd, startSeg, i-1, eLineComment); + state = eDefault; + startSeg = i; + } + } else if (state == eString) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i, 7); + state = eDefault; + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + startSeg = i; + } + } else if (state == eChar) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i, 7); + state = eDefault; + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + startSeg = i; + } + } + if (state == eDefault) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + if (chNext2 == '*') + state = eDocComment; + else + state = eComment; + } else if (ch == '/' && chNext == '/') { + state = eLineComment; + } else if (ch == '\"') { + state = eString; + } else if (ch == '\'') { + state = eChar; + } else if (iswordstart(ch)) { + state = eWord; + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i, eOperator); + startSeg = i + 1; + } + } + } + chPrev = ch; + } + if (startSeg < lengthDoc) + colourSegHwnd(hwnd, startSeg, lengthDoc, state); +} + +void classifyWordVB(char *cdoc, unsigned int start, unsigned int end, char **keywords, HWND hwnd) { + char s[100]; + bool wordIsNumber = isdigit(cdoc[start]) || (cdoc[start] == '.'); + for (int i=0; i(initStyle); + char chPrev = ' '; + char chNext = cdoc[0]; + char chNext2 = cdoc[0]; + int startSeg = 0; + for (int i=0;i(initStyle); + char chPrev = ' '; + char chPrev2 = ' '; + char chNext = cdoc[0]; + char chNext2 = cdoc[0]; + int startSeg = 0; + for (int i=0;i<=lengthDoc;i++) { + char ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + chNext2 = ' '; + if (i+2 < lengthDoc) + chNext2 = cdoc[i+2]; + + if (state == ePyDefault) { + if (iswordstart(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + state = ePyWord; + startSeg = i; + } else if (ch == '#') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + state = ePyComment; + startSeg = i; + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + startSeg = i; + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = eTripleDouble; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyString; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + startSeg = i; + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = eTriple; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyChar; + } + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + colourSegHwnd(hwnd, i, i, ePyOperator); + startSeg = i+1; + } + } else if (state == ePyWord) { + if (!iswordchar(ch)) { + classifyWordPy(cdoc, startSeg, i - 1, keywords, hwnd, prevWord); + state = ePyDefault; + startSeg = i; + if (ch == '#') { + state = ePyComment; + } else if (ch == '\"') { + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = eTripleDouble; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyString; + } + } else if (ch == '\'') { + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = eTriple; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyChar; + } + } else if (isoperator(ch)) { + colourSegHwnd(hwnd, startSeg, i, ePyOperator); + state = ePyDefault; + startSeg = i + 1; + } + } + } else { + if (state == ePyComment) { + if (ch == '\r' || ch == '\n') { + colourSegHwnd(hwnd, startSeg, i-1, ePyComment); + state = ePyDefault; + startSeg = i; + } + } else if (state == ePyString) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i, ePyString); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == ePyChar) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i, ePyChar); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == eTriple) { + if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { + colourSegHwnd(hwnd, startSeg, i, eTriple); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == eTripleDouble) { + if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { + colourSegHwnd(hwnd, startSeg, i, eTripleDouble); + state = ePyDefault; + startSeg = i+1; + } + } + } + chPrev2 = chPrev; + chPrev = ch; + } + if (startSeg < lengthDoc) { + if (state == ePyDefault) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyDefault); + } else if (state == ePyWord) { + classifyWordPy(cdoc, startSeg, lengthDoc, keywords, hwnd, prevWord); + } else if (state == ePyComment) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyComment); + } else if (state == ePyString) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyString); + } else if (state == ePyChar) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyChar); + } else if (state == eTriple) { + colourSegHwnd(hwnd, startSeg, lengthDoc, eTriple); + } else if (state == eTripleDouble) { + colourSegHwnd(hwnd, startSeg, lengthDoc, eTripleDouble); + } + } +} + +void classifyWordPyro(char *cdoc, unsigned int start, unsigned int end, char **keywords, HWND hwnd, char *prevWord) { + char s[100]; + bool wordIsNumber = isdigit(cdoc[start]); + for (int i=0; i(initStyle & 31); + char chPrev = ' '; + char chPrev2 = ' '; + char chNext = cdoc[0]; + char chNext2 = cdoc[0]; + int startSeg = 0; + for (int i=0;i<=lengthDoc;i++) { + char ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + chNext2 = ' '; + if (i+2 < lengthDoc) + chNext2 = cdoc[i+2]; + + if (state == ePyDefault) { + if (iswordstart(ch)) { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + state = ePyWord; + startSeg = i; + } else if (ch == '#') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + state = ePyComment; + startSeg = i; + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + startSeg = i; + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = eTripleDouble; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyString; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i - 1, ePyDefault); + startSeg = i; + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = eTriple; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyChar; + } + } + } else if (state == ePyWord) { + if (!iswordchar(ch)) { + classifyWordPyro(cdoc, startSeg, i - 1, keywords, hwnd, prevWord); + state = ePyDefault; + startSeg = i; + if (ch == '#') { + state = ePyComment; + } else if (ch == '\"') { + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = eTripleDouble; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyString; + } + } else if (ch == '\'') { + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = eTriple; + ch = ' '; + chPrev = ' '; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } else { + state = ePyChar; + } + } + } + } else { + if (state == ePyComment) { + if (ch == '\r' || ch == '\n') { + colourSegHwnd(hwnd, startSeg, i-1, ePyComment); + state = ePyDefault; + startSeg = i; + } + } else if (state == ePyString) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\"') { + colourSegHwnd(hwnd, startSeg, i, ePyString); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == ePyChar) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = ' '; + if (i+1 < lengthDoc) + chNext = cdoc[i+1]; + } + } else if (ch == '\'') { + colourSegHwnd(hwnd, startSeg, i, ePyChar); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == eTriple) { + if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { + colourSegHwnd(hwnd, startSeg, i, eTriple); + state = ePyDefault; + startSeg = i+1; + } + } else if (state == eTripleDouble) { + if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { + colourSegHwnd(hwnd, startSeg, i, eTripleDouble); + state = ePyDefault; + startSeg = i+1; + } + } + } + chPrev2 = chPrev; + chPrev = ch; + } + if (startSeg < lengthDoc) { + if (state == ePyDefault) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyDefault); + } else if (state == ePyWord) { + classifyWordPyro(cdoc, startSeg, lengthDoc, keywords, hwnd, prevWord); + } else if (state == ePyComment) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyComment); + } else if (state == ePyString) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyString); + } else if (state == ePyChar) { + colourSegHwnd(hwnd, startSeg, lengthDoc, ePyChar); + } else if (state == eTriple) { + colourSegHwnd(hwnd, startSeg, lengthDoc, eTriple); + } else if (state == eTripleDouble) { + colourSegHwnd(hwnd, startSeg, lengthDoc, eTripleDouble); + } + } +} + +static void ColouriseBatchLine(char *lineBuffer, int lengthLine, HWND hwnd) { + if (0 == strncmp(lineBuffer, "REM", 3)) { + colourSegHwnd(hwnd, 0, lengthLine-1, 1); + } else if (0 == strncmp(lineBuffer, "rem", 3)) { + colourSegHwnd(hwnd, 0, lengthLine-1, 1); + } else if (0 == strncmp(lineBuffer, "SET", 3)) { + colourSegHwnd(hwnd, 0, lengthLine-1, 2); + } else if (0 == strncmp(lineBuffer, "set", 3)) { + colourSegHwnd(hwnd, 0, lengthLine-1, 2); + } else if (lineBuffer[0] == ':') { + colourSegHwnd(hwnd, 0, lengthLine-1, 3); + } else { + colourSegHwnd(hwnd, 0, lengthLine-1, 0); + } +} + +static void ColouriseBatchDoc(char *cdoc, int lengthDoc, int initStyle, HWND hwnd) { + char lineBuffer[1024]; + int linePos = 0; + for (int i=0;i= sizeof(lineBuffer) - 1)) { + ColouriseBatchLine(lineBuffer, linePos, hwnd); + linePos = 0; + } + } + if (linePos > 0) + ColouriseBatchLine(lineBuffer, linePos, hwnd); +} + +static void ColourisePropsLine(char *lineBuffer, int lengthLine, HWND hwnd) { + int i=0; + while (isspace(lineBuffer[i]) && (i < lengthLine)) + i++; + if (lineBuffer[i] == '#' || lineBuffer[i] == '!') { + colourSegHwnd(hwnd, 0, lengthLine-1, 1); + } else { + colourSegHwnd(hwnd, 0, lengthLine-1, 0); + } +} + +static void ColourisePropsDoc(char *cdoc, int lengthDoc, int initStyle, HWND hwnd) { + char lineBuffer[1024]; + int linePos = 0; + for (int i=0;i<=lengthDoc;i++) { + lineBuffer[linePos++] = cdoc[i]; + if (cdoc[i] == '\r' || cdoc[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { + ColourisePropsLine(lineBuffer, linePos, hwnd); + linePos = 0; + } + } + if (linePos > 0) + ColourisePropsLine(lineBuffer, linePos, hwnd); +} + +static void ColouriseErrorListLine(char *lineBuffer, int lengthLine, HWND hwnd) { + if (lineBuffer[0] == '>') { + // Command or return status + colourSegHwnd(hwnd, 0, lengthLine-1, 4); + } else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) { + colourSegHwnd(hwnd, 0, lengthLine-1, 1); + } else { + // Look for ::message + int state = 0; + for (int i=0;i= sizeof(lineBuffer) - 1)) { + ColouriseErrorListLine(lineBuffer, linePos, hwnd); + linePos = 0; + } + } + if (linePos > 0) + ColouriseErrorListLine(lineBuffer, linePos, hwnd); +} + +void ColouriseDoc(char *cdoc, int startPos, int lengthDoc, int initStyle, const char *language, char **keywords, HWND hwnd) { + //dprintf("ColouriseDoc <%s>\n", language); + SendMessage(hwnd, SCI_STARTSTYLING, startPos, 31); + if (0 == strcmp(language, "python")) { + ColourisePyDoc(cdoc, lengthDoc, initStyle, keywords, hwnd); + } else if (0 == strcmp(language, "pyro")) { + ColourisePyroDoc(cdoc, lengthDoc, initStyle, keywords, hwnd); + } else if (0 == strcmp(language, "batch")) { + ColouriseBatchDoc(cdoc, lengthDoc, initStyle, hwnd); + } else if (0 == strcmp(language, "java")) { + ColouriseJavaDoc(cdoc, lengthDoc, initStyle, language, keywords, hwnd); + } else if (0 == strcmp(language, "javascript")) { + ColouriseJavaDoc(cdoc, lengthDoc, initStyle, language, keywords, hwnd); + } else if (0 == strcmp(language, "vb")) { + ColouriseVBDoc(cdoc, lengthDoc, initStyle, language, keywords, hwnd); + } else if (0 == strcmp(language, "cpp")) { + ColouriseCppDoc(cdoc, lengthDoc, initStyle, keywords, hwnd); + } else if (0 == strcmp(language, "props")) { + ColourisePropsDoc(cdoc, lengthDoc, initStyle, hwnd); + } else if (0 == strcmp(language, "errorlist")) { + ColouriseErrorListDoc(cdoc, lengthDoc, initStyle, hwnd); + } else { + colourSegHwnd(hwnd, 0, lengthDoc, 0); + } +} + +static bool tabsCheck(int line, int tabsPrev[], int tabsThis[]) { + dprintf("%d %0d:%0d %0d:%0d %0d:%0d %0d:%0d\n", line, + tabsPrev[0], tabsThis[0], + tabsPrev[1], tabsThis[1], + tabsPrev[3], tabsThis[3], + tabsPrev[7], tabsThis[7]); + int tabDir = 0; + if (tabsPrev[0] < tabsThis[0]) + tabDir = 1; + if (tabsPrev[0] > tabsThis[0]) + tabDir = -1; + tabsPrev[0] = tabsThis[0]; + tabsThis[0] = 1; + bool tabsSame = true; + for (int it=1;it<8;it++) { + int tabDirIt = 0; + if (tabsPrev[it] < tabsThis[it]) + tabDirIt = 1; + if (tabsPrev[it] > tabsThis[it]) + tabDirIt = -1; + if (tabDir != tabDirIt) + tabsSame = false; + tabsPrev[it] = tabsThis[it]; + tabsThis[it] = 1; + } + return tabsSame; +} + +void TabTimmy(char *cdoc, int lengthDoc, HWND hwnd) { + int tabsPrev[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + int tabsThis[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + SendMessage(hwnd, SCI_STARTSTYLING, 0, 64); + bool inIndent = true; + int line = 0; + if (lengthDoc == 0) + return; + ePyState state = static_cast(0); + char chPrev = ' '; + char chPrev2 = ' '; + char chNext = cdoc[0]; + char chNext2 = cdoc[0]; + int startSeg = 0; + for (int i=0;i + +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation. + +NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS, IN NO EVENT SHALL NEIL HODGSON BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/Pythonwin/scintilla_src/PlatGTK.cc b/Pythonwin/scintilla_src/PlatGTK.cc new file mode 100644 index 0000000000..797fb69f85 --- /dev/null +++ b/Pythonwin/scintilla_src/PlatGTK.cc @@ -0,0 +1,123 @@ +// Scintilla source code edit control +// PlatfGDK.cc - implementation of platform facilities on GTK+/Linux +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include + +#include "Platform.h" + +Surface::Surface() { + hwnd = 0; + drawable = 0; + gc = 0; + x = 0; + y = 0; +} + +Surface::~Surface() { + //gdk_gc_unref(gc); + gc = 0; +} + +void Surface::Init(GtkWidget *hwnd_, GdkDrawable *drawable_, GdkGC *gc_) { + hwnd = hwnd_; + drawable = drawable_; + //if (gc_) + gc = gc_; + //else + // gc = gdk_gc_new(hwnd->window); +} + +void Surface::PenColor(COLORREF fore) { + gdk_gc_set_foreground(gc, &fore); +} + +void Surface::MoveTo(int x_, int y_) { + x = x_; + y = y_; +} + +void Surface::LineTo(int x_, int y_) { + gdk_draw_line(drawable, gc, + x, y, + x_, y_); + x = x_; + y = y_; +} + +void Surface::Polygon(POINT *pts, int npts, COLORREF fore, + COLORREF back) { + PenColor(back); + gdk_draw_polygon(drawable, gc, 1, pts, npts); + PenColor(fore); + gdk_draw_polygon(drawable, gc, 0, pts, npts); +} + +void Surface::Rectangle(RECT rc, COLORREF fore, COLORREF back) { + PenColor(back); + gdk_draw_rectangle(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); + PenColor(fore); + gdk_draw_rectangle(drawable, gc, 0, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); +} + +void Surface::FillRectangle(RECT rc, COLORREF back) { + // GTK+ rectangles include their lower and right edges + rc.bottom--; + rc.right--; + PenColor(back); + gdk_draw_rectangle(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); +} + +void Surface::RoundedRectangle(RECT rc, COLORREF fore, COLORREF back) { + if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) { + // Approximate a round rect with some cut off corners + POINT pts[] = { + {rc.left + 2, rc.top}, + {rc.right - 2, rc.top}, + {rc.right, rc.top + 2}, + {rc.right, rc.bottom - 2}, + {rc.right - 2, rc.bottom}, + {rc.left + 2, rc.bottom}, + {rc.left, rc.bottom - 2}, + {rc.left, rc.top + 2}, + }; + Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back); + } else { + Rectangle(rc, fore, back); + } +} + +void Surface::Ellipse(RECT rc, COLORREF fore, COLORREF back) { + PenColor(back); + gdk_draw_arc(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + 0, 32767); + PenColor(fore); + gdk_draw_arc(drawable, gc, 0, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + 0, 32767); +} + +void Surface::DrawText(RECT rc, HFONT font, int ybase, char *s, int len, COLORREF fore, COLORREF back) { + FillRectangle(rc, back); + PenColor(fore); + gdk_draw_text(drawable, font, gc, rc.left, ybase, s, len); +} + +int Surface::WidthText(HFONT font, char *s, int len) { + return gdk_text_width(font, s, len); +} + +int Surface::WidthChar(HFONT font, char ch) { + return gdk_char_width(font, ch); +} + diff --git a/Pythonwin/scintilla_src/PlatWin.cc b/Pythonwin/scintilla_src/PlatWin.cc new file mode 100644 index 0000000000..1ca6553699 --- /dev/null +++ b/Pythonwin/scintilla_src/PlatWin.cc @@ -0,0 +1,167 @@ +// Scintilla source code edit control +// PlatfGDK.cc - implementation of platform facilities on GTK+/Linux +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include + +#include "Platform.h" + +Surface::Surface() { + hdc = 0; + pen = 0; + penOld = 0; + brush = 0; + brush; + brushOld = 0; + font = 0; + fontOld = 0; + hwnd = 0; +} + +Surface::~Surface() { + if (pen) { + SelectObject(hdc, penOld); + DeleteObject(pen); + pen = 0; + penOld = 0; + } + if (brush) { + SelectObject(hdc, brushOld); + DeleteObject(brush); + brush = 0; + brushOld = 0; + } + if (fontOld) { + SelectObject(hdc, fontOld); + fontOld = 0; + } + if (hwnd) { + // Surface allocated DC on window so must release it + ReleaseDC(hwnd,hdc); + hdc = 0; + hwnd = 0; + } +} + +void Surface::Init(HDC hdc_) { + hdc = hdc_; + pen = 0; + penOld = 0; + brush = 0; + brushOld = 0; + font = 0; + fontOld = 0; + hwnd = 0; +} + +void Surface::InitOnWindow(HWND hwnd_) { + pen = 0; + penOld = 0; + brush = 0; + brushOld = 0; + font = 0; + fontOld = 0; + hwnd = hwnd_; + hdc = GetDC(hwnd); +} + +void Surface::PenColor(COLORREF fore) { + if (pen) { + SelectObject(hdc, penOld); + DeleteObject(pen); + pen = 0; + penOld = 0; + } + pen = CreatePen(0,1,fore); + penOld = static_cast(SelectObject(hdc, pen)); +} + +void Surface::BrushColor(COLORREF back) { + if (brush) { + SelectObject(hdc, brushOld); + DeleteObject(brush); + brush = 0; + brushOld = 0; + } + // Only ever want pure, non-dithered brushes + COLORREF colourNearest = GetNearestColor(hdc, back); + brush = CreateSolidBrush(colourNearest); + brushOld = static_cast(SelectObject(hdc, brush)); +} + +void Surface::SetFont(HFONT font_) { + if (font_ != font) { + if (fontOld) { + SelectObject(hdc, fontOld); + fontOld = 0; + } + fontOld = static_cast(SelectObject(hdc, font_)); + font = font_; + } +} + +void Surface::MoveTo(int x_, int y_) { + MoveToEx(hdc, x_, y_, 0); +} + +void Surface::LineTo(int x_, int y_) { + ::LineTo(hdc, x_, y_); +} + +void Surface::Polygon(POINT *pts, int npts, COLORREF fore, + COLORREF back) { + PenColor(fore); + BrushColor(back); + ::Polygon(hdc, pts, npts); +} + +void Surface::Rectangle(RECT rc, COLORREF fore, COLORREF back) { + PenColor(fore); + BrushColor(back); + ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); +} + +void Surface::FillRectangle(RECT rc, COLORREF back) { + // Using ExtTextOut rather than a FillRect ensures that no dithering occurs. + // There is no need to allocate a brush either. + SetBkColor(hdc, back); + ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, "", 0, NULL); +} + +void Surface::RoundedRectangle(RECT rc, COLORREF fore, COLORREF back) { + PenColor(fore); + BrushColor(back); + RoundRect(hdc, + rc.left + 1, rc.top, + rc.right - 1, rc.bottom, + 8, 8 ); +} + +void Surface::Ellipse(RECT rc, COLORREF fore, COLORREF back) { + PenColor(fore); + BrushColor(back); + ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); +} + +void Surface::DrawText(RECT rc, HFONT font_, int ybase, char *s, int len, COLORREF fore, COLORREF back) { + SetFont(font_); + SetTextColor(hdc, fore); + SetBkColor(hdc, back); + ExtTextOut(hdc, rc.left, ybase, ETO_OPAQUE, &rc, s, len, NULL); +} + +int Surface::WidthText(HFONT font_, char *s, int len) { + SetFont(font_); + SIZE sz; + GetTextExtentPoint32(hdc, s, len, &sz); + return sz.cx; +} + +int Surface::WidthChar(HFONT font_, char ch) { + SetFont(font_); + SIZE sz; + GetTextExtentPoint32(hdc, &ch, 1, &sz); + return sz.cx; +} + diff --git a/Pythonwin/scintilla_src/Platform.h b/Pythonwin/scintilla_src/Platform.h new file mode 100644 index 0000000000..ae12829e38 --- /dev/null +++ b/Pythonwin/scintilla_src/Platform.h @@ -0,0 +1,75 @@ +// Scintilla source code edit control +// Platform.h - interface to platform facilities +// Implemented in PlatGTK.cc for GTK+/Linux and PlatWin.cc for Windows +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifdef GTK +#define PLAT_GTK 1 +#else +#define PLAT_WIN 1 +#endif + +#ifdef GTK + +#define COLORREF GdkColor +#define POINT GdkPoint +#define HFONT GdkFont* +#define HWND GtkWidget* + +struct RECT { + int left; + int top; + int right; + int bottom; +}; + +#endif + +// A surface abstracts a place to draw +class Surface { +public: +#ifdef GTK + GtkWidget *hwnd; + GdkDrawable *drawable; + GdkGC *gc; + int x; + int y; +#else + HDC hdc; + HPEN pen; + HPEN penOld; + HBRUSH brush; + HBRUSH brushOld; + HFONT font; + HFONT fontOld; + HWND hwnd; +#endif + +public: +#ifdef GTK + void Init(GtkWidget *hwnd_=0, GdkDrawable *drawable_=0, GdkGC *gc_=0); +#else + void Init(HDC hdc_); + void InitOnWindow(HWND hwnd_); +#endif + + Surface(); + ~Surface(); + void PenColor(COLORREF fore); +#ifndef GTK + void BrushColor(COLORREF back); + void SetFont(HFONT font_); +#endif + void MoveTo(int x_, int y_); + void LineTo(int x_, int y_); + void Polygon(POINT *pts, int npts, COLORREF fore, COLORREF back); + void Rectangle(RECT rc, COLORREF fore, COLORREF back); + void FillRectangle(RECT rc, COLORREF back); + void RoundedRectangle(RECT rc, COLORREF fore, COLORREF back); + void Ellipse(RECT rc, COLORREF fore, COLORREF back); + + void DrawText(RECT rc, HFONT font_, int ybase, char *s, int len, COLORREF fore, COLORREF back); + int WidthText(HFONT font_, char *s, int len); + int WidthChar(HFONT font_, char ch); +}; diff --git a/Pythonwin/scintilla_src/PropSet.cc b/Pythonwin/scintilla_src/PropSet.cc new file mode 100644 index 0000000000..13688c57d6 --- /dev/null +++ b/Pythonwin/scintilla_src/PropSet.cc @@ -0,0 +1,223 @@ +// SciTE - Scintilla based Text Editor +// PropSet.cc - a java style properties file module +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +// Maintain a dictionary of properties + +#ifndef GTK +#include +#endif + +#include +#include +#include +#include + +#include "PropSet.h" +#ifdef GTK +#include +#define stricmp strcasecmp +#endif + +// Get a line of input. If end of line escaped with '\\' then continue reading. +static bool GetFullLine(FILE *fp, char *s, int len) { + while (len > 0) { + char *cp = fgets(s, len, fp); + if (!cp) + return false; + int last = strlen(s); + // Remove probable trailing line terminator characters + if ((last > 0) && ((s[last-1] == '\n') || (s[last-1] == '\r'))) { + s[last-1] = '\0'; + last--; + } + if ((last > 0) && ((s[last-1] == '\n') || (s[last-1] == '\r'))) { + s[last-1] = '\0'; + last--; + } + if (last == 0) // Empty line so do not need to check for trailing '\\' + return true; + if (s[last-1] != '\\') + return true; + // Trailing '\\' so read another line + s[last-1] = '\0'; + last--; + s += last; + len -= last; + } + return false; +} + +PropSet::PropSet() { + superPS = 0; + size = 10; + used = 0; + vals = new char*[size]; +} + +PropSet::~PropSet() { + superPS = 0; + Clear(); + delete vals; +} + +void PropSet::EnsureCanAddEntry() { + if (used >= size - 2) { + int newsize = size + 10; + char **newvals = new char*[newsize]; + + for (int i=0;iGet(key); + } else { + return ""; + } +} + +int PropSet::GetInt(const char *key) { + char *val = Get(key); + if (*val) + return atoi(val); + else + return 0; +} + +bool isprefix(const char *target, const char *prefix) { + while (*target && *prefix) { + if (toupper(*target) != toupper(*prefix)) + return false; + target++; + prefix++; + } + if (*prefix) + return false; + else + return true; +} + +bool issuffix(const char *target, const char *suffix) { + int lentarget = strlen(target); + int lensuffix = strlen(suffix); + if (lensuffix > lentarget) + return false; + for (int i=lensuffix-1; i >= 0; i--) { + if (toupper(target[i + lentarget - lensuffix]) != toupper(suffix[i])) + return false; + } + return true; +} + +char *PropSet::GetWild(const char *keybase, const char *filename) { + int lenbase = strlen(keybase); + int lenfile = strlen(filename); + for (int i=0;iGetWild(keybase, filename); + } else { + return ""; + } +} + +char *PropSet::GetNewExpand(const char *keybase, const char *filename) { + char *base = strdup(GetWild(keybase, filename)); + char *cpvar = strstr(base, "$("); + while (cpvar) { + char *cpendvar = strchr(cpvar, ')'); + if (cpendvar) { + int lenvar = cpendvar - cpvar - 2; // Subtract the $() + char *var = static_cast(malloc(lenvar + 1)); + strncpy(var, cpvar+2, lenvar); + var[lenvar] = '\0'; + char *val = GetWild(var, filename); + int newlenbase = strlen(base) + strlen(val) - lenvar; + char *newbase = static_cast(malloc(newlenbase)); + strncpy(newbase, base, cpvar - base); + strcpy(newbase + (cpvar - base), val); + strcpy(newbase + (cpvar - base) + strlen(val), cpendvar + 1); + free(var); + free(base); + base = newbase; + } + cpvar = strstr(base, "$("); + } + return base; +} + +void PropSet::Clear() { + for (int i=0;i\n", filename); + Clear(); + FILE *rcfile = fopen(filename, "rt"); + if (rcfile) { + char linebuf[4000]; + while (GetFullLine(rcfile, linebuf, sizeof(linebuf))) { + if (isalpha(linebuf[0])) + Set(linebuf); + } + fclose(rcfile); + } else { + //printf("Could not open <%s>\n", filename); + } +} + diff --git a/Pythonwin/scintilla_src/PropSet.h b/Pythonwin/scintilla_src/PropSet.h new file mode 100644 index 0000000000..67977c6fe3 --- /dev/null +++ b/Pythonwin/scintilla_src/PropSet.h @@ -0,0 +1,25 @@ +// SciTE - Scintilla based Text Editor +// PropSet.h - a java style properties file module +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +class PropSet { +private: + char **vals; + int size; + int used; +public: + PropSet *superPS; + PropSet(); + ~PropSet(); + void EnsureCanAddEntry(); + void Set(const char *key, const char *val); + void Set(char *keyval); + char *Get(const char *key); + int GetInt(const char *key); + char *GetWild(const char *keybase, const char *filename); + char *GetNewExpand(const char *keybase, const char *filename); + void Clear(); + void Read(const char *filename); +}; + diff --git a/Pythonwin/scintilla_src/ScintRes.rc b/Pythonwin/scintilla_src/ScintRes.rc new file mode 100644 index 0000000000..ec8cb73e79 --- /dev/null +++ b/Pythonwin/scintilla_src/ScintRes.rc @@ -0,0 +1,35 @@ +// Resource file for Scintilla +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1, 0, 0, 0 +PRODUCTVERSION 1, 0, 0, 0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_DOS_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4\0" + BEGIN + VALUE "CompanyName", "Neil Hodgson\0" + VALUE "FileDescription", "Scintilla.DLL - a Source Editing Component\0" + VALUE "FileVersion", "1.0\0" + VALUE "InternalName", "Scintilla\0" + VALUE "LegalCopyright", "Copyright 1998-1999 by Neil Hodgson \0" + VALUE "OriginalFilename", "Scintilla.DLL\0" + VALUE "ProductName", "Scintilla\0" + VALUE "ProductVersion", "1.0\0" + END + END +END + diff --git a/Pythonwin/scintilla_src/Scintilla.cc b/Pythonwin/scintilla_src/Scintilla.cc new file mode 100644 index 0000000000..d699ffbcc5 --- /dev/null +++ b/Pythonwin/scintilla_src/Scintilla.cc @@ -0,0 +1,5111 @@ +// Scintilla source code edit control +// Scintilla.cc - main code for the edit control +// 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 + +#ifdef GTK +#include +#include "gtk/gtksignal.h" +#include "gtk/gtktable.h" +#include "gtk/gtktogglebutton.h" +#include "gdk/gdkkeysyms.h" +#else +#ifdef _MSC_VER +#include +#include +#else +extern "C" LONG STDCALL timeGetTime(); +#endif +#endif + +#include "Scintilla.h" +#include "Document.h" +#include "Platform.h" +#ifndef GTK +#include "CallTip.h" +#endif + +enum { + COMMAND_SIGNAL, + NOTIFY_SIGNAL, + LAST_SIGNAL +}; + +#ifdef GTK +char defaultFont[] = "lucidatypewriter"; +const int defaultSize = 12; +#else +char defaultFont[] = "Verdana"; +const int defaultSize = 8; +#endif + +#ifdef GTK + +static GdkAtom clipboard_atom = GDK_NONE; + +#define LOWORD(x) (x & 0xffff) +#define HIWORD(x) (x >> 16) + +enum { + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT +}; + +static COLORREF RGB(unsigned int red, unsigned int green, unsigned int blue) { + GdkColor ret; + ret.red = red * (65535/255); + ret.green = green * (65535/255); + ret.blue = blue * (65535/255); + // the pixel value indicates the index in the colourmap of the colour. + // it is simply a combination of the RGB values we set earlier + ret.pixel = (gulong)(red*65536 + green*256 + blue); + return ret; +} + +static bool operator==(GdkColor a, GdkColor b) { + return + a.red == b.red && + a.green == b.green && + a.blue == b.blue && + a.pixel == b.pixel; +} + +static bool PtInRect(RECT *prc, POINT pt) { + if (pt.x < prc->left || pt.x > prc->right) + return false; + if (pt.y < prc->top || pt.y > prc->bottom) + return false; + return true; +} + +static unsigned int min(unsigned int a, unsigned int b) { + if (a < b) + return a; + else + return b; +} + +static unsigned int max(unsigned int a, unsigned int b) { + if (a > b) + return a; + else + return b; +} + +unsigned int GetDoubleClickTime() { + return 500; // Half a second +} + +#else + +bool IsKeyDown(int nVirtKey) { + return GetKeyState(nVirtKey) & 0x80000000; +} + +#endif + +//#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 +} + +static int clamp(int val, int minVal, int maxVal) { + if (val > maxVal) + val = maxVal; + if (val < minVal) + val = minVal; + return val; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Scintilla Zone + +#ifdef GTK +#define SHIFT_PRESSED 1 +#define LEFT_CTRL_PRESSED 2 +#define LEFT_ALT_PRESSED 4 +#endif + +#define SCI_NORM 0 +#define SCI_SHIFT SHIFT_PRESSED +#define SCI_CTRL LEFT_CTRL_PRESSED +#define SCI_ALT LEFT_ALT_PRESSED +#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT) + +class KeyToCommand { +public: + int key; + int modifiers; + int msg; +}; + +KeyToCommand keymapDefault[] = { + VK_DOWN, SCI_NORM, SCI_LINEDOWN, + VK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND, + VK_UP, SCI_NORM, SCI_LINEUP, + VK_UP, SCI_SHIFT, SCI_LINEUPEXTEND, + VK_LEFT, SCI_NORM, SCI_CHARLEFT, + VK_LEFT, SCI_SHIFT, SCI_CHARLEFTEXTEND, + VK_LEFT, SCI_CTRL, SCI_WORDLEFT, + VK_LEFT, SCI_CSHIFT, SCI_WORDLEFTEXTEND, + VK_RIGHT, SCI_NORM, SCI_CHARRIGHT, + VK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND, + VK_RIGHT, SCI_CTRL, SCI_WORDRIGHT, + VK_RIGHT, SCI_CSHIFT, SCI_WORDRIGHTEXTEND, + VK_HOME, SCI_NORM, SCI_VCHOME, + VK_HOME, SCI_SHIFT, SCI_VCHOMEEXTEND, + VK_HOME, SCI_CTRL, SCI_DOCUMENTSTART, + VK_HOME, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND, + VK_END, SCI_NORM, SCI_LINEEND, + VK_END, SCI_SHIFT, SCI_LINEENDEXTEND, + VK_END, SCI_CTRL, SCI_DOCUMENTEND, + VK_END, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND, + VK_PRIOR, SCI_NORM, SCI_PAGEUP, + VK_PRIOR, SCI_SHIFT, SCI_PAGEUPEXTEND, + VK_NEXT, SCI_NORM, SCI_PAGEDOWN, + VK_NEXT, SCI_SHIFT, SCI_PAGEDOWNEXTEND, + VK_DELETE, SCI_NORM, WM_CLEAR, + VK_DELETE, SCI_SHIFT, WM_CUT, + VK_INSERT, SCI_NORM, SCI_EDITTOGGLEOVERTYPE, + VK_INSERT, SCI_SHIFT, WM_PASTE, + VK_INSERT, SCI_CTRL, WM_COPY, + VK_ESCAPE, SCI_NORM, SCI_CANCEL, + VK_BACK, SCI_NORM, SCI_DELETEBACK, + 'Z', SCI_CTRL, WM_UNDO, + 'Y', SCI_CTRL, SCI_REDO, + 'X', SCI_CTRL, WM_CUT, + 'C', SCI_CTRL, WM_COPY, + 'V', SCI_CTRL, WM_PASTE, + 'A', SCI_CTRL, SCI_SELECTALL, + VK_TAB, SCI_NORM, SCI_TAB, + VK_TAB, SCI_SHIFT, SCI_BACKTAB, + VK_RETURN, SCI_NORM, SCI_NEWLINE, + 'L', SCI_CTRL, SCI_FORMFEED, + 0,0,0, +}; + +// Colour pairs hold a desired colour and the colour that the graphics engine +// allocates to approximate the desired colour. +// To make palette management more automatic, ColourPairs could register at +// construction time with a palette management object. +struct ColourPair { + COLORREF desired; + COLORREF allocated; + + ColourPair(COLORREF desired_=RGB(0,0,0)) { + desired = desired_; + allocated = desired; + } +}; + +class Indicator { +public: + int style; + ColourPair fore; + Indicator() : style(INDIC_PLAIN), fore(RGB(0,0,0)) { + } + void Draw(Surface *surface, RECT &rc); +}; + +void Indicator::Draw(Surface *surface, RECT &rc) { + surface->PenColor(fore.allocated); + int ymid = (rc.bottom + rc.top) / 2; + if (style == INDIC_SQUIGGLE) { + surface->MoveTo(rc.left, rc.top); + int x = rc.left + 2; + int y = 2; + while (x < rc.right) { + surface->LineTo(x, rc.top + y); + x += 2; + y = 2 - y; + } + } else if (style == INDIC_TT) { + surface->MoveTo(rc.left, ymid); + int x = rc.left + 5; + while (x < rc.right) { + surface->LineTo(x, ymid); + surface->MoveTo(x-3, ymid); + surface->LineTo(x-3, ymid+2); + + x++; + surface->MoveTo(x, ymid); + x += 5; + } + } else { // Either INDIC_PLAIN or unknown + surface->MoveTo(rc.left, ymid); + surface->LineTo(rc.right, ymid); + } +} + +class LineMarker { +public: + int markType; + ColourPair fore; + ColourPair back; + LineMarker() { + markType = SC_MARK_CIRCLE; + fore = RGB(0,0,0); + back = RGB(0xff,0xff,0xff); + } + void Draw(Surface *surface, RECT &rc); + +}; + +void LineMarker::Draw(Surface *surface, RECT &rc) { + int minDim = min ( rc.right - rc.left, rc.bottom - rc.top ); + int centreX = (rc.right + rc.left) / 2; + int centreY = (rc.bottom + rc.top) / 2; + int dimOn2 = minDim / 2; + int dimOn4 = minDim / 4; + if (markType == SC_MARK_ROUNDRECT) { + RECT rcRounded = rc; + rcRounded.left = rc.left + 1; + rcRounded.right = rc.right - 1; + surface->RoundedRectangle(rcRounded, fore.allocated, back.allocated); + } else if (markType == SC_MARK_CIRCLE) { + RECT rcCircle; + rcCircle.left = centreX - minDim / 2; + rcCircle.top = centreY - minDim / 2; + rcCircle.right = centreX + minDim / 2; + rcCircle.bottom = centreY + minDim / 2; + surface->Ellipse(rcCircle, fore.allocated, back.allocated); + } else if (markType == SC_MARK_ARROW) { + POINT pts[] = { + {centreX, centreY - minDim / 2}, + {centreX, centreY + minDim / 2}, + {centreX + minDim / 2, centreY}, + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + + } else if (markType == SC_MARK_SMALLRECT) { + RECT rcSmall; + rcSmall.left = rc.left + 1; + rcSmall.top = rc.top + 2; + rcSmall.right = rc.right - 1; + rcSmall.bottom = rc.bottom - 2; + surface->Rectangle(rcSmall, fore.allocated, back.allocated); + } else { // SC_MARK_SHORTARROW + POINT pts[] = { + {centreX, centreY + dimOn2}, + {centreX + dimOn2, centreY}, + {centreX, centreY - dimOn2}, + {centreX, centreY - dimOn4}, + {centreX - dimOn4, centreY - dimOn4}, + {centreX - dimOn4, centreY + dimOn4}, + {centreX, centreY + dimOn4}, + {centreX, centreY + dimOn2}, + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + } +} + +class Palette { +public: + Palette(); + ~Palette(); + + void Clear(); + void Want(ColourPair &cp); + void Allocate(HWND hwnd=0); + void Find(ColourPair &cp); + + int used; + bool allowRealization; + enum {numEntries = 100}; + ColourPair entries[numEntries]; + +#ifdef GTK + GdkColor *allocatedPalette; + int allocatedLen; +#else + HPALETTE hpal; + HPALETTE SelectInto(HDC hdc, bool inBackGround); +#endif +}; + +Palette::Palette() { + used = 0; + allowRealization = false; +#ifdef GTK + allocatedPalette = 0; + allocatedLen = 0; +#else + hpal = 0; +#endif +} + +Palette::~Palette() { + Clear(); +} + +void Palette::Clear() { + used = 0; +#ifdef GTK + delete []allocatedPalette; + allocatedPalette = 0; + allocatedLen = 0; +#else + if (hpal) + DeleteObject(hpal); + hpal = 0; +#endif +} + +void Palette::Want(ColourPair &cp) { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) + return; + } + + if (used < numEntries) { + entries[used].desired = cp.desired; + entries[used].allocated = cp.desired; + used++; + } +} + +void Palette::Allocate(HWND hwnd) { +#ifdef GTK + if (allocatedPalette) { + gdk_colormap_free_colors(gtk_widget_get_colormap(hwnd), + allocatedPalette, allocatedLen); + delete []allocatedPalette; + allocatedPalette = 0; + allocatedLen = 0; + } + allocatedPalette = new GdkColor[used]; + gboolean *successPalette = new gboolean[used]; + if (allocatedPalette) { + allocatedLen = used; + int iPal = 0; + for (iPal=0;iPal(pal); + logpal->palVersion = 0x300; + logpal->palNumEntries = used; + for (int iPal=0;iPalpalPalEntry[iPal].peRed = GetRValue(desired); + logpal->palPalEntry[iPal].peGreen = GetGValue(desired); + logpal->palPalEntry[iPal].peBlue = GetBValue(desired); + entries[iPal].allocated = + PALETTERGB(GetRValue(desired), GetGValue(desired), GetBValue(desired)); + // PC_NOCOLLAPSE means exact colours allocated even when in background this means other windows + // are less likely to get their colours and also flashes more when switching windows + logpal->palPalEntry[iPal].peFlags = PC_NOCOLLAPSE; + // 0 allows approximate colours when in background, yielding moe colours to other windows + //logpal->palPalEntry[iPal].peFlags = 0; + } + hpal = CreatePalette(logpal); + delete []pal; + //dprintf("Palette created %x\n", hpal); + } +#endif +} + +void Palette::Find(ColourPair &cp) { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) { + cp.allocated = entries[i].allocated; + return; + } + } + cp.allocated = cp.desired; +} + +#ifndef GTK +HPALETTE Palette::SelectInto(HDC hdc, bool inBackGround) { + if (allowRealization) + return SelectPalette(hdc, hpal, inBackGround); + return 0; +} +#endif + +class Style { +public: + ColourPair fore; + ColourPair back; + bool bold; + bool italic; + int size; + char fontName[100]; + + HFONT font; + unsigned int lineHeight; + unsigned int ascent; + unsigned int descent; + unsigned int externalLeading; + unsigned int aveCharWidth; + unsigned int spaceWidth; + + Style(); + ~Style(); + void Clear(COLORREF fore_=RGB(0,0,0), COLORREF back_=RGB(0xff,0xff,0xff), + int size_=defaultSize, const char *fontName_=defaultFont, bool bold_=false, bool italic_=false); + void Realise(); +}; + +Style::Style() { + font = 0; + Clear(); +} + +Style::~Style() { +#ifdef GTK + if (font) + gdk_font_unref(font); +#else + if (font) + DeleteObject(font); +#endif + font = 0; +} + +void Style::Clear(COLORREF fore_, COLORREF back_, int size_, const char *fontName_, bool bold_, bool italic_) { + fore.desired = fore_; + back.desired = back_; + bold = bold_; + italic = italic_; + size = size_; + strcpy(fontName, fontName_); +#ifdef GTK + if (font) + gdk_font_unref(font); +#else + if (font) + DeleteObject(font); +#endif + font = 0; +} + +void Style::Realise() { +#ifdef GTK + char fontspec[300]; + fontspec[0] = '\0'; + strcat(fontspec, "-*-"); + strcat(fontspec, fontName); + if (bold) + strcat(fontspec, "-bold"); + else + strcat(fontspec, "-medium"); + if (italic) + strcat(fontspec, "-i"); + else + strcat(fontspec, "-r"); + strcat(fontspec, "-*-*-*"); + char sizePts[100]; + sprintf(sizePts, "-%0d", size * 10); + strcat(fontspec, sizePts); + strcat(fontspec, "-*-*-*-*-*-*"); + font = gdk_font_load(fontspec); + if (NULL == font) { + // Font not available so substitute a reasonable code font + // iso8859 appears to only allow western characters. + //dprintf("Null font %s\n", fontspec); + font = gdk_font_load("*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*"); + } + gint lbearing; + gint rbearing; + gint width; + gint ascent_; + gint descent_; + + gdk_string_extents(font, + " ", + &lbearing, + &rbearing, + &width, + &ascent_, + &descent_); + spaceWidth = width; + + gdk_string_extents(font, + " `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + &lbearing, + &rbearing, + &width, + &ascent_, + &descent_); + ascent = ascent_; + descent = descent_; + //dprintf("Font l:%s: r:%d %d w:%d a:%d d:%d\n", fontspec, lbearing, rbearing, aveCharWidth, ascent, descent); + lineHeight = ascent + descent; + externalLeading = 0; + aveCharWidth = gdk_char_width(font, 'n'); +#else + if (font) + DeleteObject(font); + font = 0; + + HDC hdc = CreateCompatibleDC(NULL); + + LOGFONT lf; + memset(&lf, 0, sizeof(lf)); + // The negative is to allow for leading + lf.lfHeight = -(abs(size) * GetDeviceCaps(hdc, LOGPIXELSY)) / 72; + lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; + lf.lfItalic = italic ? 1 : 0; + lf.lfCharSet = DEFAULT_CHARSET; + strcpy(lf.lfFaceName, fontName); + + font = CreateFontIndirect(&lf); + HFONT fontOld = static_cast(SelectObject(hdc, font)); + TEXTMETRIC tm; + GetTextMetrics(hdc, &tm); + ascent = tm.tmAscent; + descent = tm.tmDescent; + /* + dprintf("Font: ht:%d as:%d de:%d led:%d\n", + tm.tmHeight, + tm.tmAscent, + tm.tmDescent, + tm.tmExternalLeading); + */ + // Probably more typographically correct to include leading + // but that means more complex drawing as leading must be erased + //lineHeight = tm.tmExternalLeading + tm.tmHeight; + externalLeading = tm.tmExternalLeading; + lineHeight = tm.tmHeight; + aveCharWidth = tm.tmAveCharWidth; + + SIZE sz; + char chSpace = ' '; + GetTextExtentPoint32(hdc, &chSpace, 1, &sz); + spaceWidth = sz.cx; + + SelectObject(hdc, fontOld); + DeleteDC(hdc); +#endif +} + +class Scintilla { + + // Enumeration of commands and child windows + enum { + idCallTip=1, + idAutoComplete=2, + + idcmdUndo=10, + idcmdRedo=11, + idcmdCut=12, + idcmdCopy=13, + idcmdPaste=14, + idcmdDelete=15, + idcmdSelectAll=16, + }; + + HWND hwndCallTip; + bool inCallTipMode; + int posStartCallTip; + HWND hwndAutoComplete; + bool inAutoCompleteMode; + int posStartAutoComplete; + char autoCompleteStops[256]; + int endStyled; + +#ifdef GTK + _ScintillaObject *sci; + GtkWidget *hwnd; + GtkObject *adjustmentv; + GtkWidget *scrollbarv; + GtkObject *adjustmenth; + GtkWidget *scrollbarh; + GtkWidget *draw; + GtkWidget *popup; + GtkWidget *listAutoComplete; + int currentAutoComplete; + char *valCT; + int startHighlightCT; + int endHighlightCT; +#endif + + bool isModified; + bool hideSelection; + bool inOverstrike; + + int selMarginWidth; + int lineNumberWidth; + int fixedColumnWidth; + bool bufferedDraw; + + Style styles[STYLE_MAX + 1]; + bool stylesValid; + + LineMarker markers[MARKER_MAX + 1]; + + Indicator indicators[INDIC_MAX + 1]; + + Palette palette; + + bool caret; + + unsigned int tabInChars; + + enum {maxLineLength = 4000}; + int lineHeight; + unsigned int maxAscent; + unsigned int maxDescent; + unsigned int tabWidth; + unsigned int aveCharWidth; + unsigned int spaceWidth; + int xOffset; + + int eolMode; + int dbcsCodePage; + + ColourPair foreground; + ColourPair background; + int size; + char fontName[100]; + bool bold; + bool italic; + bool selforeset; + ColourPair selforeground; + bool selbackset; + ColourPair selbackground; + + ColourPair selbar; + ColourPair linenumfore; + ColourPair caretcolour; + +#ifdef GTK + GdkPixmap *pixmapLine; + GdkPixmap *pixmapSelMargin; +#else + HBRUSH selmarginbrush; + HBITMAP bitmapSelMargin; + HBITMAP bitmapLineBuffer; + HBITMAP oldBitmap; + HDC hdcBitmap; +#endif + + KeyToCommand *keymap; + int keymapLen; + int keymapAlloc; + +#ifdef GTK + unsigned char *pasteBuffer; +#endif + + void InvalidateStyleData(); + void RefreshStyleData(); + + int LineFromPosition(int pos); + + int SelectionStart(); + int SelectionEnd(); +#ifndef GTK + HGLOBAL GetSelText(); +#endif + void DelCharBack(); + int ClampPositionIntoDocument(int pos); + bool IsCrLf(int pos); + int MovePositionOutsideChar(int pos, int moveDir); + + void GetTextRect(RECT *prc); + void ScrollTo(int line); + void HorizontalScrollTo(int xPos); + void EnsureCaretVisible(); + void MoveCaret(int x, int y); + void ShowCaretAtCurrentPosition(); + void DropCaret(); + + void GetClientRectangle(RECT *rc); + int LinesOnScreen(); + int LinesToScroll(); + int MaxScrollPos(); + POINT lastClick; + unsigned int lastClickTime; + enum { selChar, selWord, selLine } selType; + bool capturedMouse; + int lastXChosen; + int lineAnchor; + int originalAnchorPos; + int currentPos; + int anchor; + int topLine; + bool viewWhitespace; + int stylingPos; + int stylingMask; + +#ifndef GTK + static HINSTANCE hInstance; + HWND hwnd; +#endif + + Document doc; + + void Paint(RECT rcPaint); +#ifdef GTK + gint PaintCT(GtkWidget *widget_, GdkEventExpose ose); +#endif + + void DropGraphics(); + void SetVertScrollFromTopLine(); + void SetScrollBars(LPARAM *plParam=NULL,WPARAM wParam=0); + void Redraw(); + void RedrawSelMargin(); + void ModifiedAt(int pos); + + // Gateways to modifying document + void DeleteChars(int pos, int len); + void InsertStyledString(int position, char *s, int insertLength); + void Undo(); + void Redo(); + + void InsertChar(int pos, char ch); + void InsertString(int position, char *s); + void InsertString(int position, char *s, int insertLength); + + void ClearAll(); + void ClearSelection(); + void DelChar(); + int LinesTotal(); + void InvalidateRange(int start, int end); + void SetSelection(int currentPos_, int anchor_); + void SetSelection(int currentPos_); + void SetPosition(int pos, bool shift=false); + int LineStart(int line); + int LineEndPosition(int position); + int VCHomePosition(int position); + int MovePositionTo(int newPos, bool extend = false); + void SetLastXChosen(); + void ChangePosition(int delta, bool fExtend=false); + void NotifyChange(); + void NotifyStyleNeeded(int endStyleNeeded); + void NotifyChar(char ch); + void NotifySavePoint(bool isSavePoint); + void NotifyModifyAttempt(); + void NotifyKey(int key, int modifiers); + + void Indent(bool forwards); + void AddChar(char ch); + int ExtendWordSelect(int pos, int delta); + int NextWordStart(int delta); + int KeyCommand(WORD iMessage); + int KeyDown(int key, bool shift, bool ctrl, bool alt); + + void AssignCmdKey(int key, int modifiers, int msg); + bool GetWhitespaceVisible(); + void SetWhitespaceVisible(bool view); + + void DeleteUndoHistory(); + void Cut(); + void Copy(); + void Paste(); + void Clear(); + void SelectAll(); + + void GoToLine(int lineNo); + + POINT LocationFromPosition(int pos); + int PositionFromLocation(POINT pt); + int LineFromLocation(POINT pt); + int Length(); + char CharAt(int pos); + int CurrentPosition(); + + void PaintSelMargin(Surface *surface, RECT &rc); +#ifndef GTK + void CreateGraphicObjects(HDC hdc); +#endif + void RealizeWindowPalette(bool inBackGround); +#ifdef GTK + void Scroll(int topLineNew); +#else + void Scroll(WPARAM wParam); +#endif + void HorizontalScroll(WPARAM wParam); + + void AutoCompleteCancel(); + void AutoCompleteMove(int delta); + void AutoCompleteChanged(char ch=0); + void AutoCompleteCompleted(); + void AutoCompleteStart(char *list); + + void CallTipStart(int pos, char *defn); + void CallTipCancel(); +#ifdef GTK + void AddToPopUp(const char *label, bool enabled=true); +#endif + void ContextMenu(POINT pt); + void Command(WPARAM wParam); + bool IsWordAt(int start, int end); + long FindText(WORD iMessage,WPARAM wParam,LPARAM lParam); + + void Capture(); + void Release(); + + void ButtonDown(POINT pt, unsigned int curTime, bool shift); + void ButtonMove(POINT pt); + void ButtonUp(POINT pt, unsigned int curTime); + +#ifdef GTK + // GTK methods + void ReceivedSelection(GtkSelectionData *selection_data, guint time); + void GetSelection(GtkSelectionData *selection_data); + void Resize(int width, int height); + + // Callback functions + static gint Expose(GtkWidget *widget, GdkEventExpose *ose, Scintilla *sci); + static gint ExposeCT(GtkWidget *widget, GdkEventExpose *ose, gpointer p); + static void ScrollSignal(GtkAdjustment *adj, Scintilla *sci); + static void ScrollHSignal(GtkAdjustment *adj, Scintilla *sci); + static void PopUpCB(GtkWidget *widget, gpointer cbdata); + static void SelectionAC(GtkWidget *clist, gint row, gint column, + GdkEventButton *event, gpointer p); + static gint MoveResize(GtkWidget *widget, GtkAllocation *allocation, Scintilla *sci); + static gint Press(GtkWidget *widget, GdkEventButton *event, Scintilla *sci); + static gint MouseRelease(GtkWidget *widget, GdkEventButton *event, Scintilla *sci); + static gint Motion(GtkWidget *widget, GdkEventMotion *event, Scintilla *sci); + static gint KeyPress(GtkWidget *widget, GdkEventKey *event, Scintilla *sci); + static gint KeyRelease(GtkWidget *widget, GdkEventKey *event, Scintilla *sci); + static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, + guint time, Scintilla *sci); + static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, + guint info, guint time, Scintilla *sci); +#endif + +public: +#ifdef GTK + Scintilla(_ScintillaObject *sci_); +#else + Scintilla(); +#endif + ~Scintilla(); + + long WndProc(WORD iMessage,WPARAM wParam,LPARAM lParam); +#ifndef GTK + static void Register(HINSTANCE hInstance_); + static LRESULT PASCAL SWndProc( + HWND hWnd,UINT iMessage,WPARAM wParam, LPARAM lParam); +#endif + int ctrlID; // Public so scintilla_set_id can use it +}; + +#ifndef GTK +HINSTANCE Scintilla::hInstance = 0; +#endif + +#ifdef GTK + +gint Scintilla::MoveResize(GtkWidget *widget, GtkAllocation *allocation, Scintilla *sci) { + //dprintf("sci move resize %d %d\n", allocation->width, allocation->height); + sci->Resize(allocation->width, allocation->height); + return TRUE; +} + +gint Scintilla::Press(GtkWidget *widget, GdkEventButton *event, Scintilla *sci) { + // dprintf("Press %x time=%d state = %x button = %x\n",p,event->time, event->state, event->button); + POINT pt; + pt.x = int(event->x); + pt.y = int(event->y); + if (event->button == 1) { + sci->ButtonDown(pt, event->time, event->state & 1); + } else if (event->button == 3) { + // PopUp menu + sci->ContextMenu(pt); + } + return TRUE; +} + +gint Scintilla::MouseRelease(GtkWidget *widget, GdkEventButton *event, Scintilla *sci) { + // dprintf("Release %x %d\n",p,event->time); + if (event->button == 1) { + POINT pt; + pt.x = int(event->x); + pt.y = int(event->y); + sci->ButtonUp(pt, event->time); + } + return TRUE; +} + +gint Scintilla::Motion(GtkWidget *widget, GdkEventMotion *event, Scintilla *sci) { + //dprintf("Move %x %d\n",p,event->time); + int x = 0; + int y = 0; + GdkModifierType state; + if (event->is_hint) { + gdk_window_get_pointer(event->window, &x, &y, &state); + } else { + x = static_cast(event->x); + y = static_cast(event->y); + state = static_cast(event->state); + } + if (state & GDK_BUTTON1_MASK) { + POINT pt; + pt.x = x; + pt.y = y; + sci->ButtonMove(pt); + } + return TRUE; +} + +gint Scintilla::KeyPress(GtkWidget *widget, GdkEventKey *event, Scintilla *sci) { + //dprintf("SC-key: %d %x %x\n",event->keyval, event->state, GTK_WIDGET_FLAGS(widget)); + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + bool alt = event->state & GDK_MOD1_MASK; + int key = event->keyval; + if (ctrl && (key < 128)) + key = toupper(key); + if (key == GDK_ISO_Left_Tab) + key = VK_TAB; + sci->KeyDown(key, shift, ctrl, alt); + //dprintf("SK-key: %d %x %x\n",event->keyval, event->state, GTK_WIDGET_FLAGS(widget)); + return 1; +} + +gint Scintilla::KeyRelease(GtkWidget *widget, GdkEventKey *event, Scintilla *sci) { + //dprintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string); + return TRUE; +} + +gint Scintilla::Expose(GtkWidget *widget, GdkEventExpose *ose, Scintilla *sci) { + RECT rcPaint; + rcPaint.left = ose->area.x; + rcPaint.top = ose->area.y; + rcPaint.right = ose->area.x + ose->area.width; + rcPaint.bottom = ose->area.y + ose->area.height; + sci->Paint(rcPaint); + return TRUE; +} + +void Scintilla::ScrollSignal(GtkAdjustment *adj, Scintilla *sci) { + //dprintf("Scrolly %g %x\n",adj->value,p); + sci->Scroll((int)adj->value); +} + +void Scintilla::ScrollHSignal(GtkAdjustment *adj, Scintilla *sci) { + //dprintf("Scrollyh %g %x\n",adj->value,p); + sci->HorizontalScrollTo((int)adj->value * 2); +} + +static gint sci_focus_in (GtkWidget *widget, + GdkEventFocus *event) { + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); + return FALSE; +} + +static gint sci_focus_out (GtkWidget *widget, + GdkEventFocus *event) { + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + return FALSE; +} + +void Scintilla::SelectionReceived(GtkWidget *widget, + GtkSelectionData *selection_data, + guint time, + Scintilla *sci) { + //dprintf("Selection received\n"); + sci->ReceivedSelection(selection_data,time); +} + +void Scintilla::SelectionGet(GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time, + Scintilla *sci) { + //dprintf("Selection get\n"); + sci->GetSelection(selection_data); +} + +#endif + +#ifdef GTK +Scintilla::Scintilla(_ScintillaObject *sci_) +#else +Scintilla::Scintilla() +#endif +{ // Because of the two signatures, the indenter would get confused if the brace were in the normal place +#ifdef GTK + sci = sci_; + hwnd = GTK_WIDGET(sci); + adjustmentv = 0; + adjustmenth = 0; + ctrlID = 0; +#endif + + hwndCallTip = 0; + hwndAutoComplete = 0; + inAutoCompleteMode = false; + inCallTipMode = false; + posStartAutoComplete = 0; + strcpy(autoCompleteStops, ""); + endStyled = 0; + + isModified = false; + hideSelection = false; + inOverstrike = false; + + selMarginWidth = 20; + lineNumberWidth = 0; + fixedColumnWidth = selMarginWidth + lineNumberWidth; + bufferedDraw = true; + + stylesValid = false; + + lastClick.x = 0; + lastClick.y = 0; + lastClickTime = 0; + capturedMouse = false; + selType = selChar; + + lastXChosen = 0; + lineAnchor = 0; + originalAnchorPos = 0; + + lineHeight = 24; // bad value to start + +#ifdef GTK + pixmapLine = 0; + pixmapSelMargin = 0; +#else + bitmapLineBuffer = NULL; + bitmapSelMargin = NULL; + oldBitmap = NULL; + hdcBitmap = NULL; +#endif + + viewWhitespace = false; + + caret = false; + + tabInChars = 8; + + lineHeight = 1; + maxAscent = 1; + maxDescent = 1; + + xOffset = 0; + currentPos = 0; + anchor = 0; + stylingPos = 0; + stylingMask = 0; + +#ifdef GTK + eolMode = SC_EOL_LF; +#else + eolMode = SC_EOL_CRLF; +#endif + + dbcsCodePage = 0; + + foreground.desired = RGB(0,0,0); + background.desired = RGB(0xff,0xff,0xff); + size = defaultSize; + strcpy(fontName, defaultFont); + bold = false; + italic = false; + selforeset = false; + selforeground.desired = RGB(0xff,0,0); + selbackset = true; + selbackground.desired = RGB(0xC0,0xC0,0xC0); +#ifdef GTK + selbar.desired = RGB(0xe0, 0xe0, 0xe0); +#else + selbar.desired = GetSysColor(COLOR_3DFACE); +#endif + // TODO: the line numbers should have their own style + linenumfore.desired = RGB(0,0,0); + caretcolour.desired = RGB(0xff,0,0); + + indicators[0].style = INDIC_SQUIGGLE; + indicators[0].fore = RGB(0,0x7f,0); + indicators[1].style = INDIC_TT; + indicators[1].fore = RGB(0,0,0xff); + indicators[2].style = INDIC_PLAIN; + indicators[2].fore = RGB(0xff,0,0); + + keymap = 0; + keymapLen = 0; + keymapAlloc = 0; + for (int keyIndex = 0; keymapDefault[keyIndex].key; keyIndex++) { + AssignCmdKey(keymapDefault[keyIndex].key, keymapDefault[keyIndex].modifiers, + keymapDefault[keyIndex].msg); + } + + topLine = 0; + +#ifdef GTK + GTK_WIDGET_SET_FLAGS(hwnd, GTK_CAN_FOCUS); + GTK_WIDGET_SET_FLAGS(GTK_WIDGET(hwnd), GTK_SENSITIVE); + gtk_signal_connect(GTK_OBJECT(hwnd), "size_allocate", + GTK_SIGNAL_FUNC(MoveResize), this); + gtk_widget_set_events (hwnd, + GDK_KEY_PRESS_MASK + | GDK_KEY_RELEASE_MASK + | GDK_FOCUS_CHANGE_MASK); + // Using "after" connect to avoid main window using cursor keys + // to move focus. + //gtk_signal_connect(GTK_OBJECT(hwnd), "key_press_event", + // GtkSignalFunc(key_event), this); + gtk_signal_connect_after(GTK_OBJECT(hwnd), "key_press_event", + GtkSignalFunc(KeyPress), this); + + gtk_signal_connect(GTK_OBJECT(hwnd), "key_release_event", + GtkSignalFunc(KeyRelease), this); + gtk_signal_connect(GTK_OBJECT(hwnd), "focus_in_event", + GtkSignalFunc(sci_focus_in), this); + gtk_signal_connect(GTK_OBJECT(hwnd), "focus_out_event", + GtkSignalFunc(sci_focus_out), this); + + draw = gtk_drawing_area_new(); + gtk_signal_connect(GTK_OBJECT(draw), "expose_event", + GtkSignalFunc(Expose), this); + gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event", + GtkSignalFunc(Motion), this); + gtk_signal_connect(GTK_OBJECT(draw), "button_press_event", + GtkSignalFunc(Press), this); + gtk_signal_connect(GTK_OBJECT(draw), "button_release_event", + GtkSignalFunc(MouseRelease), this); + gtk_signal_connect(GTK_OBJECT(draw), "selection_received", + GtkSignalFunc(SelectionReceived), this); + gtk_signal_connect(GTK_OBJECT(draw), "selection_get", + GtkSignalFunc(SelectionGet), this); + + gtk_widget_set_events(draw, + GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + ); + + gtk_drawing_area_size(GTK_DRAWING_AREA(draw), 400, 400); + gtk_fixed_put(GTK_FIXED(sci), draw, 0, 0); + + adjustmentv = gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0); + scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv)); + GTK_WIDGET_UNSET_FLAGS(scrollbarv, GTK_CAN_FOCUS); + gtk_signal_connect(GTK_OBJECT(adjustmentv), "value_changed", + GTK_SIGNAL_FUNC(ScrollSignal), this); + gtk_fixed_put(GTK_FIXED(sci), scrollbarv, 0, 0); + + adjustmenth = gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0); + scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth)); + GTK_WIDGET_UNSET_FLAGS(scrollbarh, GTK_CAN_FOCUS); + gtk_signal_connect(GTK_OBJECT(adjustmenth), "value_changed", + GTK_SIGNAL_FUNC(ScrollHSignal), this); + gtk_fixed_put(GTK_FIXED(sci), scrollbarh, 0, 0); + + gtk_widget_grab_focus(hwnd); + + static const GtkTargetEntry targets[] = { + { "STRING", 0, TARGET_STRING + }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } + }; + static const gint n_targets = sizeof(targets) / sizeof(targets[0]); + + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + gtk_selection_add_targets (GTK_WIDGET(draw), clipboard_atom, + targets, n_targets); + + popup = 0; + listAutoComplete = 0; + currentAutoComplete = 0; + valCT = 0; + startHighlightCT = 0; + endHighlightCT = 0; +#endif +} + +Scintilla::~Scintilla() { + DropCaret(); + + DropGraphics(); + +#ifdef GTK + if (hwndAutoComplete) + gtk_widget_destroy(GTK_WIDGET(hwndAutoComplete)); +#else + if (hwndAutoComplete) + DestroyWindow(hwndAutoComplete); +#endif + hwndAutoComplete = 0; +#ifdef GTK + if (hwndCallTip) + gtk_widget_destroy(GTK_WIDGET(hwndCallTip)); +#else + if (hwndCallTip) + DestroyWindow(hwndCallTip); +#endif + hwndCallTip = 0; + + delete []keymap; + keymap = 0; + keymapLen = 0; + keymapAlloc = 0; +} + +void Scintilla::DropGraphics() { +#ifdef GTK + if (pixmapLine) { + gdk_pixmap_unref(pixmapLine); + pixmapLine = 0; + } + if (pixmapSelMargin) { + gdk_pixmap_unref(pixmapSelMargin); + pixmapSelMargin = 0; + } +#else + if (selmarginbrush) + DeleteObject(selmarginbrush); + selmarginbrush = 0; + if (hdcBitmap && oldBitmap) + SelectObject(hdcBitmap,oldBitmap); + oldBitmap = NULL; + if (bitmapLineBuffer) + DeleteObject(bitmapLineBuffer); + bitmapLineBuffer = NULL; + if (bitmapSelMargin) + DeleteObject(bitmapSelMargin); + bitmapSelMargin = 0; + if (hdcBitmap) + DeleteDC(hdcBitmap); + hdcBitmap = NULL; +#endif +} + +void Scintilla::InvalidateStyleData() { + stylesValid = false; + palette.Clear(); + DropGraphics(); + DropCaret(); +} + +void Scintilla::RefreshStyleData() { + if (!stylesValid) { + stylesValid = true; + maxAscent = 1; + maxDescent = 1; + int i; // A common iterator to avoid thinking about old vs new scope rules + for (i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { + styles[i].Realise(); + if (maxAscent < styles[i].ascent) + maxAscent = styles[i].ascent; + if (maxDescent < styles[i].descent) + maxDescent = styles[i].descent; + palette.Want(styles[i].fore); + palette.Want(styles[i].back); + } + for (i=0;i<(sizeof(indicators)/sizeof(indicators[0]));i++) { + palette.Want(indicators[i].fore); + } + for (i=0;i<(sizeof(markers)/sizeof(markers[0]));i++) { + palette.Want(markers[i].fore); + palette.Want(markers[i].back); + } + palette.Want(foreground); + palette.Want(background); + palette.Want(selforeground); + palette.Want(selbackground); + palette.Want(selbar); + palette.Want(linenumfore); + palette.Want(caretcolour); + + lineHeight = maxAscent + maxDescent; + aveCharWidth = styles[0].aveCharWidth; + spaceWidth = styles[0].spaceWidth; + tabWidth = spaceWidth * tabInChars; + + palette.Allocate(hwnd); + for (i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { + palette.Find(styles[i].fore); + palette.Find(styles[i].back); + } + for (i=0;i<(sizeof(indicators)/sizeof(indicators[0]));i++) { + palette.Find(indicators[i].fore); + } + for (i=0;i<(sizeof(markers)/sizeof(markers[0]));i++) { + palette.Find(markers[i].fore); + palette.Find(markers[i].back); + } + palette.Find(foreground); + palette.Find(background); + palette.Find(selforeground); + palette.Find(selbackground); + palette.Find(selbar); + palette.Find(linenumfore); + palette.Find(caretcolour); + + SetScrollBars(); + } +} + +void Scintilla::GetClientRectangle(RECT *rc) { +#ifdef GTK + rc->left = 0; + rc->top = 0; + // Before any size allocated pretend its 100 wide so not scrolled + rc->right = 100; + rc->bottom = 100; + if (draw && draw->allocation.width > 20) { + rc->right = draw->allocation.width; + rc->bottom = draw->allocation.height; + } +#else + GetClientRect(hwnd, rc); +#endif +} + +int Scintilla::LinesTotal() { + return doc.Lines(); +} + +int Scintilla::LinesOnScreen() { + RECT rcClient; + GetClientRectangle(&rcClient); + int htClient = rcClient.bottom - rcClient.top; +#ifdef GTK + htClient -= 16; +#else + htClient -= GetSystemMetrics(SM_CYHSCROLL); +#endif + //dprintf("lines on screen = %d\n", htClient / lineHeight + 1); + return htClient / lineHeight + 1; +} + +int Scintilla::LinesToScroll() { + int retVal = LinesOnScreen() - 1; + if (retVal < 1) + return 1; + else + return retVal; +} + +int Scintilla::MaxScrollPos() { + //dprintf("Lines %d screen = %d maxScroll = %d\n", + //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1); + //int retVal = LinesTotal() - LinesOnScreen() + 1; + int retVal = LinesTotal() - LinesOnScreen(); + if (retVal < 0) + return 0; + else + return retVal; +} + +int Scintilla::ClampPositionIntoDocument(int pos) { + return clamp(pos, 0, Length()); +} + +bool Scintilla::IsCrLf(int pos) { + if (pos < 0) + return false; + if (pos >= (Length()-1)) + return false; + return (doc.CharAt(pos) == '\r') && (doc.CharAt(pos+1) == '\n'); +} + +// Normalise a position so that it is not halfway through a two byte character. +// This can occur in two situations - +// When lines are terminated with \r\n pairs which should be treated as one character. +// When displaying DBCS text such as Japanese. +// If moving, move the position in the indicated direction. +int Scintilla::MovePositionOutsideChar(int pos, int moveDir) { + //dprintf("NoCRLF %d %d\n", pos, moveDir); + // If out of range, just return value - should be fixed up after + if (pos < 0) + return pos; + if (pos > Length()) + return pos; + + // Position 0 and Length() can not be between any two characters + if (pos == 0) + return pos; + if (pos == Length()) + return pos; + + // assert pos > 0 && pos < Length() + if (IsCrLf(pos-1)) { + if (moveDir > 0) + return pos + 1; + else + return pos - 1; + } + + // Not between CR and LF + +#ifndef GTK + // DBCS support + if (dbcsCodePage) { + // Anchor DBCS calculations at start of line because start of line can + // not be a DBCS trail byte. + int startLine = pos; + while (startLine > 0 && doc.CharAt(startLine) != '\r' && doc.CharAt(startLine) != '\n') + startLine--; + bool atLeadByte = false; + while (startLine < pos) { + if (atLeadByte) + atLeadByte = false; + else if (IsDBCSLeadByteEx(dbcsCodePage, doc.CharAt(startLine))) + atLeadByte = true; + else + atLeadByte = false; + startLine++; + //dprintf("DBCS %s\n", atlead ? "D" : "-"); + } + if (atLeadByte) { + // Position is between a lead byte and a trail byte + if (moveDir > 0) + return pos + 1; + else + return pos - 1; + } + } +#endif + + return pos; +} + +void Scintilla::InvalidateRange(int start, int end) { + int minPos = start; + if (minPos > end) + minPos = end; + int maxPos = start; + if (maxPos < end) + maxPos = end; + int minLine = LineFromPosition(minPos); + int maxLine = LineFromPosition(maxPos); + RECT rcRedraw; + rcRedraw.left = fixedColumnWidth; + rcRedraw.top = (minLine - topLine) * lineHeight; + rcRedraw.right = 32000; + rcRedraw.bottom = (maxLine - topLine + 1) * lineHeight; + // Ensure rectangle is within 16 bit space + rcRedraw.top = clamp(rcRedraw.top, -32000, 32000); + rcRedraw.bottom = clamp(rcRedraw.bottom, -32000, 32000); + +#ifdef GTK + if (hwnd) { + GdkRectangle update_rect; + update_rect.x = rcRedraw.left; + update_rect.y = rcRedraw.top; + update_rect.width = 32000; + update_rect.height = rcRedraw.bottom - rcRedraw.top + 1; + gtk_widget_queue_draw_area(draw, + update_rect.x, + update_rect.y, + update_rect.width, + update_rect.height); + } +#else + InvalidateRect(hwnd,&rcRedraw,FALSE); +#endif +} + +void Scintilla::SetSelection(int currentPos_, int anchor_) { + currentPos_ = ClampPositionIntoDocument(currentPos_); + anchor_ = ClampPositionIntoDocument(anchor_); + if ((currentPos != currentPos_) || (anchor != anchor_)) { + int firstAffected = anchor; + if (firstAffected > currentPos) + firstAffected = currentPos; + if (firstAffected > anchor_) + firstAffected = anchor_; + if (firstAffected > currentPos_) + firstAffected = currentPos_; + int lastAffected = anchor; + if (lastAffected < currentPos) + lastAffected = currentPos; + if (lastAffected < anchor_) + lastAffected = anchor_; + if (lastAffected < (currentPos_+1)) // +1 ensures caret repainted + lastAffected = (currentPos_+1); + currentPos = currentPos_; + anchor = anchor_; + InvalidateRange(firstAffected, lastAffected); + } +} + +void Scintilla::SetSelection(int currentPos_) { + currentPos_ = ClampPositionIntoDocument(currentPos_); + if (currentPos != currentPos_) { + int firstAffected = anchor; + if (firstAffected > currentPos) + firstAffected = currentPos; + if (firstAffected > currentPos_) + firstAffected = currentPos_; + int lastAffected = anchor; + if (lastAffected < currentPos) + lastAffected = currentPos; + if (lastAffected < (currentPos_+1)) // +1 ensures caret repainted + lastAffected = (currentPos_+1); + currentPos = currentPos_; + InvalidateRange(firstAffected, lastAffected); + } +} + +void Scintilla::SetPosition(int pos, bool shift) { + int oldPos = currentPos; + currentPos = ClampPositionIntoDocument(pos); + currentPos = MovePositionOutsideChar(currentPos, oldPos - currentPos); + if (!shift) + anchor = currentPos; + EnsureCaretVisible(); +} + +int Scintilla::LineStart(int line) { + return doc.LineStart(line); +} + +int Scintilla::LineEndPosition(int position) { + int line = LineFromPosition(position); + if (line == LinesTotal() - 1) + position = LineStart(line+1); + else + position = LineStart(line+1) - 1; + if (position > 0 && (doc.CharAt(position-1) == '\r' || doc.CharAt(position-1) == '\n')) { + position--; + } + return position; +} + +int Scintilla::VCHomePosition(int position) { + int line = LineFromPosition(position); + int startPosition = LineStart(line); + int endLine = LineStart(line+1) - 1; + int startText = startPosition; + while (startText < endLine && (doc.CharAt(startText) == ' ' || doc.CharAt(startText) == '\t' ) ) + startText++; + if (position == startText) + return startPosition; + else + return startText; +} + +int Scintilla::MovePositionTo(int newPos, bool extend) { + int delta = newPos - currentPos; + newPos = ClampPositionIntoDocument(newPos); + newPos = MovePositionOutsideChar(newPos, delta); + if (extend) { + SetSelection(newPos); + } else { + SetSelection(newPos, newPos); + } + ShowCaretAtCurrentPosition(); + EnsureCaretVisible(); + return 0; +} + +// Choose the x position that the caret will try to stick to as it is moves up and down +void Scintilla::SetLastXChosen() { + POINT pt = LocationFromPosition(currentPos); + lastXChosen = pt.x; +} + +void Scintilla::GetTextRect(RECT *prc) { + GetClientRectangle(prc); + prc->left += fixedColumnWidth; +} + +void Scintilla::ScrollTo(int line) { + if (line < 0) + line = 0; + if (line > MaxScrollPos()) + line = MaxScrollPos(); + topLine = clamp(line, 0, MaxScrollPos()); + SetVertScrollFromTopLine(); + Redraw(); +} + +void Scintilla::HorizontalScrollTo(int xPos) { + //dprintf("HorizontalScroll %d\n", xPos); + xOffset = xPos; + if (xOffset < 0) + xOffset = 0; +#ifdef GTK + gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset / 2); +#else + SetScrollPos(hwnd,SB_HORZ,xOffset,TRUE); +#endif + Redraw(); +} + +void Scintilla::EnsureCaretVisible() { + //dprintf("EnsureCaretVisible %d\n", xOffset); + RECT rcClient = {0,0,0,0}; + GetTextRect(&rcClient); + POINT pt = LocationFromPosition(currentPos); + POINT ptBottomCaret = pt; + int lineCaret = LineFromPosition(currentPos); + ptBottomCaret.y += lineHeight; + if (!PtInRect(&rcClient,pt) || !PtInRect(&rcClient,ptBottomCaret)) { + //dprintf("EnsureCaretVisible move, (%d,%d) (%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right); + if (topLine > lineCaret) { + ScrollTo(lineCaret); + } else if (topLine < (lineCaret - LinesToScroll())) { + ScrollTo(lineCaret - LinesToScroll()); + } + // The 2s here are to ensure the caret is really visible + if (pt.x < rcClient.left) { + xOffset = xOffset - (rcClient.left - pt.x) - 2; + } else if (pt.x > rcClient.right) { + xOffset = xOffset + (pt.x - rcClient.right) + 2; + } + if (xOffset < 0) + xOffset = 0; +#ifdef GTK + gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset / 2); +#else + SetScrollPos(hwnd,SB_HORZ,xOffset,TRUE); +#endif + Redraw(); + } +} + +int Scintilla::SelectionStart() { + return min(currentPos, anchor); +} + +int Scintilla::SelectionEnd() { + return max(currentPos, anchor); +} + +#ifndef GTK +HGLOBAL Scintilla::GetSelText() { + int bytes = 0; + int startPos = SelectionStart(); + bytes = SelectionEnd() - startPos; + HGLOBAL hand = 0; + LPSTR ptr; + hand = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,bytes+1); + if (hand) { + ptr = (LPSTR)GlobalLock(hand); + for (int i=0;i 0) { + RECT rcClient; + GetClientRectangle(&rcClient); + rcClient.right = fixedColumnWidth; +#ifdef GTK + GdkRectangle update_rect; + update_rect.x = rcClient.left; + update_rect.y = rcClient.top; + update_rect.width = fixedColumnWidth; + update_rect.height = rcClient.bottom - rcClient.top + 1; + gtk_widget_queue_draw_area(draw, + update_rect.x, + update_rect.y, + update_rect.width, + update_rect.height); +#else + InvalidateRect(hwnd,&rcClient,FALSE); +#endif + } else { + Redraw(); + } +} + +void Scintilla::MoveCaret(int x, int y) { +#ifdef GTK + // Under GTK caret is displayed by expose method so this does nothing +#else + RECT rcClient; + GetClientRect(hwnd,&rcClient); + POINT ptTop = {x,y}; + POINT ptBottom = {x,y+lineHeight}; + bool caretVisible = PtInRect(&rcClient, ptTop) || PtInRect(&rcClient, ptBottom); + if (GetFocus() != hwnd) + caretVisible = false; + if (caret) { + if (caretVisible) { + POINT pt; + GetCaretPos(&pt); + // Only moving caret when it is really moving reduces flickering + if (pt.x != x || pt.y != y) { + if (inOverstrike) { + SetCaretPos(x,y + lineHeight - 2); + } else { + SetCaretPos(x,y); + } + } + } else { + DropCaret(); + } + } else { + if (caretVisible) { + if (inOverstrike) { + CreateCaret(hwnd, 0, aveCharWidth-1, 2); + SetCaretPos(x,y + lineHeight - 2); + } else { + CreateCaret(hwnd, 0, 1, lineHeight); + SetCaretPos(x,y); + } + ShowCaret(hwnd); + caret = true; + } + } +#endif +} + +void Scintilla::ShowCaretAtCurrentPosition() { +#ifndef GTK + POINT ptCaret = LocationFromPosition(currentPos); + MoveCaret(ptCaret.x, ptCaret.y); +#endif +} + +void Scintilla::DropCaret() { +#ifndef GTK + if (caret) { + HideCaret(hwnd); + DestroyCaret(); + caret = false; + } +#endif +} + +void Scintilla::PaintSelMargin(Surface *surfWindow, RECT &rc) { + if (fixedColumnWidth == 0) + return; + + RECT rcMargin = {0,0,0,0}; + GetClientRectangle(&rcMargin); + rcMargin.right = fixedColumnWidth; + + RECT rcSelMargin = rcMargin; + rcSelMargin.left = lineNumberWidth; + + RECT rcis; + Surface surface; +#ifdef GTK + if (bufferedDraw) { + if (!pixmapSelMargin) { + pixmapSelMargin = gdk_pixmap_new(hwnd->window, + fixedColumnWidth, + rcMargin.bottom - rcMargin.top, + -1); + } + surface.Init(surfWindow->hwnd, pixmapSelMargin, surfWindow->gc); + } else { + surface.Init(surfWindow->hwnd, surfWindow->hwnd->window, surfWindow->gc); + } +#else + BOOL intersects = IntersectRect(&rcis, &rc, &rcMargin); + if (!intersects) + return; + + HBITMAP oldBM = 0; + if (bufferedDraw) { + if (NULL == bitmapSelMargin) { + bitmapSelMargin = CreateCompatibleBitmap( + surfWindow->hdc, + fixedColumnWidth, + rcMargin.bottom - rcMargin.top); + } + oldBM = static_cast(SelectObject(hdcBitmap, bitmapSelMargin)); + surface.Init(hdcBitmap); + } else { + surface.Init(surfWindow->hdc); + } + SetTextAlign(surface.hdc, TA_BASELINE); +#endif + + //dprintf("Scintilla cleared sel %d %d %d %d\n", rcSelMargin.left, rcSelMargin.top, rcSelMargin.right, rcSelMargin.bottom); +#ifdef GTK + surface.FillRectangle(rcSelMargin, selbar.allocated); +#else + // Required because of special way brush is created for selection margin + FillRect(surface.hdc, &rcSelMargin, selmarginbrush); +#endif + + { // Scope the line and yposScreen variables + int line = topLine; + int yposScreen = 0; + + while (line < LinesTotal() && yposScreen < rcMargin.bottom) { + int marks = doc.GetMark(line); + if (marks) { + RECT rcMarker; + rcMarker.left = 1 + lineNumberWidth; + rcMarker.top = yposScreen + 1; + rcMarker.right = lineNumberWidth + selMarginWidth - 1; + rcMarker.bottom = yposScreen + lineHeight - 1; + for (int markBit=0;(markBit < 32) && marks; markBit++) { + if (marks & 1) { + markers[markBit].Draw(&surface, rcMarker); + } + marks >>= 1; + } + } + line++; + yposScreen += lineHeight; + } + } + + if (lineNumberWidth > 0) { + int line = topLine; + int ypos = 0; + + while (ypos < rcMargin.bottom) { + char number[100]; + number[0] = '\0'; + if (line < LinesTotal()) + sprintf(number, "%d", line+1); + int xpos = 0; + RECT rcNumber; + rcNumber.left = xpos; + rcNumber.right = xpos + lineNumberWidth; + rcNumber.top = ypos; + rcNumber.bottom = ypos + lineHeight; + surface.FillRectangle(rcNumber,selbar.allocated); + // Right justify + int width = surface.WidthText(styles[0].font, number, strlen(number)); + xpos += lineNumberWidth - width - 3; + rcNumber.left = xpos; +#ifdef GTK + // Different y parameters because Windows is drawing text at base line, GTK+ from bottom + surface.DrawText(rcNumber, styles[0].font, + ypos + lineHeight - styles[0].descent, + number, strlen(number), + linenumfore.allocated, selbar.allocated); +#else + surface.DrawText(rcNumber, styles[0].font, + ypos + maxAscent, + number, strlen(number), + linenumfore.allocated, selbar.allocated); +#endif + line++; + ypos += lineHeight; + } + } + + if (bufferedDraw) { +#ifdef GTK + gdk_draw_pixmap(surfWindow->hwnd->window, + surfWindow->hwnd->style->fg_gc[GTK_WIDGET_STATE (hwnd)], + pixmapSelMargin, + 0, 0, + 0, 0, + fixedColumnWidth, rcMargin.bottom - rcMargin.top); +#else + BitBlt(surfWindow->hdc,rcMargin.left, rcMargin.top, rcMargin.right, rcMargin.bottom, + hdcBitmap, 0, 0, SRCCOPY); + SelectObject(hdcBitmap, oldBM); +#endif + } +} + +#ifndef GTK +void Scintilla::CreateGraphicObjects(HDC hdc) { + hdcBitmap = CreateCompatibleDC(hdc); + + HBITMAP selMap = CreateCompatibleBitmap(hdc,8,8); + oldBitmap = static_cast(SelectObject(hdcBitmap, selMap)); + + // This complex procedure is to reproduce the checker board dithered pattern used by windows + // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half + // way between the chrome colour and the chrome highlight colour making a nice transition + // between the window chrome and the content area. And it works in low colour depths. + COLORREF highlight = GetSysColor(COLOR_3DHILIGHT); + if (highlight == RGB(0xff,0xff,0xff)) { + RECT rcPattern = {0,0,8,8}; + FillRect(hdcBitmap,&rcPattern,(HBRUSH)GetStockObject(WHITE_BRUSH)); + HPEN penSel = CreatePen(0,1,GetSysColor(COLOR_3DFACE)); + HPEN penOld = static_cast(SelectObject(hdcBitmap, penSel)); + for (int stripe=0;stripe<8;stripe++) { + MoveToEx(hdcBitmap, 0, stripe * 2, 0); + LineTo(hdcBitmap, 8, stripe * 2 - 8); + } + selmarginbrush = CreatePatternBrush(selMap); + + SelectObject(hdcBitmap, penOld); + DeleteObject(penSel); + } else { + // User has choen an unusual chrome colour scheme so just use the highlight edge colour. + selmarginbrush = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT)); + } + + RECT rcClient = {0,0,0,0}; + GetClientRect(hwnd,&rcClient); + bitmapLineBuffer = CreateCompatibleBitmap(hdc, rcClient.right - rcClient.left, lineHeight); + SelectObject(hdcBitmap, bitmapLineBuffer); + DeleteObject(selMap); +} +#endif + +void Scintilla::Paint(RECT rcPaint) { + + RefreshStyleData(); + +#ifdef GTK + if (bufferedDraw) { + if (!pixmapLine) { + pixmapLine = gdk_pixmap_new(draw->window, + draw->allocation.width, + lineHeight, + -1); + } + } +#else + PAINTSTRUCT ps; + BeginPaint(hwnd,&ps); + if (NULL == hdcBitmap) { + CreateGraphicObjects(ps.hdc); + } + + HPALETTE hpalSaveBM = palette.SelectInto(hdcBitmap,GetFocus() != hwnd); + HPALETTE hpalSave = palette.SelectInto(ps.hdc,TRUE); + if (palette.allowRealization) { + RealizePalette(ps.hdc); + RealizePalette(hdcBitmap); + } + rcPaint = ps.rcPaint; +#endif + //dprintf("Paint: (%3d,%3d) ... (%3d,%3d) %d\n", + // rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom); + + RECT rcClient = {0,0,0,0}; + GetClientRectangle(&rcClient); + //dprintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", + // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + + int screenLinePaintFirst = rcPaint.top / lineHeight; + int linePaintLast = topLine + rcPaint.bottom / lineHeight + 1; + int endPosPaint = Length(); + if (linePaintLast < LinesTotal()) + endPosPaint = LineStart(linePaintLast+1)-1; + + if (endPosPaint > endStyled) { + // Notify container to do some more styling + NotifyStyleNeeded(endPosPaint); + } + +#ifdef GTK + GdkGC *gc = gdk_gc_new(hwnd->window); +#endif + + int sty = 0; + char segment[maxLineLength]; + int xpos = fixedColumnWidth; + int ypos = 0; + if (!bufferedDraw) + ypos += screenLinePaintFirst * lineHeight; + int yposScreen = screenLinePaintFirst * lineHeight; + + //dprintf("start display %d tabWidth = %d, margin = %d offset = %d\n", doc.Length(), tabWidth, selMarginWidth, xOffset); + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); { + Surface marginSurface; +#ifdef GTK + marginSurface.Init(draw, draw->window, gc); +#else + marginSurface.Init(ps.hdc); +#endif + PaintSelMargin(&marginSurface,rcPaint); + } + + Surface *surface = new Surface(); + if (rcPaint.right > selMarginWidth) { + +#ifdef GTK + GdkDrawable *hdcShow = draw->window; + if (bufferedDraw) { + hdcShow = pixmapLine; + } + surface->Init(draw, hdcShow, gc); +#else + HDC hdcShow = ps.hdc; + if (bufferedDraw) { + SelectObject(hdcBitmap, bitmapSelMargin); + hdcShow = hdcBitmap; + } + surface->Init(hdcShow); + SelectObject(hdcBitmap, bitmapLineBuffer); +#endif + + int line = topLine + screenLinePaintFirst; + + // Remove selection margin from drawing area so text will not be drawn + // on it in unbuffered mode. +#ifndef GTK + IntersectClipRect(ps.hdc, fixedColumnWidth, 0, 32000, 32000); +#endif + while (line < LinesTotal() && yposScreen < rcPaint.bottom) { + + int marks = 0; + COLORREF markBack = RGB(0,0,0); + if (selMarginWidth == 0) { + marks = doc.GetMark(line); + if (marks) { + for (int markBit=0;(markBit < 32) && marks; markBit++) { + if (marks & 1) { + markBack = markers[markBit].back.allocated; + } + marks >>= 1; + } + } + marks = doc.GetMark(line); + } + + segment[0] = '\0'; + int segPos = 0; + + int posLineStart = LineStart(line); + int posLineEnd = LineStart(line+1); + //dprintf("line %d %d - %d\n", line, posLineStart, posLineEnd); + + RECT rcBlank; + rcBlank.top = ypos; + rcBlank.bottom = ypos + lineHeight; + + bool inSelection = posLineStart > selStart && posLineStart < selEnd; + +#ifndef GTK + SetTextAlign(hdcShow, TA_BASELINE); +#endif + + int indicatorsSet = 0; + if (posLineStart < posLineEnd) + indicatorsSet = doc.StyleAt(posLineStart) & INDICS_MASK; + + int prevIndic = doc.StyleAt(posLineStart) & INDICS_MASK; + + + int indStart[INDIC_MAX+1] = {0}; + for (int indica=0; indica <= INDIC_MAX; indica++) + indStart[indica] = fixedColumnWidth; + + char chPrev = '\0'; + bool visibleInSelection = false; + int ch = ' '; + int colour = -1; + + int xposCaret = -1; + + for (int i=posLineStart;i<=posLineEnd;i++) { + //dprintf("pos %d\n", line, posLineStart, posLineEnd); + if (i < posLineEnd) { // Do not index onto next line + ch = doc.CharAt(i); + colour = doc.StyleAt(i); + } + + // If there is the end of a style run for any reason + if (colour != sty || (ch == '\t') || (chPrev == '\t') || (i == selStart) || (i == selEnd) || (i == posLineEnd)) { + int styleMain = sty & 31; + // text appears not to have a background color, so draw backing rect first + COLORREF textBack = styles[styleMain].back.allocated; + COLORREF textFore = styles[styleMain].fore.allocated; + if (inSelection && !hideSelection) { + if (selbackset) + textBack = selbackground.allocated; + if (selforeset) + textFore = selforeground.allocated; + visibleInSelection = true; + } else { + if (marks) + textBack = markBack; + } + unsigned int width = 0; + if (segment[0] == '\t') { + int nextTab = (((xpos + 2 - fixedColumnWidth) / tabWidth) + 1) * tabWidth + + fixedColumnWidth; + rcBlank.left = xpos - xOffset; + rcBlank.right = nextTab - xOffset; + surface->FillRectangle(rcBlank, textBack); + if (viewWhitespace) { + RECT rcTab; + rcTab.left = rcBlank.left + 1 - xOffset; + rcTab.top = rcBlank.top + 4; + rcTab.right = rcBlank.right - 1 - xOffset; + rcTab.bottom = rcBlank.bottom - maxDescent; + int ymid = ypos + lineHeight/2; + int ydiff = (rcTab.bottom - rcTab.top) / 2; + int xhead = rcTab.right - 1 - ydiff; + surface->PenColor(textFore); + if ((rcTab.left + 2) < (rcTab.right - 1)) + surface->MoveTo(rcTab.left + 2, ymid); + else + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid - ydiff); + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid + ydiff); + } + width = nextTab - xpos; + } else { + //dprintf("pos %d %d %s\n", line, i, segment); + width = surface->WidthText(styles[styleMain].font, segment, segPos); + rcBlank.left = xpos - xOffset; + rcBlank.right = xpos + width - xOffset; + rcBlank.top = ypos; + rcBlank.bottom = ypos + lineHeight; +#ifdef GTK + // Different y parameters because Windows is drawing text at base line, GTK+ from bottom + surface->DrawText(rcBlank, styles[styleMain].font, + ypos + lineHeight - styles[styleMain].descent, segment, segPos, + textFore, textBack); +#else + surface->DrawText(rcBlank, styles[styleMain].font, + ypos + maxAscent, segment, segPos, + textFore, textBack); +#endif + if (viewWhitespace) { + surface->PenColor(textFore); + int xx = xpos; + for (int cpos=0;cpos < segPos;cpos++) { + int szc = surface->WidthChar(styles[styleMain].font, segment[cpos]); + if (segment[cpos] == ' ') { + surface->MoveTo(xx + szc / 2 - xOffset, ypos + lineHeight/2); + surface->LineTo(xx + szc / 2 - xOffset + 1, ypos + lineHeight/2); + } + xx += szc; + } + } + } + segPos = 0; + segment[segPos] = '\0'; + xpos += width; + sty = colour; + + if (i == selStart) + inSelection = true; + + if (i == selEnd) + inSelection = false; + + if (i == currentPos && (iFillRectangle(rcBlank, selbackground.allocated); + } else if (marks) { + surface->FillRectangle(rcBlank, markBack); + } else { + surface->FillRectangle(rcBlank, background.allocated); + } + + rcBlank.left = xpos + aveCharWidth - xOffset; + rcBlank.right = rcClient.right; + if (marks) { + surface->FillRectangle(rcBlank, markBack); + } else { + surface->FillRectangle(rcBlank, background.allocated); + } + +#ifdef GTK + // TODO: Add optional blinking here, enable for windows and make this the only + // caret drawing code. + if (xposCaret >= 0) { + rcBlank.left = xposCaret - xOffset; + rcBlank.right = xposCaret - xOffset + 1; + surface->FillRectangle(rcBlank, caretcolour.allocated); + } +#endif + if (bufferedDraw) { +#ifdef GTK + gdk_draw_pixmap(draw->window, + draw->style->fg_gc[GTK_WIDGET_STATE (hwnd)], + pixmapLine, + fixedColumnWidth, 0, + fixedColumnWidth, yposScreen, + draw->allocation.width, lineHeight); +#else + BitBlt(ps.hdc,fixedColumnWidth,yposScreen,rcClient.right - rcClient.left, + lineHeight+1,hdcBitmap,fixedColumnWidth,0,SRCCOPY); +#endif + } + + if (!bufferedDraw) { + ypos += lineHeight; + } + + yposScreen += lineHeight; + xpos = fixedColumnWidth; + line++; + } + RECT rcBeyondEOF = rcClient; + rcBeyondEOF.left = fixedColumnWidth; + rcBeyondEOF.right = rcBeyondEOF.right; + rcBeyondEOF.top = (LinesTotal() - topLine) * lineHeight; + if (rcBeyondEOF.top < rcBeyondEOF.bottom) { + Surface endSurface; +#ifdef GTK + endSurface.Init(draw, draw->window, gc); +#else + endSurface.Init(ps.hdc); +#endif + endSurface.FillRectangle(rcBeyondEOF, background.allocated); + } + } + + delete surface; +#ifdef GTK + gdk_gc_unref(gc); +#else + if (hpalSaveBM) + SelectPalette(hdcBitmap,hpalSaveBM,TRUE); + if (hpalSave) + SelectPalette(ps.hdc,hpalSave,TRUE); + EndPaint(hwnd,&ps); + + ShowCaretAtCurrentPosition(); +#endif +} + +void Scintilla::RealizeWindowPalette(bool inBackGround) { +#ifdef GTK +#else + RefreshStyleData(); + HDC hdc = GetDC(hwnd); + HPALETTE hpalSave = palette.SelectInto(hdc, inBackGround); + if (hpalSave) { + int changes = RealizePalette(hdc); + SelectPalette(hdc, hpalSave, TRUE); + if (changes > 0) + Redraw(); + } + ReleaseDC(hwnd,hdc); +#endif +} + +void Scintilla::ModifiedAt(int pos) { + if (endStyled > pos) + endStyled = pos; +} + +// Document only modified by gateways DeleteChars, InsertStyledString, Undo, Redo, and SetStyleAt. +// SetStyleAt does not change the persistent state of a document + +// Unlike Undo, Redo, and InsertStyledString, the pos argument is a cell number not a char number +void Scintilla::DeleteChars(int pos, int len) { + if (doc.IsReadOnly()) + NotifyModifyAttempt(); + if (!doc.IsReadOnly()) { + bool startSavePoint = doc.IsSavePoint(); + doc.DeleteChars(pos*2, len * 2); + if (startSavePoint && doc.IsCollectingUndo()) + NotifySavePoint(!startSavePoint); + ModifiedAt(pos); + NotifyChange(); + SetScrollBars(); + } +} + +void Scintilla::InsertStyledString(int position, char *s, int insertLength) { + if (doc.IsReadOnly()) + NotifyModifyAttempt(); + if (!doc.IsReadOnly()) { + bool startSavePoint = doc.IsSavePoint(); + doc.InsertString(position, s, insertLength); + if (startSavePoint && doc.IsCollectingUndo()) + NotifySavePoint(!startSavePoint); + ModifiedAt(position / 2); + NotifyChange(); + SetScrollBars(); + } +} + +void Scintilla::Undo() { + if (doc.CanUndo()) { + bool startSavePoint = doc.IsSavePoint(); + int earliestMod = Length(); + int newPos = doc.Undo(&earliestMod) / 2; + SetSelection(newPos, newPos); + EnsureCaretVisible(); + ModifiedAt(earliestMod / 2); + NotifyChange(); + Redraw(); + bool endSavePoint = doc.IsSavePoint(); + if (startSavePoint != endSavePoint) + NotifySavePoint(endSavePoint); + SetScrollBars(); + } +} + +void Scintilla::Redo() { + if (doc.CanRedo()) { + bool startSavePoint = doc.IsSavePoint(); + int earliestMod = Length(); + int newPos = doc.Redo(&earliestMod) / 2; + SetSelection(newPos, newPos); + EnsureCaretVisible(); + ModifiedAt(earliestMod / 2); + NotifyChange(); + Redraw(); + bool endSavePoint = doc.IsSavePoint(); + if (startSavePoint != endSavePoint) + NotifySavePoint(endSavePoint); + SetScrollBars(); + } +} + +void Scintilla::InsertChar(int pos, char ch) { + char chs[2]; + chs[0] = ch; + chs[1] = 0; + InsertStyledString(pos*2, chs, 2); +} + +// Insert a null terminated string +void Scintilla::InsertString(int position, char *s) { + InsertString(position, s, strlen(s)); +} + +// Insert a string with a length +void Scintilla::InsertString(int position, char *s, int insertLength) { + char *sWithStyle = new char[insertLength*2]; + if (sWithStyle) { + for (int i=0; i(&scn)); +#endif +} + +void Scintilla::NotifyChar(char ch) { + SCNotification scn; + scn.nmhdr.hwndFrom = hwnd; + scn.nmhdr.idFrom = ctrlID; + scn.nmhdr.code = SCN_CHARADDED; + scn.ch = ch; +#ifdef GTK + gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], + ctrlID, &scn); +#else + SendMessage(GetParent(hwnd), WM_NOTIFY, + GetDlgCtrlID(hwnd), reinterpret_cast(&scn)); +#endif +} + +void Scintilla::NotifySavePoint(bool isSavePoint) { + SCNotification scn; + scn.nmhdr.hwndFrom = hwnd; + scn.nmhdr.idFrom = ctrlID; + if (isSavePoint) { + scn.nmhdr.code = SCN_SAVEPOINTREACHED; + } else { + scn.nmhdr.code = SCN_SAVEPOINTLEFT; + } +#ifdef GTK + gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], + ctrlID, &scn); +#else + SendMessage(GetParent(hwnd), WM_NOTIFY, + GetDlgCtrlID(hwnd), reinterpret_cast(&scn)); +#endif +} + +void Scintilla::NotifyModifyAttempt() { + SCNotification scn; + scn.nmhdr.hwndFrom = hwnd; + scn.nmhdr.idFrom = ctrlID; + scn.nmhdr.code = SCN_MODIFYATTEMPTRO; +#ifdef GTK + gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], + ctrlID, &scn); +#else + SendMessage(GetParent(hwnd), WM_NOTIFY, + GetDlgCtrlID(hwnd), reinterpret_cast(&scn)); +#endif +} + +void Scintilla::NotifyKey(int key, int modifiers) { +#ifdef GTK + SCNotification scn; + scn.nmhdr.hwndFrom = hwnd; + scn.nmhdr.idFrom = ctrlID; + scn.nmhdr.code = SCN_KEY; + scn.ch = key; + scn.modifiers = modifiers; + + gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], + ctrlID, &scn); +#endif +} + +void Scintilla::Indent(bool forwards) { + int lineAnchor = LineFromPosition(anchor); + int lineCurrentPos = LineFromPosition(currentPos); + if (lineAnchor == lineCurrentPos) { + ClearSelection(); + InsertChar(currentPos++, '\t'); + SetSelection(currentPos, currentPos); + } else { + int anchorPosOnLine = anchor - LineStart(lineAnchor); + int currentPosPosOnLine = currentPos - LineStart(lineCurrentPos); + // Multiple lines selected so indent / dedent + int lineTopSel = min(lineAnchor, lineCurrentPos); + int lineBottomSel = max(lineAnchor, lineCurrentPos); + if (LineStart(lineBottomSel) == anchor || LineStart(lineBottomSel) == currentPos) + lineBottomSel--; // If not selecting any characters on a line, do not indent + if (!forwards) { + // Dedent - suck white space off the front of the line to dedent by equivalent of a tab + for (int line=lineBottomSel; line >= lineTopSel; line--) { + int ispc = 0; + while (ispc < tabInChars && doc.CharAt(LineStart(line) + ispc) == ' ') + ispc++; + int posStartLine = LineStart(line); + if (ispc == tabInChars) { + DeleteChars(posStartLine,ispc); + } else if (doc.CharAt(posStartLine + ispc) == '\t') { + DeleteChars(posStartLine,ispc+1); + } else { // Hit a non-white + DeleteChars(posStartLine,ispc); + } + } + } else { + // Indent by a tab + for (int line=lineBottomSel; line >= lineTopSel; line--) { + InsertChar(LineStart(line), '\t'); + } + } + if (lineAnchor < lineCurrentPos) { + if (currentPosPosOnLine == 0) + SetSelection(LineStart(lineCurrentPos), LineStart(lineAnchor)); + else + SetSelection(LineStart(lineCurrentPos+1), LineStart(lineAnchor)); + } else { + if (anchorPosOnLine == 0) + SetSelection(LineStart(lineCurrentPos), LineStart(lineAnchor)); + else + SetSelection(LineStart(lineCurrentPos), LineStart(lineAnchor+1)); + } + } +} + +static bool iswordchar(char ch) { + return isalnum(ch) || ch == '_'; +} + +int Scintilla::ExtendWordSelect(int pos, int delta) { + int newPos = pos; + if (delta < 0) { + while (newPos > 0 && iswordchar(doc.CharAt(newPos-1))) + newPos--; + } else { + while (newPos < (Length()-1) && iswordchar(doc.CharAt(newPos))) + newPos++; + } + return newPos; +} + +int Scintilla::NextWordStart(int delta) { + int newPos = currentPos; + if (delta < 0) { + while (newPos > 0 && (doc.CharAt(newPos-1) == ' ' || doc.CharAt(newPos-1) == '\t')) + newPos--; + if (isspace(doc.CharAt(newPos-1))) { // Back up to previous line + while (newPos > 0 && isspace(doc.CharAt(newPos-1))) + newPos--; + } else { + bool startAtWordChar = iswordchar(doc.CharAt(newPos-1)); + while (newPos > 0 && !isspace(doc.CharAt(newPos-1)) && (startAtWordChar == iswordchar(doc.CharAt(newPos-1)))) + newPos--; + } + } else { + bool startAtWordChar = iswordchar(doc.CharAt(newPos)); + while (newPos < (Length()-1) && isspace(doc.CharAt(newPos))) + newPos++; + while (newPos < (Length()-1) && !isspace(doc.CharAt(newPos)) && (startAtWordChar == iswordchar(doc.CharAt(newPos)))) + newPos++; + while (newPos < (Length()-1) && (doc.CharAt(newPos) == ' ' || doc.CharAt(newPos) == '\t')) + newPos++; + } + return newPos; +} + +POINT MakePoint(int x, int y) { + POINT pt = {x, y}; + return pt; +} + +int Scintilla::KeyCommand(WORD iMessage) { + POINT pt = LocationFromPosition(currentPos); + + // Most key commands cancel autocompletion mode + if (inAutoCompleteMode) { + switch (iMessage) { + // Except for these + case SCI_LINEDOWN: + case SCI_LINEUP: + case SCI_DELETEBACK: + case SCI_TAB: + break; + + default: + AutoCompleteCancel(); + } + } + + switch (iMessage) { + case SCI_LINEDOWN: + if (inAutoCompleteMode) { + AutoCompleteMove(1); + } else { + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y + lineHeight))); + } + return 0; + case SCI_LINEDOWNEXTEND: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y + lineHeight)), true); + case SCI_LINEUP: + if (inAutoCompleteMode) { + AutoCompleteMove(-1); + } else { + MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y - lineHeight))); + } + return 0; + case SCI_LINEUPEXTEND: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y - lineHeight)), true); + case SCI_CHARLEFT: + MovePositionTo(currentPos - 1); + SetLastXChosen(); + return 0; + case SCI_CHARLEFTEXTEND: + MovePositionTo(currentPos - 1, true); + SetLastXChosen(); + return 0; + case SCI_CHARRIGHT: + MovePositionTo(currentPos + 1); + SetLastXChosen(); + return 0; + case SCI_CHARRIGHTEXTEND: + MovePositionTo(currentPos + 1, true); + SetLastXChosen(); + return 0; + case SCI_WORDLEFT: + return MovePositionTo(NextWordStart(-1)); + case SCI_WORDLEFTEXTEND: + return MovePositionTo(NextWordStart(-1), true); + case SCI_WORDRIGHT: + return MovePositionTo(NextWordStart(1)); + case SCI_WORDRIGHTEXTEND: + return MovePositionTo(NextWordStart(1), true); + case SCI_HOME: + MovePositionTo(LineStart(LineFromPosition(currentPos))); + SetLastXChosen(); + return 0; + case SCI_HOMEEXTEND: + MovePositionTo(LineStart(LineFromPosition(currentPos)), true); + SetLastXChosen(); + return 0; + case SCI_LINEEND: + MovePositionTo(LineEndPosition(currentPos)); + SetLastXChosen(); + return 0; + case SCI_LINEENDEXTEND: + MovePositionTo(LineEndPosition(currentPos), true); + SetLastXChosen(); + return 0; + case SCI_DOCUMENTSTART: + MovePositionTo(0); + SetLastXChosen(); + return 0; + case SCI_DOCUMENTSTARTEXTEND: + MovePositionTo(0, true); + SetLastXChosen(); + return 0; + case SCI_DOCUMENTEND: + MovePositionTo(Length()); + SetLastXChosen(); + return 0; + case SCI_DOCUMENTENDEXTEND: + MovePositionTo(Length(), true); + SetLastXChosen(); + return 0; + case SCI_PAGEUP: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y - lineHeight * LinesToScroll()))); + case SCI_PAGEUPEXTEND: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y - lineHeight * LinesToScroll())), true); + case SCI_PAGEDOWN: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y + lineHeight * LinesToScroll()))); + case SCI_PAGEDOWNEXTEND: + return MovePositionTo(PositionFromLocation(MakePoint(lastXChosen, pt.y + lineHeight * LinesToScroll())), true); + case SCI_EDITTOGGLEOVERTYPE: + inOverstrike = !inOverstrike; + DropCaret(); + ShowCaretAtCurrentPosition(); + return 0; + case SCI_CANCEL: + CallTipCancel(); + return 0; + case SCI_DELETEBACK: + DelCharBack(); + if (inAutoCompleteMode) + AutoCompleteChanged(); + if (inCallTipMode && (posStartCallTip > currentPos)) + CallTipCancel(); + NotifyChange(); + EnsureCaretVisible(); + break; + case SCI_TAB: + if (inAutoCompleteMode) { + AutoCompleteCompleted(); + } else { + Indent(true); + } + NotifyChange(); + break; + case SCI_BACKTAB: + Indent(false); + break; + case SCI_NEWLINE: + AutoCompleteCancel(); + CallTipCancel(); + ClearSelection(); + if (eolMode == SC_EOL_CRLF) { + InsertString(currentPos, "\r\n"); + SetSelection(currentPos+2, currentPos+2); + } else if (eolMode == SC_EOL_CR) { + InsertChar(currentPos, '\r'); + SetSelection(currentPos+1, currentPos+1); + } else if (eolMode == SC_EOL_LF) { + InsertChar(currentPos, '\n'); + SetSelection(currentPos+1, currentPos+1); + } + EnsureCaretVisible(); + break; + case SCI_FORMFEED: + AddChar('\f'); + break; + case SCI_VCHOME: + MovePositionTo(VCHomePosition(currentPos)); + SetLastXChosen(); + return 0; + case SCI_VCHOMEEXTEND: + MovePositionTo(VCHomePosition(currentPos), true); + SetLastXChosen(); + break; + + } + return 0; +} + +void Scintilla::AssignCmdKey(int key, int modifiers, int msg) { + if ((keymapLen+1) >= keymapAlloc) { + KeyToCommand *ktcNew = new KeyToCommand[keymapAlloc + 5]; + if (!ktcNew) + return; + for (int k=0;k 0) { + if (IsCrLf(currentPos-2)) { + DeleteChars(currentPos-2,2); + SetSelection(currentPos-2, currentPos-2); + } else { + DeleteChars(currentPos-1,1); + SetSelection(currentPos-1, currentPos-1); + } + } + NotifyChange(); +} + +void Scintilla::AddChar(char ch) { + bool wasSelection = currentPos != anchor; + ClearSelection(); + if (inOverstrike && !wasSelection) { + if (currentPos < (Length() - 1)) { + if ((doc.CharAt(currentPos) != '\r') && (doc.CharAt(currentPos) != '\n')) { + DeleteChars(currentPos,1); + } + } + } + InsertChar(currentPos, ch); + SetSelection(currentPos+1, currentPos+1); + if (inAutoCompleteMode) + AutoCompleteChanged(ch); + EnsureCaretVisible(); + NotifyChange(); + Redraw(); + NotifyChar(ch); +} + +static bool Close(POINT pt1, POINT pt2) { + if (abs(pt1.x - pt2.x) > 3) + return false; + if (abs(pt1.y - pt2.y) > 3) + return false; + return true; +} + +static POINT PointFromLparam(LPARAM lParam) { + POINT pt; + pt.x = (int)(short)LOWORD(lParam); + pt.y = (int)(short)HIWORD(lParam); + return pt; +} + +COLORREF ColourFromLparam(LPARAM lParam) { +#ifdef GTK + return RGB(lParam >> 16, (lParam >> 8) & 0xff, lParam & 0xff); +#else + return lParam; +#endif +} + +LPARAM LparamFromColour(COLORREF col) { +#ifdef GTK + return col.pixel; +#else + return col; +#endif +} + +void Scintilla::ButtonDown(POINT pt, unsigned int curTime, bool shift) { + AutoCompleteCancel(); + CallTipCancel(); + Capture(); +#ifdef GTK + gtk_widget_grab_focus(GTK_WIDGET(sci)); +#endif + //dprintf("Scintilla:ButtonDown %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime); + int newPos = PositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + if (shift) + SetSelection(newPos); + else + SetSelection(newPos, newPos); + if ((curTime - lastClickTime) < GetDoubleClickTime() && Close(pt,lastClick)) { + // Stop mouse button bounce changing selection type + if (curTime != lastClickTime) { + if (selType == selChar) { +#ifndef GTK + // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too. + SendMessage(hwnd, WM_LBUTTONDBLCLK, + shift ? MK_SHIFT : 0, + MAKELPARAM(pt.x, pt.y)); +#endif + selType = selWord; + } else if (selType == selWord) { + selType = selLine; + } else { + selType = selChar; + } + } + + if (selType == selWord) { + if (currentPos >= originalAnchorPos) { // Moved forward + SetSelection(ExtendWordSelect(currentPos, 1), + ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(ExtendWordSelect(currentPos, -1), + ExtendWordSelect(originalAnchorPos, 1)); + } + } else if (selType == selLine) { + lineAnchor = LineFromLocation(pt); + SetSelection(LineStart(lineAnchor+1), LineStart(lineAnchor)); + //dprintf("Triple click: %d - %d\n", anchor, currentPos); + } else { + SetSelection(currentPos, currentPos); + } + //dprintf("Double click: %d - %d\n", anchor, currentPos); + } else { + selType = selChar; + originalAnchorPos = currentPos; + } + lastClickTime = curTime; +#ifdef GTK + Redraw(); +#endif + lastXChosen = pt.x; + ShowCaretAtCurrentPosition(); +} + +void Scintilla::ButtonMove(POINT pt) { + //dprintf("Move %x\n", lParam); + if (capturedMouse) { + int movePos = PositionFromLocation(pt); + movePos = MovePositionOutsideChar(movePos, currentPos - movePos); + if (selType == selChar) { + SetSelection(movePos); + //dprintf("Move: %d - %d\n", anchor, currentPos); + } else if (selType == selWord) { + // continue selecting by word + if (currentPos > originalAnchorPos) { // Moved forward + SetSelection(ExtendWordSelect(movePos, 1), + ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(ExtendWordSelect(movePos, -1), + ExtendWordSelect(originalAnchorPos, 1)); + } + } else { + // continue selecting by line + //int lineMove = LineFromPosition(movePos); + int lineMove = LineFromLocation(pt); + if (lineAnchor < lineMove) { + SetSelection(LineStart(lineMove+1), + LineStart(lineAnchor)); + } else { + SetSelection(LineStart(lineAnchor+1), + LineStart(lineMove)); + } + } + EnsureCaretVisible(); + } +} + +void Scintilla::ButtonUp(POINT pt, unsigned int curTime) { + if (capturedMouse) { + Release(); + if (selType == selChar) { + int newPos = PositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + SetSelection(newPos); + //dprintf("Up: %d - %d\n", anchor, currentPos); + } + lastClickTime = curTime; + lastClick = pt; + lastXChosen = pt.x; + } +} + +void Scintilla::DeleteUndoHistory() { + doc.DeleteUndoHistory(); +} + +void Scintilla::Cut() { + Copy(); + ClearSelection(); + Redraw(); +} + +void Scintilla::Copy() { + //dprintf("Copy\n"); + if (currentPos != anchor) { +#ifdef GTK + free(pasteBuffer); + pasteBuffer = 0; + unsigned int bytes = 0; + int startPos = SelectionStart(); + bytes = SelectionEnd() - startPos; + pasteBuffer = reinterpret_cast(malloc(bytes+1)); + if (pasteBuffer) { + for (int i=0;itype == GDK_TARGET_STRING) { + if ((selection_data->selection == clipboard_atom) && + (selection_data->length > 0)) { + ClearSelection(); + char *ptr = reinterpret_cast(selection_data->data); + unsigned int bytes = selection_data->length; + //dprintf("Size = %d\n", bytes); + int len = bytes; + for (int i=0; i(pasteBuffer))); +} +#endif + +void Scintilla::Clear() { + if (currentPos == anchor) { + DelChar(); + } else { + ClearSelection(); + } + SetSelection(currentPos, currentPos); + Redraw(); +} + +void Scintilla::SelectAll() { + SetSelection(0, doc.Length()); + Redraw(); +} + +int Scintilla::KeyDown(int key, bool shift, bool ctrl, bool alt) { + int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + for (int keyIndex = 0; keyIndex < keymapLen; keyIndex++) { + if ((key == keymap[keyIndex].key) && (modifiers == keymap[keyIndex].modifiers)) { + return WndProc(keymap[keyIndex].msg, 0, 0); + } + } +#ifdef GTK + if (!ctrl && !alt && (key < 128)) { + AddChar(key); + } else { + // Pass up to container in case it is an accelerator + NotifyKey(key, modifiers); + } +#endif + return TRUE; +} + +void Scintilla::GoToLine(int lineNo) { + if (lineNo > LinesTotal()) + lineNo = LinesTotal(); + if (lineNo < 0) + lineNo = 0; + SetSelection(LineStart(lineNo), LineStart(lineNo)); + ShowCaretAtCurrentPosition(); + EnsureCaretVisible(); +} + +#ifdef GTK +void Scintilla::Resize(int width, int height) { + //dprintf("Resize %d %d\n", width, height); + DropGraphics(); + GtkAllocation alloc; + + alloc.x = 0; + alloc.y = 0; + alloc.width = width - 16; + alloc.height = height - 16; + gtk_widget_size_allocate(GTK_WIDGET(draw), &alloc); + + alloc.x = 0; + alloc.y = height - 16; + alloc.width = width - 16; + alloc.height = 16; + gtk_widget_size_allocate(GTK_WIDGET(scrollbarh), &alloc); + + alloc.x = width - 16; + alloc.y = 0; + alloc.width = 16; + alloc.height = height - 16; + gtk_widget_size_allocate(GTK_WIDGET(scrollbarv), &alloc); + + SetScrollBars(0,0); +} +#endif + +void Scintilla::SetScrollBars(LPARAM *plParam,WPARAM wParam) { + RefreshStyleData(); + RECT rsClient = {0,0, 0,0}; + +#ifndef GTK + if (plParam) { + rsClient.right = LOWORD(*plParam); + rsClient.bottom = HIWORD(*plParam); + } else +#endif + GetClientRectangle(&rsClient); + + int nMax = LinesTotal(); + int nPage = LinesTotal() - MaxScrollPos() + 1; +#ifdef GTK + int pageScroll = LinesToScroll(); + + if (GTK_ADJUSTMENT(adjustmentv)->upper != nMax || + GTK_ADJUSTMENT(adjustmentv)->page_size != nPage || + GTK_ADJUSTMENT(adjustmentv)->page_increment != pageScroll) { + GTK_ADJUSTMENT(adjustmentv)->upper = nMax; + GTK_ADJUSTMENT(adjustmentv)->page_size = nPage; + GTK_ADJUSTMENT(adjustmentv)->page_increment = pageScroll; + gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv)); + } +#else + SCROLLINFO sci = { + sizeof(sci) + }; + sci.fMask = SIF_PAGE|SIF_RANGE; + sci.nMin = 0; + sci.nMax = LinesTotal(); + sci.nPage = LinesTotal() - MaxScrollPos() + 1; + sci.nPos = 0; + sci.nTrackPos = 1; + int b = SetScrollInfo(hwnd, SB_VERT, &sci,TRUE); +#endif + +#ifdef GTK + if (GTK_ADJUSTMENT(adjustmenth)->upper != 2000 || + GTK_ADJUSTMENT(adjustmenth)->page_size != 20) { + GTK_ADJUSTMENT(adjustmenth)->upper = 2000; + GTK_ADJUSTMENT(adjustmenth)->page_size = 20; + gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth)); + } +#else + SetScrollRange(hwnd,SB_HORZ,0,2000,TRUE); +#endif + + // TODO: ensure always showing as many lines as possible + // May not be, if, for example, window made larger + if (topLine > MaxScrollPos()) { + topLine = clamp(topLine, 0, MaxScrollPos()); + SetVertScrollFromTopLine(); + Redraw(); + } + if (!plParam) + Redraw(); + //dprintf("end max = %d page = %d\n", nMax, nPage); +} + +void Scintilla::Capture() { + capturedMouse = true; +#ifdef GTK + //gtk_grab_add(GTK_WIDGET(sci)); +#else + SetCapture(hwnd); +#endif +} + +void Scintilla::Release() { + capturedMouse = false; +#ifdef GTK + //gtk_grab_remove(GTK_WIDGET(sci)); +#else + ReleaseCapture(); +#endif +} + +int Scintilla::LineFromPosition(int pos) { + return doc.lc.LineFromPosition(pos); +} + +POINT Scintilla::LocationFromPosition(int pos) { + RefreshStyleData(); + POINT pt = {0,0}; + int line = LineFromPosition(pos); + //dprintf("line=%d\n", line); + int xpos = fixedColumnWidth; + Surface surfaceMeasure; +#ifdef GTK + surfaceMeasure.Init(); +#else + surfaceMeasure.InitOnWindow(hwnd); +#endif + pt.y = (line - topLine) * lineHeight; // + half a lineheight? + unsigned int posLineStart = LineStart(line); + unsigned int posLineEnd = LineStart(line+1); + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + int retPos = posLineStart; + int sty = 0; + char segment[maxLineLength]; + segment[0] = '\0'; + int segPos = 0; + char chPrev = '\0'; + bool complete = false; + for (int i=posLineStart;i<=posLineEnd && !complete && (posLineEnd > posLineStart);i++) { + char ch = doc.CharAt(i); + int colour = doc.StyleAt(i); + unsigned int width = 0; + if (colour != sty || (ch == '\t') || (chPrev == '\t') || (i == selStart) || (i == selEnd) || (i == posLineEnd)) { + if (segment[0] == '\t') { + width = (((xpos + 2 - fixedColumnWidth) / tabWidth) + 1) * tabWidth + fixedColumnWidth - xpos; + if (i >= pos) { + xpos += width; + complete = true; + } + } else { + int styleMain = sty & 31; + if (i >= pos) { + int charsToMeasure = segPos - (i - pos); + width = surfaceMeasure.WidthText(styles[styleMain].font, segment, charsToMeasure); + xpos += width; + complete = true; + } else { + width = surfaceMeasure.WidthText(styles[styleMain].font, segment, segPos); + } + } + if (!complete) { + retPos += segPos; + segPos = 0; + segment[segPos] = '\0'; + xpos += width; + sty = colour; + } + } + if (ch != '\r' && ch != '\n' && ((segPos+1) < sizeof(segment))) + segment[segPos++] = ch; + chPrev = ch; + } + pt.x = xpos - xOffset; + return pt; +} + +int Scintilla::LineFromLocation(POINT pt) { + return pt.y / lineHeight + topLine; +} + +int Scintilla::Length() { + return doc.Length(); +} + +char Scintilla::CharAt(int pos) { + return doc.CharAt(pos); +} + +int Scintilla::CurrentPosition() { + return currentPos; +} + +int Scintilla::PositionFromLocation(POINT pt) { + pt.x += xOffset; + int line = pt.y / lineHeight + topLine; + if (line < 0) + return 0; + if (line >= doc.Lines()) + return Length(); + int xpos = fixedColumnWidth; + Surface surfaceMeasure; +#ifdef GTK + surfaceMeasure.Init(); +#else + surfaceMeasure.InitOnWindow(hwnd); +#endif + unsigned int posLineStart = LineStart(line); + unsigned int posLineEnd = LineStart(line+1); + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + int retPos = posLineStart; + int sty = 0; + char segment[maxLineLength]; + segment[0] = '\0'; + int segPos = 0; + char chPrev = '\0'; + bool complete = false; + for (int i=posLineStart;i<=posLineEnd && xpos < pt.x && !complete && (posLineEnd > posLineStart);i++) { + char ch = doc.CharAt(i); + int colour = doc.StyleAt(i); + unsigned int width = 0; + if (colour != sty || (ch == '\t') || (chPrev == '\t') || (i == selStart) || (i == selEnd) || (i == posLineEnd)) { + if (segment[0] == '\t') { + width = (((xpos + 2 - fixedColumnWidth) / tabWidth) + 1) * tabWidth + fixedColumnWidth - xpos; + if ((xpos + width) >= pt.x) { + complete = true; + if ((xpos + width / 2) <= pt.x) // If in first half of char + retPos = retPos + 1; + } + } else { + int styleMain = sty & 31; + width = surfaceMeasure.WidthText(styles[styleMain].font, segment, segPos); + if ((xpos + width) >= pt.x) { + // For non -tabs must measure positions + int prevWidth = 0; + for (int j=1; j<=segPos && !complete; j++) { + width = surfaceMeasure.WidthText(styles[styleMain].font, segment, j); + if ((xpos + width) >= pt.x) { + complete = true; + retPos += j - 1; + if ((xpos + prevWidth + (width-prevWidth) / 2) < pt.x) + retPos = retPos + 1; + } + prevWidth = width; + } + } + } + if (!complete) { + retPos += segPos; + segPos = 0; + segment[segPos] = '\0'; + xpos += width; + sty = colour; + } + } + if (ch != '\r' && ch != '\n' && ((segPos+1) < sizeof(segment))) + segment[segPos++] = ch; + chPrev = ch; + } + + if (retPos > posLineEnd) + retPos = posLineEnd; + if (retPos < posLineStart) + retPos = posLineStart; + return retPos; +} + +void Scintilla::SetVertScrollFromTopLine() { +#ifdef GTK + gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine); +#else + SetScrollPos(hwnd,SB_VERT,topLine,TRUE); +#endif +} + +#ifdef GTK +void Scintilla::Scroll(int topLineNew) { + //dprintf("Scrolling\n"); + topLine = topLineNew; + Redraw(); +} +#else +void Scintilla::Scroll(WPARAM wParam) { + //DWORD dwStart = timeGetTime(); + //dprintf("Scroll %x %d\n", wParam, lParam); + + SCROLLINFO sci; + memset(&sci, 0, sizeof(sci)); + sci.cbSize = sizeof(sci); + sci.fMask = SIF_ALL; + + BOOL b = GetScrollInfo(hwnd, SB_VERT, &sci); + + //dprintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask, + //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos); + + switch (LOWORD(wParam)) { + case SB_LINEUP: topLine -= 1; break; + case SB_LINEDOWN: topLine += 1; break; + case SB_PAGEUP: topLine -= LinesToScroll(); break; + case SB_PAGEDOWN: topLine += LinesToScroll(); break; + case SB_TOP: topLine = 0; break; + case SB_BOTTOM: topLine = MaxScrollPos(); break; + case SB_THUMBPOSITION: topLine = sci.nTrackPos; break; + case SB_THUMBTRACK: topLine = sci.nTrackPos; break; + } + topLine = clamp(topLine, 0, MaxScrollPos()); + SetVertScrollFromTopLine(); + ShowCaretAtCurrentPosition(); + Redraw(); + //DWORD dwEnd = timeGetTime(); + //dprintf("end scroll %d\n", dwEnd - dwStart); +} + +void Scintilla::HorizontalScroll(WPARAM wParam) { + int xPos = xOffset; + switch (LOWORD(wParam)) { + case SB_LINEUP: xPos -= 20; break; + case SB_LINEDOWN: xPos += 20; break; + case SB_PAGEUP: xPos -= 200; break; + case SB_PAGEDOWN: xPos += 200; break; + case SB_TOP: xPos = 0; break; + case SB_BOTTOM: xPos = 2000; break; + case SB_THUMBPOSITION: xPos = HIWORD(wParam); break; + case SB_THUMBTRACK: xPos = HIWORD(wParam); break; + } + HorizontalScrollTo(xPos); +} +#endif + +void Scintilla::AutoCompleteStart(char *list) { + //dprintf("AutoCOmplete %s\n", list); +#ifdef GTK + listAutoComplete = 0; + currentAutoComplete = 0; + GtkWidget *list_item = 0; + GtkWidget *scrolled_window=0; +#endif + CallTipCancel(); + + RECT rcClient; + GetClientRectangle(&rcClient); + if (!hwndAutoComplete) { +#ifdef GTK + hwndAutoComplete = gtk_window_new(GTK_WINDOW_POPUP); + scrolled_window = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_usize(scrolled_window, 120, 100); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(hwndAutoComplete), scrolled_window); + + listAutoComplete = gtk_clist_new(1); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), listAutoComplete); + gtk_widget_show(scrolled_window); + + gtk_widget_show(listAutoComplete); + gtk_signal_connect(GTK_OBJECT(listAutoComplete), "select_row", + GTK_SIGNAL_FUNC(SelectionAC), this); + //gtk_clist_set_shadow_type(GTK_CLIST(listAutoComplete), GTK_SHADOW_NONE); + gtk_clist_set_sort_column(GTK_CLIST(listAutoComplete), 0); + gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(scrolled_window)->child), GTK_SHADOW_OUT); +#else + hwndAutoComplete = CreateWindowEx( + WS_EX_CLIENTEDGE, "listbox", "", + WS_CHILD|WS_BORDER|WS_VSCROLL|LBS_SORT|LBS_NOTIFY, + 100,100, 150,80, hwnd, (HMENU)idAutoComplete, hInstance, 0); +#endif + } + inAutoCompleteMode = true; + posStartAutoComplete = currentPos; + //dprintf("Auto complete %x\n", hwndAutoComplete); + POINT pt = LocationFromPosition(currentPos); + int heightLB = 100; + int widthLB = 100; + int maxStrLen = 12; + if (pt.x >= rcClient.right - widthLB) { + HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB); + Redraw(); + pt = LocationFromPosition(currentPos); + } +#ifdef GTK + int ox = 0; + int oy = 0; + gdk_window_get_origin(draw->window, &ox, &oy); + GtkAllocation alloc; + if (pt.y >= rcClient.bottom - heightLB) { + alloc.x = ox + pt.x-3; + alloc.y = oy + pt.y - heightLB; + } else { + alloc.x = ox + pt.x-3; + alloc.y = oy + pt.y + lineHeight; + } + alloc.width = 100; + alloc.height = 100; + gtk_widget_set_uposition(hwndAutoComplete, alloc.x, alloc.y); + gtk_widget_size_allocate(GTK_WIDGET(listAutoComplete), &alloc); +#else + if (pt.y >= rcClient.bottom - heightLB) { + SetWindowPos(hwndAutoComplete, 0, pt.x-3, pt.y - heightLB, widthLB, heightLB, 0); + } else { + SetWindowPos(hwndAutoComplete, 0, pt.x-3, pt.y + lineHeight, widthLB, heightLB, 0); + } + SendMessage(hwndAutoComplete, WM_SETFONT, (WPARAM)styles[0].font, 0); + SendMessage(hwndAutoComplete, LB_RESETCONTENT, 0, 0); +#endif + + char *words = strdup(list); + char *startword = words; + int i = 0; + for (; words && words[i]; i++) { + if (words[i] == ' ') { + words[i] = '\0'; +#ifdef GTK + char *szs[] = { startword, 0}; + gtk_clist_append(GTK_CLIST(listAutoComplete), szs); +#else + SendMessage(hwndAutoComplete, LB_ADDSTRING, 0, (LPARAM)startword); +#endif + maxStrLen = max(maxStrLen, strlen(startword)); + startword = words + i + 1; + } + } + if (startword) { +#ifdef GTK + char *szs[] = { startword, 0}; + gtk_clist_append(GTK_CLIST(listAutoComplete), szs); +#else + SendMessage(hwndAutoComplete, LB_ADDSTRING, 0, (LPARAM)startword); +#endif + maxStrLen = max(maxStrLen, strlen(startword)); + } + free(words); +#ifdef GTK + //gtk_fixed_put(GTK_FIXED(sci), hwndAutoComplete, alloc.x, alloc.y); + gtk_clist_sort(GTK_CLIST(listAutoComplete)); + gtk_widget_show(listAutoComplete); + gtk_widget_realize(hwndAutoComplete); + gtk_widget_show(hwndAutoComplete); +#else + // Fiddle the position of the list so it is right next to the target and wide enough for all its strings + RECT rcList; + GetWindowRect(hwndAutoComplete, &rcList); + int heightAlloced = rcList.bottom - rcList.top; + // Make an allowance for large strings in list + widthLB = max(widthLB, maxStrLen * 8 + 16); + if (pt.y >= rcClient.bottom - heightLB) { + SetWindowPos(hwndAutoComplete, 0, pt.x-3, pt.y - heightAlloced, widthLB, heightAlloced, 0); + } else { + SetWindowPos(hwndAutoComplete, 0, pt.x-3, pt.y + lineHeight, widthLB, heightAlloced, 0); + } + ShowWindow(hwndAutoComplete, SW_SHOWNORMAL); + SendMessage(hwndAutoComplete, LB_SETCURSEL, 0, 0); +#endif +} + +void Scintilla::AutoCompleteCancel() { + if (hwndAutoComplete) { +#ifdef GTK + gtk_widget_destroy(GTK_WIDGET(hwndAutoComplete)); +#else + ShowWindow(hwndAutoComplete, SW_HIDE); + DestroyWindow(hwndAutoComplete); +#endif + inAutoCompleteMode = false; + hwndAutoComplete = 0; + } +} + +#ifdef GTK +void Scintilla::SelectionAC(GtkWidget *clist, + gint row, + gint column, + GdkEventButton *event, + gpointer p) { + Scintilla *psci = reinterpret_cast(p); + psci->currentAutoComplete = row; +} +#endif + +void Scintilla::AutoCompleteMove(int delta) { +#ifdef GTK + int count = GTK_CLIST(listAutoComplete)->rows; + int current = currentAutoComplete; +#else + int count = SendMessage(hwndAutoComplete, LB_GETCOUNT, 0, 0); + int current = SendMessage(hwndAutoComplete, LB_GETCURSEL, 0, 0); +#endif + current += delta; + if (current >= count) + current = count - 1; + if (current < 0) + current = 0; +#ifdef GTK + gtk_clist_select_row(GTK_CLIST(listAutoComplete), + current, 0); +#else + SendMessage(hwndAutoComplete, LB_SETCURSEL, current, 0); +#endif +} + +void Scintilla::AutoCompleteChanged(char ch) { + if (posStartAutoComplete > currentPos) { + AutoCompleteCancel(); + } else if (ch && strchr(autoCompleteStops, ch)) { + AutoCompleteCancel(); + } else { + char wordCurrent[1000]; + int i; + for (i=posStartAutoComplete;irows; + for (int j=0;j %d\n", wordCurrent, pos); + if (pos != -1) + SendMessage(hwndAutoComplete, LB_SETCURSEL, pos, 0); +#endif + } +} + +void Scintilla::AutoCompleteCompleted() { + inAutoCompleteMode = false; + if (currentPos != posStartAutoComplete) { + DeleteChars(posStartAutoComplete, currentPos - posStartAutoComplete); + } + SetSelection(posStartAutoComplete, posStartAutoComplete); +#ifdef GTK + int item = currentAutoComplete; + if (item != -1) { + char *selected=0; + gtk_clist_get_text(GTK_CLIST(listAutoComplete), item, 0, &selected); + if (selected) { + InsertString(currentPos, selected); + SetSelection(currentPos+strlen(selected), currentPos+strlen(selected)); + } + } + gtk_widget_destroy(GTK_WIDGET(hwndAutoComplete)); +#else + ShowWindow(hwndAutoComplete, SW_HIDE); + int item = SendMessage(hwndAutoComplete, LB_GETCURSEL, 0, 0); + if (item != -1) { + char selected[200]; + SendMessage(hwndAutoComplete, LB_GETTEXT, item, (LPARAM) selected); + //dprintf("Selecting %d <%s>\n", item, selected); + //dprintf("Selecting <%s>\n", selected); + InsertString(currentPos, selected); + SetSelection(currentPos+strlen(selected), currentPos+strlen(selected)); + } + DestroyWindow(hwndAutoComplete); +#endif + hwndAutoComplete = 0; + NotifyChange(); +} + +#ifdef GTK +gint Scintilla::PaintCT(GtkWidget *widget_, GdkEventExpose ose) { + GdkGC *gc = gdk_gc_new(widget_->window); + Surface surfaceCT; + surfaceCT.Init(widget_, widget_->window, gc); + + RECT rcClient; + rcClient.left = 1; + rcClient.right = widget_->allocation.width-2; + rcClient.top = 1; + rcClient.bottom = widget_->allocation.height-2; + + COLORREF bg = RGB(0xff, 0xff, 0xff); + + surfaceCT.FillRectangle(rcClient, bg); + + int x = 5; + int xEnd = x + surfaceCT.WidthText(styles[0].font, valCT, startHighlightCT); + rcClient.left = x; + rcClient.right = xEnd; + surfaceCT.DrawText(rcClient, styles[0].font, lineHeight - styles[0].descent, + valCT, startHighlightCT, + RGB(0x80,0x80,0x80), bg); + x = xEnd; + + xEnd = x + surfaceCT.WidthText(styles[0].font, valCT + startHighlightCT, endHighlightCT - startHighlightCT); + rcClient.left = x; + rcClient.right = xEnd; + surfaceCT.DrawText(rcClient, styles[0].font, lineHeight - styles[0].descent, + valCT + startHighlightCT, endHighlightCT - startHighlightCT, + RGB(0,0,0x80), bg); + x = xEnd; + + xEnd = x + surfaceCT.WidthText(styles[0].font, valCT + endHighlightCT, strlen(valCT) - endHighlightCT); + rcClient.left = x; + rcClient.right = xEnd; + surfaceCT.DrawText(rcClient, styles[0].font, lineHeight - styles[0].descent, + valCT + endHighlightCT, strlen(valCT) - endHighlightCT, + RGB(0x80,0x80,0x80), bg); + x = xEnd; + + surfaceCT.MoveTo(0, widget_->allocation.height-1); + surfaceCT.PenColor(RGB(0,0,0)); + surfaceCT.LineTo(widget_->allocation.width-1, widget_->allocation.height-1); + surfaceCT.LineTo(widget_->allocation.width-1, 0); + surfaceCT.PenColor(RGB(0xc0,0xc0,0xc0)); + surfaceCT.LineTo(0, 0); + surfaceCT.LineTo(0, widget_->allocation.height-1); + + gdk_gc_unref(gc); +} + +gint Scintilla::ExposeCT(GtkWidget *widget, GdkEventExpose *ose, gpointer p) { + Scintilla *psci = reinterpret_cast(p); + return psci->PaintCT(widget, *ose); +} +#endif + +void Scintilla::CallTipStart(int pos, char *defn) { + AutoCompleteCancel(); +#ifdef GTK + if (valCT) + free(valCT); + valCT = strdup(defn); + startHighlightCT = 0; + endHighlightCT = 0; + if (!hwndCallTip) { + Surface surfaceCT; + surfaceCT.Init(); + int width = surfaceCT.WidthText(styles[0].font, valCT, strlen(valCT)) + 10; + int height = lineHeight + 2; + hwndCallTip = gtk_window_new(GTK_WINDOW_POPUP); + GtkWidget *drawCT = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(hwndCallTip), drawCT); + gtk_signal_connect(GTK_OBJECT(drawCT), "expose_event", + GtkSignalFunc(ExposeCT), this); + gtk_widget_set_events(drawCT, GDK_EXPOSURE_MASK); + gtk_drawing_area_size(GTK_DRAWING_AREA(drawCT), width, height); + + POINT pt = LocationFromPosition(pos); + int ox = 0; + int oy = 0; + gdk_window_get_origin(draw->window, &ox, &oy); + GtkAllocation alloc; + alloc.x = ox + pt.x-5; + alloc.y = oy + pt.y + lineHeight; + alloc.width = width; + alloc.height = height; + gtk_widget_set_uposition(hwndCallTip, alloc.x, alloc.y); + gtk_widget_size_allocate(GTK_WIDGET(drawCT), &alloc); + + gtk_widget_show(drawCT); + gtk_widget_realize(hwndCallTip); + gtk_widget_show(hwndCallTip); + } +#else + if (!hwndCallTip) { + hwndCallTip = CreateWindow(callClassName, "ACallTip", WS_VISIBLE|WS_CHILD, 100,100, 150,20, hwnd, (HMENU)idAutoComplete, hInstance, 0); + } + //dprintf("Made tool tip %x\n", hwndCallTip); + POINT pt = LocationFromPosition(pos); + SetWindowPos(hwndCallTip, 0, pt.x-5, pt.y + lineHeight, 10, lineHeight + 2, 0); + SendMessage(hwndCallTip, WM_SETFONT, (WPARAM)styles[0].font, 0); + SetWindowText(hwndCallTip,defn); + ShowWindow(hwndCallTip, SW_SHOWNORMAL); +#endif + inCallTipMode = true; + posStartCallTip = currentPos; +} + +void Scintilla::CallTipCancel() { + inCallTipMode = false; +#ifdef GTK + if (hwndCallTip) { + gtk_widget_destroy(GTK_WIDGET(hwndCallTip)); + hwndCallTip = 0; + } +#else + ShowWindow(hwndCallTip, SW_HIDE); +#endif +} + +#ifdef GTK +void Scintilla::PopUpCB(GtkWidget *widget, gpointer cbdata) { + Scintilla *sci = reinterpret_cast(cbdata); + GList *children = gtk_container_children(GTK_CONTAINER(widget)); + GtkWidget *child = reinterpret_cast( + g_list_nth(children, 0)->data); + GtkLabel *label = GTK_LABEL(child); + //dprintf("Pop up %x %x %s\n", children, child, label->label); + int cmd = 0; + if (0 == strcmp(label->label, "Undo")) { + cmd = idcmdUndo; + } else if (0 == strcmp(label->label, "Redo")) { + cmd = idcmdRedo; + } else if (0 == strcmp(label->label, "Cut")) { + cmd = idcmdCut; + } else if (0 == strcmp(label->label, "Copy")) { + cmd = idcmdCopy; + } else if (0 == strcmp(label->label, "Paste")) { + cmd = idcmdPaste; + } else if (0 == strcmp(label->label, "Delete")) { + cmd = idcmdDelete; + } else if (0 == strcmp(label->label, "Select All")) { + cmd = idcmdSelectAll; + } + if (cmd) { + sci->Command(cmd); + } +} + +void Scintilla::AddToPopUp(const char *label, bool enabled) { + GtkWidget *it = 0; + if (0 == strcmp(label, "")) + it = gtk_menu_item_new(); + else + it = gtk_menu_item_new_with_label(label); + gtk_widget_set_sensitive(it, enabled); + gtk_menu_append(GTK_MENU(popup), it); + gtk_widget_show(it); + gtk_signal_connect(GTK_OBJECT(it), "activate", + GTK_SIGNAL_FUNC(Scintilla::PopUpCB), this); +} +#endif + +void Scintilla::ContextMenu(POINT pt) { +#ifndef GTK + HMENU popMenu = CreatePopupMenu(); + if (popMenu == NULL) + return; + + if (doc.CanUndo()) + AppendMenu(popMenu, MF_STRING, idcmdUndo, "Undo" ); + else + AppendMenu(popMenu, MF_STRING | MF_DISABLED | MF_GRAYED, idcmdUndo, "Undo" ); + if (doc.CanRedo()) + AppendMenu(popMenu, MF_STRING, idcmdRedo, "Redo" ); + else + AppendMenu(popMenu, MF_STRING | MF_DISABLED | MF_GRAYED, idcmdRedo, "Redo" ); + AppendMenu(popMenu, MF_SEPARATOR, 0, ""); + if (currentPos != anchor) { + AppendMenu(popMenu, MF_STRING, idcmdCut, "Cut" ); + AppendMenu(popMenu, MF_STRING, idcmdCopy, "Copy"); + } else { // Empty selection + AppendMenu(popMenu, MF_STRING | MF_DISABLED | MF_GRAYED, idcmdCut, "Cut" ); + AppendMenu(popMenu, MF_STRING | MF_DISABLED | MF_GRAYED, idcmdCopy, "Copy"); + } + if (SendMessage(hwnd, EM_CANPASTE, 0, 0)) + AppendMenu(popMenu, MF_STRING, idcmdPaste, "Paste"); + else + AppendMenu(popMenu, MF_STRING | MF_DISABLED | MF_GRAYED, idcmdPaste, "Paste"); + AppendMenu(popMenu, MF_STRING, idcmdDelete, "Delete"); + AppendMenu(popMenu, MF_SEPARATOR, 0, ""); + AppendMenu(popMenu, MF_STRING, idcmdSelectAll, "Select All"); + + if (!TrackPopupMenu(popMenu, 0, pt.x - 4, pt.y, 0, hwnd, NULL)) + return; + + DestroyMenu(popMenu); +#else + if (popup != 0) { + gtk_widget_unref(popup); + popup = 0; + } + popup = gtk_menu_new(); + AddToPopUp("Undo", doc.CanUndo()); + AddToPopUp("Redo", doc.CanRedo()); + AddToPopUp(""); + AddToPopUp("Cut", currentPos != anchor); + AddToPopUp("Copy", currentPos != anchor); + AddToPopUp("Paste"); + AddToPopUp("Delete"); + AddToPopUp(""); + AddToPopUp("Select All"); + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, + 3, 0); + //gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, + // 3, event->time); +#endif +} + +void Scintilla::Command(WPARAM wParam) { + int cmd = HIWORD(wParam); + switch (LOWORD(wParam)) { + case idAutoComplete: + //dprintf("S command %x %x\n", wParam); +#ifndef GTK + if (cmd == LBN_DBLCLK) { + AutoCompleteCompleted(); + } else { + if (cmd != LBN_SETFOCUS) + SetFocus(hwnd); + } +#endif + break; + + case idCallTip: // Nothing to do + break; + + case idcmdUndo: + Undo(); + break; + + case idcmdRedo: + Redo(); + break; + + case idcmdCut: + Cut(); + break; + + case idcmdCopy: + Copy(); + break; + + case idcmdPaste: + Paste(); + break; + + case idcmdDelete: + Clear(); + break; + + case idcmdSelectAll: + SelectAll(); + break; + } +} + +bool Scintilla::IsWordAt(int start, int end) { + int lengthDoc = Length(); + if (start > 0) { + char ch = CharAt(start-1); + //dprintf("start = %c\n", ch); + if (isalnum(ch)) + return false; + } + if (end < lengthDoc - 1) { + char ch = CharAt(end + 1); + //dprintf("end = %c\n", ch); + if (isalnum(ch)) + return false; + } + return true; +} + +long Scintilla::FindText(WORD iMessage,WPARAM wParam,LPARAM lParam) { + FINDTEXTEX *ft = reinterpret_cast(lParam); + int startPos = ClampPositionIntoDocument(ft->chrg.cpMin); + int endPos = ClampPositionIntoDocument(ft->chrg.cpMax); + int lengthFind = strlen(ft->lpstrText); + //dprintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind); + for (int pos=startPos;poslpstrText[0]) { + bool found = true; + for (int posMatch = 0; posMatch < lengthFind && found; posMatch++) { + ch = CharAt(pos + posMatch); + if (ch != ft->lpstrText[posMatch]) + found = false; + } + if (found) { + if (wParam & FR_WHOLEWORD) + found = IsWordAt(pos,pos + lengthFind); + if (found) { + if (iMessage == EM_FINDTEXTEX) { + ft->chrgText.cpMin = pos; + ft->chrgText.cpMax = pos + lengthFind; + } + return pos; + } + } + } + } else { + if (toupper(ch) == toupper(ft->lpstrText[0])) { + bool found = true; + for (int posMatch = 0; posMatch < lengthFind && found; posMatch++) { + ch = CharAt(pos + posMatch); + if (toupper(ch) != toupper(ft->lpstrText[posMatch])) + found = false; + } + if (found) { + if (wParam & FR_WHOLEWORD) + found = IsWordAt(pos,pos + lengthFind); + if (found) { + if (iMessage == EM_FINDTEXTEX) { + ft->chrgText.cpMin = pos; + ft->chrgText.cpMax = pos + lengthFind; + } + return pos; + } + } + } + } + } + //dprintf("Not found\n"); + return -1; +} + +long Scintilla::WndProc(WORD iMessage,WPARAM wParam,LPARAM lParam) { + //dprintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + switch (iMessage) { + +#ifndef GTK + case WM_CREATE: + ctrlID = GetDlgCtrlID(hwnd); + break; + + case WM_PAINT: { + RECT rcPaint = {0,0,0,0}; + Paint(rcPaint); + } + break; + + case WM_VSCROLL: + Scroll(wParam); + break; + + case WM_HSCROLL: + HorizontalScroll(wParam); + break; + + case WM_SIZE: + //dprintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + SetScrollBars(&lParam,wParam); + DropGraphics(); + break; +#endif + + case WM_GETTEXT: { + char *ptr = reinterpret_cast(lParam); + for (int iChar=0;iChar(lParam)); + NotifyChange(); + Redraw(); + } + break; + + case WM_GETTEXTLENGTH: + return Length(); + +#ifndef GTK + case WM_GETMINMAXINFO: + return DefWindowProc(hwnd,iMessage,wParam,lParam); + break; + + case WM_LBUTTONDOWN: + ButtonDown(PointFromLparam(lParam), GetTickCount(), wParam & MK_SHIFT); + break; + + case WM_MOUSEMOVE: + ButtonMove(PointFromLparam(lParam)); + break; + + case WM_LBUTTONUP: + ButtonUp(PointFromLparam(lParam), GetTickCount()); + break; + + case WM_CHAR: + //dprintf("S char proc %d %x %x\n",iMessage, wParam, lParam); + if (!iscntrl(wParam&0xff)) + AddChar(wParam&0xff); + return 1; + + case WM_KEYDOWN: + //dprintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL)); + return KeyDown(wParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL), false); + break; + + case WM_KEYUP: + //dprintf("S keyup %d %x %x\n",iMessage, wParam, lParam); + break; + + case WM_SETTINGCHANGE: + //dprintf("Setting Changed\n"); + DropGraphics(); + break; + + case WM_GETDLGCODE: + return DLGC_HASSETSEL | DLGC_WANTALLKEYS; + + case WM_KILLFOCUS: + DropCaret(); + //RealizeWindowPalette(true); + break; + + case WM_SETFOCUS: + ShowCaretAtCurrentPosition(); + RealizeWindowPalette(false); + break; + + case WM_SYSCOLORCHANGE: + //dprintf("Setting Changed\n"); + DropGraphics(); + break; + + case WM_PALETTECHANGED: + if ((int)wParam != (int)hwnd) { + dprintf("** Palette Changed\n"); + RealizeWindowPalette(true); + } + break; + + case WM_QUERYNEWPALETTE: + dprintf("** Query palette\n"); + RealizeWindowPalette(false); + break; +#endif + + case WM_COMMAND: + Command(wParam); + break; + + case WM_NOTIFY: + //dprintf("S notify %d %d\n", wParam, lParam); + break; + + case WM_CUT: + Cut(); + break; + + case WM_COPY: + Copy(); + break; + + case WM_PASTE: + Paste(); + SetScrollBars(); + break; + + case WM_CLEAR: + //dprintf("S Clear %d %x %x\n",iMessage, wParam, lParam); + Clear(); + SetScrollBars(); + break; + + case WM_UNDO: + Undo(); + break; + +#ifndef GTK + case WM_CONTEXTMENU: + ContextMenu(PointFromLparam(lParam)); + break; +#endif + + // Edit control mesages + + // Not supported (no-ops): + // EM_GETWORDBREAKPROC + // EM_GETWORDBREAKPROCEX + // EM_SETWORDBREAKPROC + // EM_SETWORDBREAKPROCEX + // EM_GETWORDWRAPMODE + // EM_SETWORDWRAPMODE + // EM_LIMITTEXT + // EM_EXLIMITTEXT + // EM_SETRECT + // EM_SETRECTNP + // EM_FMTLINES + // EM_GETHANDLE + // EM_SETHANDLE + // EM_GETPASSWORDCHAR + // EM_SETPASSWORDCHAR + // EM_SETTABSTOPS + // EM_FINDWORDBREAK + // EM_GETCHARFORMAT + // EM_SETCHARFORMAT + // EM_GETOLEINTERFACE + // EM_SETOLEINTERFACE + // EM_SETOLECALLBACK + // EM_GETPARAFORMAT + // EM_SETPARAFORMAT + // EM_PASTESPECIAL + // EM_REQUESTRESIZE + // EM_GETBKGNDCOLOR + // EM_SETBKGNDCOLOR + // EM_STREAMIN + // EM_STREAMOUT + // EM_GETIMECOLOR + // EM_SETIMECOLOR + // EM_GETIMEOPTIONS + // EM_SETIMEOPTIONS + // EM_GETMARGINS + // EM_SETMARGINS + // EM_GETOPTIONS + // EM_SETOPTIONS + // EM_GETPUNCTUATION + // EM_SETPUNCTUATION + // EM_GETTHUMB + + // Not supported but should be: + // EM_GETEVENTMASK + // EM_SETEVENTMASK + // For printing: + // EM_DISPLAYBAND + // EM_FORMATRANGE + // EM_SETTARGETDEVICE + + case EM_CANUNDO: + return doc.CanUndo() ? TRUE : FALSE; + + case EM_UNDO: + Undo(); + SetScrollBars(); + break; + + case EM_EMPTYUNDOBUFFER: + DeleteUndoHistory(); + return 0; + + case EM_GETFIRSTVISIBLELINE: + return topLine; + + case EM_GETLINE: { + int lineStart = LineStart(wParam); + int lineEnd = LineStart(wParam+1); + char *ptr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar=lineStart;iChar < lineEnd;iChar++) + ptr[iPlace++] = doc.CharAt(iChar); + ptr[iPlace] = '\0'; + return iPlace; + } + break; + + case EM_GETLINECOUNT: + return LinesTotal(); + + case EM_GETMODIFY: + return isModified; + + case EM_SETMODIFY: + isModified = wParam; + return isModified; + + case EM_GETRECT: + GetClientRectangle(reinterpret_cast(lParam)); + break; + + case EM_GETSEL: + case EM_EXGETSEL: + if (wParam) + *reinterpret_cast(wParam) = SelectionStart(); + if (lParam) + *reinterpret_cast(lParam) = SelectionEnd(); + return MAKELONG(SelectionStart(), SelectionEnd()); + break; + + case EM_SETSEL: + case EM_EXSETSEL: { + int nStart = static_cast(wParam); + int nEnd = static_cast(lParam); + if (nEnd < 0) + nEnd = Length(); + if (nStart < 0) + nStart = nEnd; + SetSelection(nEnd, nStart); + EnsureCaretVisible(); + Redraw(); + } + break; + + case EM_GETSELTEXT: { + char *ptr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar=SelectionStart();iChar < SelectionEnd();iChar++) + ptr[iPlace++] = doc.CharAt(iChar); + ptr[iPlace] = '\0'; + return iPlace; + } + break; + + case EM_GETWORDBREAKPROC: + return 0; + + case EM_SETWORDBREAKPROC: + break; + + case EM_LIMITTEXT: + // wParam holds the number of characters control should be limited to + break; + + case EM_GETLIMITTEXT: + return 0xffffffff; + + case EM_GETOLEINTERFACE: + return 0; + + case EM_LINEFROMCHAR: + case EM_EXLINEFROMCHAR: + return LineFromPosition(wParam); + + case EM_LINEINDEX: + return LineStart(wParam); + + case EM_LINELENGTH: + return LineStart(wParam+1) - LineStart(wParam); + + case EM_REPLACESEL: { + ClearSelection(); + char *replacement = reinterpret_cast(lParam); + InsertString(currentPos, replacement); + SetSelection(currentPos + strlen(replacement), currentPos + strlen(replacement)); + NotifyChange(); + SetScrollBars(); + EnsureCaretVisible(); + Redraw(); + } + break; + + case EM_SCROLL: { + int topStart = topLine; + Scroll(wParam); + return MAKELONG(topLine - topStart, TRUE); + } + break; + + case EM_LINESCROLL: + ScrollTo(topLine + lParam); + HorizontalScrollTo(xOffset + wParam * spaceWidth); + return TRUE; + + case EM_SCROLLCARET: + EnsureCaretVisible(); + break; + + case EM_SETREADONLY: + doc.SetReadOnly(wParam); + return TRUE; + + case EM_SETRECT: + break; + + case EM_CANPASTE: { +#ifdef GTK + return 1; +#else + OpenClipboard(hwnd); + HGLOBAL hmemSelection = GetClipboardData(CF_TEXT); + if (hmemSelection) + GlobalUnlock(hmemSelection); + CloseClipboard(); + return hmemSelection != 0; +#endif + } + break; + + case EM_CHARFROMPOS: { + POINT *ppt=reinterpret_cast(lParam); + return PositionFromLocation(*ppt); + } + break; + + case EM_POSFROMCHAR: { + POINT *ppt=reinterpret_cast(lParam); + *ppt = LocationFromPosition(wParam); + return 0; + } + break; + + case EM_FINDTEXT: + return FindText(iMessage, wParam, lParam); + break; + + case EM_FINDTEXTEX: + return FindText(iMessage, wParam, lParam); + break; + + case EM_GETTEXTRANGE: { + TEXTRANGE *tr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar=tr->chrg.cpMin;iChar < tr->chrg.cpMax;iChar++) + tr->lpstrText[iPlace++] = doc.CharAt(iChar); + tr->lpstrText[iPlace] = '\0'; + return iPlace; + } + break; + + case EM_SELECTIONTYPE: + if (currentPos == anchor) + return SEL_EMPTY; + else + return SEL_TEXT; + + case EM_HIDESELECTION: + hideSelection = wParam; + Redraw(); + break; + + // Control specific mesages + + case SCI_ADDTEXT: { + InsertString(CurrentPosition(), reinterpret_cast(lParam), wParam); + SetSelection(currentPos + wParam, currentPos + wParam); + SetScrollBars(); + NotifyChange(); + Redraw(); + return 0; + } + break; + + case SCI_ADDSTYLEDTEXT: { + InsertStyledString(CurrentPosition() * 2, reinterpret_cast(lParam), wParam); + SetSelection(currentPos + wParam/2, currentPos + wParam/2); + SetScrollBars(); + Redraw(); + return 0; + } + break; + + case SCI_INSERTTEXT:{ + int insertPos = wParam; + if (wParam == -1) + insertPos = CurrentPosition(); + int newCurrent = CurrentPosition(); + int newAnchor = anchor; + char *sz = reinterpret_cast(lParam); + InsertString(insertPos, sz); + if (newCurrent > insertPos) + newCurrent += strlen(sz); + if (newAnchor > insertPos) + newAnchor += strlen(sz); + SetSelection(newCurrent, newAnchor); + SetScrollBars(); + NotifyChange(); + Redraw(); + return 0; + } + break; + + case SCI_CLEARALL: + ClearAll(); + return 0; + + case SCI_SETUNDOCOLLECTION: + doc.SetUndoCollection(static_cast(wParam)); + return 0; + + case SCI_APPENDUNDOSTARTACTION: + doc.AppendUndoStartAction(); + return 0; + + case SCI_GETLENGTH: + return Length(); + + case SCI_GETCHARAT: + return CharAt(wParam); + + case SCI_GETCURRENTPOS: + return currentPos; + + case SCI_GETANCHOR: + return anchor; + + case SCI_GETSTYLEAT: + if (wParam >= Length()) + return 0; + else + return doc.StyleAt(wParam); + + case SCI_REDO: + Redo(); + break; + + case SCI_SELECTALL: + SelectAll(); + break; + + case SCI_SETSAVEPOINT: + doc.SetSavePoint(); + NotifySavePoint(true); + break; + + case SCI_GETSTYLEDTEXT: { + TEXTRANGE *tr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar=tr->chrg.cpMin;iChar < tr->chrg.cpMax;iChar++) { + tr->lpstrText[iPlace++] = doc.CharAt(iChar); + tr->lpstrText[iPlace++] = doc.StyleAt(iChar); + } + tr->lpstrText[iPlace] = '\0'; + tr->lpstrText[iPlace+1] = '\0'; + return iPlace; + } + break; + + case SCI_GETVIEWWS: + return viewWhitespace; + + case SCI_SETVIEWWS: + viewWhitespace = wParam; + Redraw(); + break; + + case SCI_GOTOLINE: + GoToLine(wParam); + break; + + case SCI_GOTOPOS: + SetPosition(wParam); + Redraw(); + break; + + case SCI_SETANCHOR: + SetSelection(currentPos, wParam); + //dprintf("SetAnchor %d %d\n", currentPos, anchor); + break; + + case SCI_GETCURLINE: { + int lineCurrentPos = LineFromPosition(currentPos); + int lineStart = LineStart(lineCurrentPos); + int lineEnd = LineStart(lineCurrentPos+1); + char *ptr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar=lineStart;iChar < lineEnd && iPlace < wParam;iChar++) + ptr[iPlace++] = doc.CharAt(iChar); + ptr[iPlace++] = '\0'; + return currentPos - lineStart; + } + break; + + case SCI_GETENDSTYLED: + return endStyled; + + case SCI_GETEOLMODE: + return eolMode; + + case SCI_SETEOLMODE: + eolMode = wParam; + break; + + case SCI_STARTSTYLING: + stylingPos = wParam; + stylingMask = lParam; + break; + + case SCI_SETSTYLING: { + for (int iPos=0; iPos(lParam)[iPos], stylingMask); + } + endStyled = stylingPos; + } + break; + + case SCI_SETMARGINWIDTH: + if (wParam < 100) { + selMarginWidth = wParam; + fixedColumnWidth = selMarginWidth + lineNumberWidth; + } + Redraw(); + break; + + case SCI_SETBUFFEREDDRAW: + bufferedDraw = wParam; + break; + + case SCI_SETTABWIDTH: + if (wParam > 0) + tabInChars = wParam; + InvalidateStyleData(); + break; + + case SCI_SETCODEPAGE: + dbcsCodePage = wParam; + break; + + case SCI_SETLINENUMBERWIDTH: + if (wParam < 200) { + lineNumberWidth = wParam; + fixedColumnWidth = selMarginWidth + lineNumberWidth; + InvalidateStyleData(); + } + Redraw(); + break; + + case SCI_SETUSEPALETTE: + palette.allowRealization = wParam; + InvalidateStyleData(); + Redraw(); + break; + + // Marker definition and setting + case SCI_MARKERDEFINE: + if (wParam <= MARKER_MAX) + markers[wParam].markType = lParam; + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETFORE: + if (wParam <= MARKER_MAX) + markers[wParam].fore.desired = ColourFromLparam(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETBACK: + if (wParam <= MARKER_MAX) + markers[wParam].back.desired = ColourFromLparam(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERADD: + doc.SetMark(wParam, doc.GetMark(wParam) | (1 << lParam)); + RedrawSelMargin(); + break; + + case SCI_MARKERDELETE: + doc.SetMark(wParam, doc.GetMark(wParam) & ~(1 << lParam)); + RedrawSelMargin(); + break; + + case SCI_MARKERDELETEALL: + doc.DeleteAllMarks(static_cast(wParam)); + RedrawSelMargin(); + break; + + case SCI_MARKERGET: + return doc.GetMark(wParam); + + case SCI_MARKERNEXT: { + for (int iLine=wParam; iLine(lParam)); + InvalidateStyleData(); + } + break; + + case SCI_SETFORE: + foreground.desired = ColourFromLparam(wParam); + InvalidateStyleData(); + break; + + case SCI_SETBACK: + background.desired = ColourFromLparam(wParam); + InvalidateStyleData(); + break; + + case SCI_SETBOLD: + bold = wParam; + InvalidateStyleData(); + break; + + case SCI_SETITALIC: + italic = wParam; + InvalidateStyleData(); + break; + + case SCI_SETSIZE: + size = wParam; + InvalidateStyleData(); + break; + + case SCI_SETFONT: + strcpy(fontName, reinterpret_cast(wParam)); + InvalidateStyleData(); + break; + + case SCI_SETSELFORE: + selforeset = wParam; + selforeground.desired = ColourFromLparam(lParam); + InvalidateStyleData(); + break; + + case SCI_SETSELBACK: + selbackset = wParam; + selbackground.desired = ColourFromLparam(lParam); + InvalidateStyleData(); + break; + + case SCI_SETCARETFORE: + caretcolour.desired = ColourFromLparam(wParam); + InvalidateStyleData(); + break; + + case SCI_ASSIGNCMDKEY: + //dprintf("Assign key %d %d\n", wParam, lParam); + AssignCmdKey(LOWORD(wParam), HIWORD(wParam), lParam); + break; + + case SCI_CLEARCMDKEY: + //dprintf("Clear key %d\n", wParam); + AssignCmdKey(LOWORD(wParam), HIWORD(wParam), WM_NULL); + break; + + case SCI_CLEARALLCMDKEYS: + delete []keymap; + keymap = 0; + keymapLen = 0; + keymapAlloc = 0; + break; + + case SCI_INDICSETSTYLE: + if (wParam <= INDIC_MAX) { + indicators[wParam].style = lParam; + InvalidateStyleData(); + } + break; + + case SCI_INDICGETSTYLE: + return (wParam <= INDIC_MAX) ? indicators[wParam].style : 0; + + case SCI_INDICSETFORE: + if (wParam <= INDIC_MAX) { + indicators[wParam].fore.desired = ColourFromLparam(lParam); + InvalidateStyleData(); + } + break; + + case SCI_INDICGETFORE: + return (wParam <= INDIC_MAX) ? LparamFromColour(indicators[wParam].fore.desired) : 0; + + case SCI_AUTOCSHOW: + AutoCompleteStart(reinterpret_cast(lParam)); + break; + + case SCI_AUTOCCANCEL: + AutoCompleteCancel(); + break; + + case SCI_AUTOCACTIVE: + return inAutoCompleteMode; + break; + + case SCI_AUTOCPOSSTART: + return posStartAutoComplete; + + case SCI_AUTOCCOMPLETE: + AutoCompleteCompleted(); + break; + + case SCI_AUTOCSTOPS: + strcpy(autoCompleteStops, reinterpret_cast(lParam)); + break; + + case SCI_CALLTIPSHOW: + CallTipStart(wParam, reinterpret_cast(lParam)); + break; + + case SCI_CALLTIPCANCEL: + CallTipCancel(); + break; + + case SCI_CALLTIPACTIVE: + return inCallTipMode; + + case SCI_CALLTIPPOSSTART: + return posStartCallTip; + + case SCI_CALLTIPSETHLT: +#ifdef GTK + startHighlightCT = wParam; + endHighlightCT = lParam; + if (hwndCallTip) { + gtk_widget_queue_draw(hwndCallTip); + } +#else + SendMessage(hwndCallTip, SCI_CALLTIPSETHLT, wParam, lParam); +#endif + break; + + case SCI_GRABFOCUS: +#ifdef GTK + gtk_widget_grab_focus(hwnd); +#endif + break; + + case SCI_LINEDOWN: + case SCI_LINEDOWNEXTEND: + case SCI_LINEUP: + case SCI_LINEUPEXTEND: + case SCI_CHARLEFT: + case SCI_CHARLEFTEXTEND: + case SCI_CHARRIGHT: + case SCI_CHARRIGHTEXTEND: + case SCI_WORDLEFT: + case SCI_WORDLEFTEXTEND: + case SCI_WORDRIGHT: + case SCI_WORDRIGHTEXTEND: + case SCI_HOME: + case SCI_HOMEEXTEND: + case SCI_LINEEND: + case SCI_LINEENDEXTEND: + case SCI_DOCUMENTSTART: + case SCI_DOCUMENTSTARTEXTEND: + case SCI_DOCUMENTEND: + case SCI_DOCUMENTENDEXTEND: + case SCI_PAGEUP: + case SCI_PAGEUPEXTEND: + case SCI_PAGEDOWN: + case SCI_PAGEDOWNEXTEND: + case SCI_EDITTOGGLEOVERTYPE: + case SCI_CANCEL: + case SCI_DELETEBACK: + case SCI_TAB: + case SCI_BACKTAB: + case SCI_NEWLINE: + case SCI_FORMFEED: + case SCI_VCHOME: + case SCI_VCHOMEEXTEND: + return KeyCommand(iMessage); + + default: +#ifndef GTK + return DefWindowProc(hwnd,iMessage,wParam,lParam); +#endif + break; + } + //dprintf("end wnd proc\n"); + return 0l; +} + +#ifdef GTK +long scintilla_send_message(ScintillaObject *sci,int iMessage,int wParam,int lParam) { + Scintilla *psci = reinterpret_cast(sci->pscin); + return psci->WndProc(iMessage, wParam, lParam); +} + +static void scintilla_class_init (ScintillaClass *klass); +static void scintilla_init (ScintillaObject *sci); + +guint scintilla_get_type() { + static guint scintilla_type = 0; + + if (!scintilla_type) { + GtkTypeInfo scintilla_info = { + "Scintilla", + sizeof (ScintillaObject), + sizeof (ScintillaClass), + (GtkClassInitFunc) scintilla_class_init, + (GtkObjectInitFunc) scintilla_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + scintilla_type = gtk_type_unique(gtk_fixed_get_type(), &scintilla_info); + } + + return scintilla_type; +} + +static void scintilla_class_init(ScintillaClass *klass) { + GtkObjectClass *object_class; + + object_class = (GtkObjectClass*) klass; + + scintilla_signals[COMMAND_SIGNAL] = gtk_signal_new( + "command", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET(ScintillaClass, command), + gtk_marshal_NONE__INT_POINTER, + GTK_TYPE_NONE, + 2, GTK_TYPE_INT, GTK_TYPE_POINTER); + + scintilla_signals[NOTIFY_SIGNAL] = gtk_signal_new( + "notify", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET(ScintillaClass, notify), + gtk_marshal_NONE__INT_POINTER, + GTK_TYPE_NONE, + 2, GTK_TYPE_INT, GTK_TYPE_POINTER); + + gtk_object_class_add_signals(object_class, + reinterpret_cast(scintilla_signals), LAST_SIGNAL); + + klass->command = NULL; + klass->notify = NULL; +} + +static void scintilla_init(ScintillaObject *sci) { + GTK_WIDGET_SET_FLAGS(sci, GTK_CAN_FOCUS); + sci->pscin = new Scintilla(sci); +} + +GtkWidget* scintilla_new() { + return GTK_WIDGET(gtk_type_new(scintilla_get_type())); +} + +void scintilla_set_id(ScintillaObject *sci,int id) { + Scintilla *psci = reinterpret_cast(sci->pscin); + psci->ctrlID = id; +} +#else + +const char *scintillaClassName = "Scintilla"; + +void Scintilla::Register(HINSTANCE hInstance_) { + + hInstance = hInstance_; + + InitCommonControls(); + + WNDCLASS wndclass; // Structure used to register Windows class. + + wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = (WNDPROC)::Scintilla::SWndProc; + 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(Scintilla*); + wndclass.hInstance = hInstance; + wndclass.hIcon = NULL; + wndclass.hCursor = LoadCursor(NULL,IDC_IBEAM); + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = scintillaClassName; + + if (!RegisterClass(&wndclass)) { + //dprintf("Could not register class\n"); + exit(FALSE); + } +} + +LRESULT PASCAL Scintilla::SWndProc( + HWND hWnd,UINT iMessage,WPARAM wParam, LPARAM lParam) { + //dprintf("S W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam); + + // Find C++ object associated with window. + Scintilla *sci = reinterpret_cast(GetWindowLong(hWnd,0)); + // sci will be zero if WM_CREATE not seen yet + if (sci == 0) { + if (iMessage == WM_CREATE) { + // Create C++ object associated with window + sci = new Scintilla(); + sci->hwnd = hWnd; + SetWindowLong(hWnd, 0, reinterpret_cast(sci)); + return sci->WndProc(iMessage, wParam, lParam); + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } else { + if (iMessage == WM_DESTROY) { + delete sci; + SetWindowLong(hWnd, 0, 0); + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } else { + return sci->WndProc(iMessage, wParam, lParam); + } + } +} + +// This function is externally visible so it can be called from container when building statically +void Scintilla_RegisterClasses(HINSTANCE hInstance) { + CallTip_Register(hInstance); + Scintilla::Register(hInstance); +} + +#ifndef STATIC_BUILD +extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { + //dprintf("Scintilla::DllMain %d %d\n", hInstance, dwReason); + if (dwReason == DLL_PROCESS_ATTACH) { + Scintilla_RegisterClasses(hInstance); + } + return TRUE; +} +#endif + +#endif + diff --git a/Pythonwin/scintilla_src/Scintilla.h b/Pythonwin/scintilla_src/Scintilla.h new file mode 100644 index 0000000000..6ea24cd12c --- /dev/null +++ b/Pythonwin/scintilla_src/Scintilla.h @@ -0,0 +1,233 @@ +// Scintilla source code edit control +// Scintilla.h - interface to the edit control +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef __SCINTILLA_H__ +#define __SCINTILLA_H__ + +#ifdef GTK +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SCINTILLA(obj) GTK_CHECK_CAST (obj, scintilla_get_type (), ScintillaObject) +#define SCINTILLA_CLASS(klass) GTK_CHECK_CLASS_CAS T (klass, scintilla_get_type (), ScintillaClass) +#define IS_SCINTILLA(obj) GTK_CHECK_TYPE (obj, scintilla_get_type ()) + + typedef struct _ScintillaObject ScintillaObject; + typedef struct _ScintillaClass ScintillaClass; + + struct _ScintillaObject + { + GtkFixed vbox; + void *pscin; + }; + + struct _ScintillaClass + { + GtkFixedClass parent_class; + + void (* command) (ScintillaObject *ttt); + void (* notify) (ScintillaObject *ttt); + }; + + guint scintilla_get_type (void); + GtkWidget* scintilla_new (void); + void scintilla_set_id (ScintillaObject *sci,int id); + long scintilla_send_message (ScintillaObject *sci,int iMessage,int wParam,int lParam); + +#include "WinDefs.h" + +#ifdef __cplusplus +} +#endif + +#endif + +// Both GTK and Windows + +// Define start of Scintilla messages to be greater than all edit (EM_*) messages +// as many EM_ messages can be used. +#define SCI_START 2000 + +#define SCI_ADDTEXT SCI_START + 1 +#define SCI_ADDSTYLEDTEXT SCI_START + 2 +#define SCI_INSERTTEXT SCI_START + 3 +#define SCI_CLEARALL SCI_START + 4 +#define SCI_GETLENGTH SCI_START + 6 +#define SCI_GETCHARAT SCI_START + 7 +#define SCI_GETCURRENTPOS SCI_START + 8 +#define SCI_GETANCHOR SCI_START + 9 +#define SCI_GETSTYLEAT SCI_START + 10 + +#define SCI_REDO SCI_START + 11 +#define SCI_SETUNDOCOLLECTION SCI_START + 12 +#define SCI_SELECTALL SCI_START + 13 +#define SCI_SETSAVEPOINT SCI_START + 14 +#define SCI_GETSTYLEDTEXT SCI_START + 15 + +#define SCI_GETVIEWWS SCI_START + 20 +#define SCI_SETVIEWWS SCI_START + 21 +#define SCI_CHANGEPOSITION SCI_START + 22 +#define SCI_GOTOLINE SCI_START + 24 +#define SCI_GOTOPOS SCI_START + 25 +#define SCI_SETANCHOR SCI_START + 26 +#define SCI_GETCURLINE SCI_START + 27 +#define SCI_GETENDSTYLED SCI_START + 28 + +#define SCI_GETEOLMODE SCI_START + 30 +#define SCI_SETEOLMODE SCI_START + 31 + +#define SC_EOL_CRLF 0 +#define SC_EOL_CR 1 +#define SC_EOL_LF 2 + +#define SCI_STARTSTYLING SCI_START + 32 +#define SCI_SETSTYLING SCI_START + 33 + +#define SCI_SETMARGINWIDTH SCI_START + 34 +#define SCI_SETBUFFEREDDRAW SCI_START + 35 +#define SCI_SETTABWIDTH SCI_START + 36 +#define SCI_SETCODEPAGE SCI_START + 37 +#define SCI_SETLINENUMBERWIDTH SCI_START + 38 +#define SCI_SETUSEPALETTE SCI_START + 39 + +#define MARKER_MAX 31 + +#define SC_MARK_CIRCLE 0 +#define SC_MARK_ROUNDRECT 1 +#define SC_MARK_ARROW 2 +#define SC_MARK_SMALLRECT 3 +#define SC_MARK_SHORTARROW 4 + +#define SCI_MARKERDEFINE SCI_START + 40 +#define SCI_MARKERSETFORE SCI_START + 41 +#define SCI_MARKERSETBACK SCI_START + 42 +#define SCI_MARKERADD SCI_START + 43 +#define SCI_MARKERDELETE SCI_START + 44 +#define SCI_MARKERDELETEALL SCI_START + 45 +#define SCI_MARKERGET SCI_START + 46 +#define SCI_MARKERNEXT SCI_START + 47 + +#define STYLE_MAX 31 + +#define SCI_STYLECLEARALL SCI_START + 50 +#define SCI_STYLESETFORE SCI_START + 51 +#define SCI_STYLESETBACK SCI_START + 52 +#define SCI_STYLESETBOLD SCI_START + 53 +#define SCI_STYLESETITALIC SCI_START + 54 +#define SCI_STYLESETSIZE SCI_START + 55 +#define SCI_STYLESETFONT SCI_START + 56 + +// Default style settings +#define SCI_SETFORE SCI_START + 60 +#define SCI_SETBACK SCI_START + 61 +#define SCI_SETBOLD SCI_START + 62 +#define SCI_SETITALIC SCI_START + 63 +#define SCI_SETSIZE SCI_START + 64 +#define SCI_SETFONT SCI_START + 65 +#define SCI_SETSELFORE SCI_START + 67 +#define SCI_SETSELBACK SCI_START + 68 +#define SCI_SETCARETFORE SCI_START + 69 + +#define SCI_ASSIGNCMDKEY SCI_START + 70 +#define SCI_CLEARCMDKEY SCI_START + 71 +#define SCI_CLEARALLCMDKEYS SCI_START + 72 + +#define SCI_SETSTYLINGEX SCI_START + 73 +#define SCI_APPENDUNDOSTARTACTION SCI_START + 74 + +#define INDIC_MAX 2 + +#define INDIC_PLAIN 0 +#define INDIC_SQUIGGLE 1 +#define INDIC_TT 2 + +#define INDIC0_MASK 32 +#define INDIC1_MASK 64 +#define INDIC2_MASK 128 +#define INDICS_MASK (INDIC0_MASK | INDIC1_MASK | INDIC2_MASK) + +#define SCI_INDICSETSTYLE SCI_START + 80 +#define SCI_INDICGETSTYLE SCI_START + 81 +#define SCI_INDICSETFORE SCI_START + 82 +#define SCI_INDICGETFORE SCI_START + 83 + +#define SCI_AUTOCSHOW SCI_START + 100 +#define SCI_AUTOCCANCEL SCI_START + 101 +#define SCI_AUTOCACTIVE SCI_START + 102 +#define SCI_AUTOCPOSSTART SCI_START + 103 +#define SCI_AUTOCCOMPLETE SCI_START + 104 +#define SCI_AUTOCSTOPS SCI_START + 105 + +#define SCI_CALLTIPSHOW SCI_START + 200 +#define SCI_CALLTIPCANCEL SCI_START + 201 +#define SCI_CALLTIPACTIVE SCI_START + 202 +#define SCI_CALLTIPPOSSTART SCI_START + 203 +#define SCI_CALLTIPSETHLT SCI_START + 204 + +// Key messages +#define SCI_LINEDOWN SCI_START + 300 +#define SCI_LINEDOWNEXTEND SCI_START + 301 +#define SCI_LINEUP SCI_START + 302 +#define SCI_LINEUPEXTEND SCI_START + 303 +#define SCI_CHARLEFT SCI_START + 304 +#define SCI_CHARLEFTEXTEND SCI_START + 305 +#define SCI_CHARRIGHT SCI_START + 306 +#define SCI_CHARRIGHTEXTEND SCI_START + 307 +#define SCI_WORDLEFT SCI_START + 308 +#define SCI_WORDLEFTEXTEND SCI_START + 309 +#define SCI_WORDRIGHT SCI_START + 310 +#define SCI_WORDRIGHTEXTEND SCI_START + 311 +#define SCI_HOME SCI_START + 312 +#define SCI_HOMEEXTEND SCI_START + 313 +#define SCI_LINEEND SCI_START + 314 +#define SCI_LINEENDEXTEND SCI_START + 315 +#define SCI_DOCUMENTSTART SCI_START + 316 +#define SCI_DOCUMENTSTARTEXTEND SCI_START + 317 +#define SCI_DOCUMENTEND SCI_START + 318 +#define SCI_DOCUMENTENDEXTEND SCI_START + 319 +#define SCI_PAGEUP SCI_START + 320 +#define SCI_PAGEUPEXTEND SCI_START + 321 +#define SCI_PAGEDOWN SCI_START + 322 +#define SCI_PAGEDOWNEXTEND SCI_START + 323 +#define SCI_EDITTOGGLEOVERTYPE SCI_START + 324 +#define SCI_CANCEL SCI_START + 325 +#define SCI_DELETEBACK SCI_START + 326 +#define SCI_TAB SCI_START + 327 +#define SCI_BACKTAB SCI_START + 328 +#define SCI_NEWLINE SCI_START + 329 +#define SCI_FORMFEED SCI_START + 330 +#define SCI_VCHOME SCI_START + 331 +#define SCI_VCHOMEEXTEND SCI_START + 332 + +// GTK+ Specific +#define SCI_GRABFOCUS SCI_START + 400 + +// Notifications + +struct SCNotification { + NMHDR nmhdr; + int position; + int ch; + int modifiers; +}; + +#define SCN_STYLENEEDED 2000 +#define SCN_CHARADDED 2001 +#define SCN_SAVEPOINTREACHED 2002 +#define SCN_SAVEPOINTLEFT 2003 +#define SCN_MODIFYATTEMPTRO 2004 + +// GTK+ Specific to work around focus and accelerator problems +#define SCN_KEY 2005 + +#ifdef STATIC_BUILD +void Scintilla_RegisterClasses(HINSTANCE hInstance); +#endif + +#endif diff --git a/Pythonwin/scintilla_src/Scintilla.html b/Pythonwin/scintilla_src/Scintilla.html new file mode 100644 index 0000000000..3cea43bc52 --- /dev/null +++ b/Pythonwin/scintilla_src/Scintilla.html @@ -0,0 +1,102 @@ + + + + +Scintilla and SciTE + + + + + + + +
+ + Scintilla and SciTE + + + + + + Release version 1.0.
+ Site last modified May 17 1999. +
+
+

+

+ Scintilla and SciTE are no longer beta - this means that I think + they are fairly stable and are unlikely to crash under normal + use. There will certainly still be bugs in there so watch out for them + and please send me mail if you see one.
+ I am going on an overseas holiday until mid September and will + not have an opportunity to do much coding work on this project + while away. + I'll still be reading my email whenever possible, although less + frequently, possibly once a week depending on where 'net access + can be found.
+ The name of the editor application has changed from "Tide" to + "SciTE" to avoid confusion with a TCL based IDE. +
+

+

+ Scintilla is a free source code editing component. + As well as features found in standard text editing components, Scintilla includes + features especially useful when editing and debugging source code. + These include support for syntax styling, error indicators, code completion and + call tips. + The selection margin can contain markers like those used + in debuggers to indicate breakpoints and the current line. + Styling choices are more open than with many editors, allowing the use of + proportional fonts, bold and italics, multiple foreground and background + colours and multiple fonts.
+ It comes with complete source code and may be used in any free project or + commercial product. +

+

+ SciTE is a SCIntilla based Text Editor. + Originally built to demonstrate Scintilla, it has grown to be a generally useful + editor with facilities for building and running programs. + It is best used for jobs with simple configurations - I use it for building test and + demonstration programs as well as SciTE and Scintilla, themselves. +

+

+ Development of Scintilla started as an effort to improve the text + editor in PythonWin. After being frustrated by problems in the Richedit + control used by PythonWin, it looked like the best way forward was to + write a new edit control. + The biggest problem with Richedit and other similar controls is that they treat + styling changes as important persistent changes to the document so they are + saved into the undo stack and set the document's dirty flag. For source code, + styling should not be persisted as it can be mechanically recreated.
+ PythonWin builds 124 + and 125 include copies of Scintilla.
+ In general, the best strategy to use with combining Scintilla and PythonWin + is to use the version of Scintilla included with PythonWin as newer versions + may not be compatible.
+ Scintilla 1.0 is not compatible with PythonWin 125, but should be + compatible with PythonWin 126. +

+

+ Scintilla and SciTE are currently available for Intel Win32 and Linux with GTK+. + They have been run on Windows 95, NT, and Red Hat Linux 4.2 with GTK+ 1.2.0. + Here is a screenshot of SciTE.
+

+

+ You can download + Scintilla and SciTE. +

+

+ Related sites. +

+

+ Bugs and To Do list. +

+

+ History. +

+

+ You can write to me, Neil Hodgson, + at neilh@hare.net.au. +

+ + diff --git a/Pythonwin/scintilla_src/ScintillaDoc.html b/Pythonwin/scintilla_src/ScintillaDoc.html new file mode 100644 index 0000000000..9ab03a142a --- /dev/null +++ b/Pythonwin/scintilla_src/ScintillaDoc.html @@ -0,0 +1,617 @@ + + + + +Scintilla and SciTE + + + + + +
+ + Scintilla + + +
+

+

Scintilla Documentation

+

+ For now, the best way to work out how to develop using Scintilla is to see + how SciTE uses it. SciTE exercises most of Scintilla's facilities. +

+

+ Scintilla is a Windows Control. + As such, its primary programming interface is + through Windows messages. It responds to many of the messages + defined for the standard Edit and Richedit controls as well as a suite of its own + for controlling syntax styling, markers, auto-completion and call tips. +

+ The GTK+ version also uses messages in a similar way to the Windows + version. This is different to normal GTK+ practice but made it easier + to implement rapidly. +

+ The messages are (with wParam and lParam use) + +

Text retrieval and modification.

+
+WM_GETTEXT(int length, char *text)
+WM_SETTEXT(<unused>, char *text)
+EM_GETLINE(int line, char *text)
+EM_REPLACESEL(<unused>, char *text)
+EM_SETREADONLY
+EM_GETTEXTRANGE(<unused>, TEXTRANGE *tr)
+SCI_ADDTEXT(char *s, int length)
+SCI_ADDSTYLEDTEXT(cell *s,int length)
+SCI_CLEARALL
+SCI_GETCHARAT(int position)
+SCI_GETSTYLEAT(int position)
+SCI_GETSTYLEDTEXT(<unused>, TEXTRANGE *tr)
+
+

+ Each character in a Scintilla document is followed by an associated byte + of styling information. + The combination of a character byte and a style byte is + called a cell. Style bytes are interpreted as a style index in the low 5 bits + and as 3 individual bits of indicators. This allows 32 fundamental styles + which is enough for most languages and three independant indicators so + that, for example, syntax errors, deprecated names and bad indentation + could all be displayed at once. Indicators may be displayed as simple + underlines, squiggly underlines or a line of small 'T' shapes. Additional + indicators such as strike-out or blurred could be defined in the future. +

+

+ Positions within the Scintilla document refer to a character or the + gap before that character. The caret exists between character positions and + can be located from before the first character to after the last character. There + are places where the caret can not go where two character bytes make up one + character. This occurs when a DBCS character from a language like Japanese + is included in the document or when line ends are marked with the + CP/M standard of a carriage return followed by a line feed. +

+

+ All lines of text in Scintilla are the same height, and this height is calculated + from the largest font in any current style. This restriction is for performance + as if lines differed in height then calculations involving positioning of text + would require that text to be styled first. +

+

Standard commands

+
+WM_CUT
+WM_COPY
+WM_PASTE
+WM_CLEAR
+
+

Undo and Redo

+
+WM_UNDO
+EM_CANUNDO
+EM_EMPTYUNDOBUFFER
+SCI_REDO
+SCI_SETUNDOCOLLECTION(bool docollection)
+
+

+ Scintilla has multiple level undo and redo. + It will continue to collect undoable actions until + memory runs out. Sequences of typing or deleting are compressed into single + actions to make it easier to undo and redo at a sensible level of detail. +

+

Selection and information

+
+WM_GETTEXTLENGTH
+EM_GETFIRSTVISIBLELINE
+EM_GETLINECOUNT
+EM_GETMODIFY
+EM_SETMODIFY(bool ismodified)
+EM_GETRECT(RECT *rect)
+EM_GETSEL(int *start, int *end)
+EM_EXGETSEL(int *start, int *end)
+EM_SETSEL(int start, int end)
+EM_EXSETSEL(int start, int end)
+EM_GETSELTEXT(<unused>, char *text)
+EM_LINEFROMCHAR(int position)
+EM_EXLINEFROMCHAR(int position)
+EM_LINEINDEX(int line)
+EM_LINELENGTH(int line)
+EM_SCROLL(int line)
+EM_CANPASTE
+EM_CHARFROMPOS(<unused>, POINT *location)
+EM_POSFROMCHAR(int position, POINT *location)
+EM_SELECTIONTYPE
+EM_HIDESELECTION(bool hide)
+SCI_GETLENGTH
+SCI_GETCURRENTPOS
+SCI_GETANCHOR
+SCI_SELECTALL
+SCI_CHANGEPOSITION(int delta, bool extendselection)
+SCI_PAGEMOVE(int cmdkey, bool extendselection)
+SCI_GOTOLINE(int line)
+SCI_GOTOPOS(int position)
+SCI_SETANCHOR(int position)
+SCI_GETCURLINE
+
+

+ Scintilla maintains a selection which stretches between two points, the anchor + and the current position. +

+

Searching

+
+EM_FINDTEXT(int flags, FINDTEXTEX *ft)
+EM_FINDTEXTEX(int flags, FINDTEXTEX *ft)
+
+

+ Scintilla can find where a string is present in its document. +

+

Visible white space

+
+SCI_GETVIEWWS
+SCI_SETVIEWWS(bool visisble)
+
+

+ White space can be made visible which may useful for languages in which + whitespace is significant, such as Python. + Space characters appear as small centred dots and tab characters + as rounded rectangles. +

+

Line endings

+
+SCI_GETEOLMODE
+SCI_SETEOLMODE(SC_EOL_CRLF or SC_EOL_CR or SC_EOL_LF)
+
+

+ Scintilla can interpret any of the three major line end conventions, + Macintosh (\r), Unix (\n) and CP/M (\r\n). When the user presses the Enter key, + one of these line end strings is inserted into the buffer. The default is \r\n, + but this can be changed with the SCI_SETEOLMODE message taking a wParam of + SC_EOL_CRLF, SC_EOL_CR, or SC_EOL_LF. The SCI_GETEOLMODE message + retrieves the current state. +

+

Styling

+
+SCI_GETENDSTYLED
+SCI_STARTSTYLING(int position, int mask)
+SCI_SETSTYLING(int length, int style)
+
+

+ Scintilla keeps a record of the last character that is likely to be styled correctly. + This is moved forwards when characters after it are styled and moved + backwards if changes are made to the text of the document before it. Before + drawing text, this position is checked to see if any styling is needed and + a notification message sent to the container if so. The container can + send SCI_GETENDSTYLED to work out where it needs to start styling. +

+

+ To perform the actual styling, SCI_STARTSTYLING is sent with the position + to start at and a mask indicating which bits of the style bytes can be set. + The mask allows styling to occur over several passes, with, for example, + basic styling done on an initial pass to ensure that the text of the code is seen + quickly and correctly, and then a second slower pass, detecting syntax + errors and using indicators to show where these are. After + SCI_STARTSTYLING, multiple SCI_SETSTYLING messages are sent for each + lexical entity to be styled. +

+

Style Definiition

+
+SCI_STYLECLEARALL
+SCI_STYLESETFORE(int stylenumber, int colour)
+SCI_STYLESETBACK(int stylenumber, int colour)
+SCI_STYLESETBOLD(int stylenumber, bool bold)
+SCI_STYLESETITALIC(int stylenumber, bool italic)
+SCI_STYLESETSIZE(int stylenumber, int sizeinpoints)
+SCI_STYLESETFONT(int stylenumber, char *fontname)
+
+

+ While the style setting messages mentioned above, change the style + numbers associated with text, these messages define how those style + numbers are interpreted visually. +

+
+SCI_SETFORE(int colour)
+SCI_SETBACK(int colour)
+SCI_SETBOLD(bool bold)
+SCI_SETITALIC(bool italic)
+SCI_SETSIZE(int sizeinpoints)
+SCI_SETFONT(char *fontname)
+
+

+ These messages are responsible for global default styling and are + used when no explicit setting is defined for a style. +

+
+SCI_SETSELFORE(int colour)
+SCI_SETSELBACK(int colour)
+SCI_SETCARETFORE(int colour)
+
+

+ The selection is shown by changing the foreground and / or background + colours. + If one of these is not set + then that attribute is not changed for the selection. The default is to show + the selection by changing the background to light grey and leaving the + foreground the same as when it was not selected. +

+

+ On GTK+, the colour of the caret can be set with SCI_SETCARETFORE. + This is to make up for the caret not blinking on GTK+ +

+

Other settings

+
+SCI_SETMARGINWIDTH(int pixelwidth)
+
+

+ Sets the width of the selection margin. + The selection margin is used both to make it easier to select + whole lines and to display markers which are mostly used in debuggers. + The selection margin may be turned off by setting it to zero width and then + markers are indicated by changing the background colour of the line + instead. +

+
+SCI_SETLINENUMBERWIDTH(int pixelwidth)
+
+

+ Sets the width of the line number margin to the left of the + selection margin. This is best used with severely retro + programming environments where all lines must be numbered + to allow them to be the target for goto statements. Early + rumours suggest that Python 2.0 will require line numbers. + The selection margin may be turned off by setting it to zero width. +

+
+SCI_SETUSEPALETTE(bool allowPaletteUse)
+
+

+ On 8 bit displays, which can only display a maximum of + 256 colours, the graphics environment mediates between + the colour needs of applications through the use of palettes. + On GTK+, Scintilla always uses a palette. + On Windows, there are some problems with + visual flashing when switching between applications with + palettes and it is also necessary for the application containing + the Scintilla control to forward some messages to Scintilla for + its palette code to work. +

+

+ Because of these issues, the application must tell Scintilla + to use a palette. If Scintilla is not using a palette, then it will + only be able to display in those colours already available, which + are often the 20 Windows system colours. +

+

+ To see an eample of how to enable palette support in Scintilla, + search the text of SciTE for WM_PALETTECHANGED, + WM_QUERYNEWPALETTE and SCI_SETUSEPALETTE. +

+
+SCI_SETBUFFEREDDRAW(bool isbuffered)
+
+

+ Turns on or off buffered drawing. + Buffered drawing draws each line into a bitmap rather than directly to the screen + and then copies the bitmap to the screen. This avoids flickering although it does + take longer. The default is for drawing to be buffered. +

+
+SCI_SETTABWIDTH(int widthinchars)
+
+

+ Sets the size of a tab as a multiple of the size of a space character in the style + of the first style definition +

+
+SCI_SETCODEPAGE(int codepage)
+
+

+ Scintilla has some very simple Japanese DBCS (and probably Chinese and Korean) + support. Use this message with wParam set to the code page number to set + Scintilla to use code page information to ensure double byte characters are + treated as one character rather than two. This also stops the caret from moving + between the two bytes in a double byte character. Call with wParam set to zero + to disable DBCS support. +

+
+SCI_GRABFOCUS
+
+

+ On GTK+, focus handling is more complicated than on Windows, + so Scintilla can be told with this message to grab the focus. +

+

Markers

+
+SCI_MARKERDEFINE(int markernumber, int markersymbols)
+SCI_MARKERSETFORE(int markernumber, int colour)
+SCI_MARKERSETBACK(int markernumber, int colour)
+SCI_MARKERADD(int line, int markernumber)
+SCI_MARKERDELETE(int line, int markernumber)
+SCI_MARKERDELETEALL(int markernumber)
+SCI_MARKERGET(int line)
+SCI_MARKERNEXT(int lineStart, int markermask)
+
+

+ Markers appear in the selection margin to the left of the text. They are small + geometric symbols often used in debuggers to indicate breakpoints + and the current line. If the selection margin is set to zero width then + the background colour of the whole line is changed instead. + There may be upto 32 marker symbols defined and each line has a + set of these markers associated with it. + The markers are drawn in the order of their numbers. + Markers try to move with their text by tracking where the start + of their line moves. When a line is deleted, its markers are combined, + by an or operation, with the markers of the previous line. + The SCI_MARKERDELETEALL treats a parameter of -1 as meaning delete all + markers from all lines. +

+

+ SCI_MARKERGET retrieves the set of markers associated with + a line. SCI_MARKERNEXT can be used to efficiently search + for lines that contain markers. It returns the next line with + a set of markers that includes some of the bits set in the + markermask parameter. +

+

+ The marker symbols currently available are SC_MARK_CIRCLE, + SC_MARK_ROUNDRECT, SC_MARK_ARROW, SC_MARK_SMALLRECT, + SC_MARK_SHORTARROW. +

+

Indicators

+
+SCI_INDICSETSTYLE(int indicatornumber, int indicatorstyle)
+SCI_INDICGETSTYLE(int indicatornumber)
+SCI_INDICSETFORE(int indicatornumber, int colour)
+SCI_INDICGETFORE(int indicatornumber)
+
+

+ These messages allow setting the visual appearance of the three (0, 1, and 2) + available indicators. +

+

+ The indicator styles currently available are INDIC_PLAIN, INDIC_SQUIGGLE, and + INDIC_TT. +

+

Autocompletion

+
+SCI_AUTOCSHOW(<unused>,char *list)
+SCI_AUTOCCANCEL
+SCI_AUTOCACTIVE
+SCI_AUTOCPOSSTART
+SCI_AUTOCCOMPLETE
+SCI_AUTOCSTOPS(<unused>,char *chars)
+
+

+ Auto completion displays a list box based upon the users + typing showing likely identifiers. The SCI_AUTOCSHOW message + causes this list to be displayed, with its argument being a list of words + separated by space characters. SCI_AUTOCPOSSTART returns the value + of the current position when SCI_AUTOCSHOW started display of the list. +

+

+ The current selection can be triggered with the SCI_AUTOCCOMPLETE + message. This has the same effect as the tab key. + When in autocompletion mode, the list should disappear when + the user types a character that can not be part of the autocompletion, + such as '.', '(' or '[' when typing an identifier. A set of characters which + will cancel autocompletion can be specified with the SCI_AUTOCSTOPS. +

+

Calltips

+
+SCI_CALLTIPSHOW(<unused>, char *definition)
+SCI_CALLTIPCANCEL
+SCI_CALLTIPACTIVE
+SCI_CALLTIPPOSSTART
+SCI_CALLTIPSETHLT(int highlightstart, int highlightend)
+
+

+ Call tips are small windows displaying the arguments to a function + and are displayed after the user has typed the name of the function. + As the user types values for each argument, the name of the argument + currently being entered is highlighted. +

+

+ SCI_CALLTIPSHOW starts the process by displaying the calltip window, with + the definition argument containing the text to display. + SCI_CALLTIPPOSSTART returns the value + of the current position when SCI_CALLTIPSHOW started display of the list. + SCI_CALLTIPSETHLT sets the region of the calltip text displayed in a + highlighted style. +

+

Keyboard Commands

+
+SCI_LINEDOWN
+SCI_LINEDOWNEXTEND
+SCI_LINEUP
+SCI_LINEUPEXTEND
+SCI_CHARLEFT
+SCI_CHARLEFTEXTEND
+SCI_CHARRIGHT
+SCI_CHARRIGHTEXTEND
+SCI_WORDLEFT
+SCI_WORDLEFTEXTEND
+SCI_WORDRIGHT
+SCI_WORDRIGHTEXTEND
+SCI_HOME
+SCI_HOMEEXTEND
+SCI_LINEEND
+SCI_LINEENDEXTEND
+SCI_DOCUMENTSTART
+SCI_DOCUMENTSTARTEXTEND
+SCI_DOCUMENTEND
+SCI_DOCUMENTENDEXTEND
+SCI_PAGEUP
+SCI_PAGEUPEXTEND
+SCI_PAGEDOWN
+SCI_PAGEDOWNEXTEND
+SCI_EDITTOGGLEOVERTYPE
+SCI_CANCEL
+SCI_DELETEBACK
+SCI_TAB
+SCI_BACKTAB
+SCI_NEWLINE
+SCI_FORMFEED
+SCI_VCHOME
+SCI_VCHOMEEXTEND
+
+

+ To allow the container application to perform any of the actions available + to the user with keyboard, all the keyboard actions are now messages. + They do not take any parameters. +

+

+ These commands are also used when redefining the key bindings with the + SCI_ASSIGNCMDKEY message. +

+

Key Bindings

+
+SCI_ASSIGNCMDKEY((short key,short modifiers), int message)
+SCI_CLEARCMDKEY((short key,short modifiers))
+SCI_CLEARALLCMDKEYS
+
+

+ There is a default binding of keys to commands in Scintilla which can be + overridden with these messages. + To fit the parameters into a message, the wParam contains the key code + in the low word and the key modifiers (possibly shift and control) in the + high word. The key code is from the VK_* enumeration, and the modifiers + are a combination of zero or more of SHIFT_PRESSED and + LEFT_CTRL_PRESSED. +

+

Notifications

+

+ Notifications are sent (fired) from the Scintilla control to its container + when an event has occurred that may interest the container. + Notifications are sent using the WM_NOTIFY message with a structure + containing information about the event. +

+
+SCN_STYLENEEDED(int endstyleneeded)
+
+

+ Before displaying a page, this message is sent to the container. + It is a good opportunity for the container to ensure that syntax styling + information for the visible text. +

+
+SCN_CHARADDED(int charadded)
+
+

+ Fired when the user types an ordinary text character (as opposed to a command + character) which is entered into the text. Can be used by the container to decide + to display a call tip or auto completion list. +

+
+EN_CHANGE
+
+

+ Fired when the text of the document has been changed for any reason. + This notification is sent using the WM_COMMAND message as this is the + behaviour of the standard edit control. +

+
+SCN_SAVEPOINTREACHED(int issavepoint)
+SCI_SETSAVEPOINT
+
+

+ Sent to the container when the savepoint is entered or left, allowing + the container to to display a dirty indicator and change its menues. + The wParam parameter is 1 when entering the save point, 0 when leaving. +

+

+ The container tells Scintilla where the save point is by sending the + SCI_SETSAVEPOINT message. +

+
+SCN_MODIFYATTEMPTRO
+
+

+ When in read-only mode, this notification is sent to the + container should the user try to edit the document. This can be used to check the + document out of a version control system. +

+

Edit messages not supported by Scintilla

+
+EM_GETWORDBREAKPROC EM_GETWORDBREAKPROCEX
+EM_SETWORDBREAKPROC EM_SETWORDBREAKPROCEX
+EM_GETWORDWRAPMODE EM_SETWORDWRAPMODE
+EM_LIMITTEXT EM_EXLIMITTEXT
+EM_SETRECT EM_SETRECTNP
+EM_FMTLINES
+EM_GETHANDLE EM_SETHANDLE
+EM_GETPASSWORDCHAR EM_SETPASSWORDCHAR
+EM_SETTABSTOPS
+EM_FINDWORDBREAK
+EM_GETCHARFORMAT EM_SETCHARFORMAT
+EM_GETOLEINTERFACE EM_SETOLEINTERFACE
+EM_SETOLECALLBACK
+EM_GETPARAFORMAT EM_SETPARAFORMAT
+EM_PASTESPECIAL
+EM_REQUESTRESIZE
+EM_GETBKGNDCOLOR EM_SETBKGNDCOLOR
+EM_STREAMIN EM_STREAMOUT
+EM_GETIMECOLOR EM_SETIMECOLOR
+EM_GETIMEOPTIONS EM_SETIMEOPTIONS
+EM_GETMARGINS EM_SETMARGINS
+EM_GETOPTIONS EM_SETOPTIONS
+EM_GETPUNCTUATION EM_SETPUNCTUATION
+EM_GETTHUMB
+
+

+ Scintilla tries to be a superset of the standard windows Edit and Richedit + controls wherever that makes sense. As it is not intended for use in a word + processor, some edit messages can not be sensibly handled. + Unsupported messages have no effect. +

+
+EM_GETEVENTMASK
+EM_SETEVENTMASK
+EM_DISPLAYBAND
+EM_FORMATRANGE
+EM_SETTARGETDEVICE
+
+

+ To support printing and control the notifications fired, these messages + should be supported but are not yet. +

+

Building Scintilla on Windows

+

+ Scintilla and SciTE have been developed using Mingw32 EGCS 1.1.2 and most + testing has occurred on executables produced by this compiler. It may also be + compiled with Visual C++ which leads to smaller executables. +

+

+ To compile with EGCS, use make on the "makefile" file.
+

+

+ To compile with VC++, use nmake on the "makefile_vc" file. +

+

+ There is a problem with Scintilla.DLL not being relocatable + when compiled with EGCS 1.1.2 on Windows 95. + If you want to distribute + Scintilla, you can build with EGCS on Windows NT, or with + Visual C++, or with Paul Sokolovsky's updated ld components + and it should then be relocatable. The updated ld components + can be downloaded from
+ ftp://ftp.is.lg.ua/pub/gnuwin32/alpha/bfd-dll.zip
+ ftp://ftp.is.lg.ua/pub/gnuwin32/alpha/ld-mingw32-replacement.zip
+ The FTP server is a little unusual and works best with a + command line client. +

+

+ Scintilla can also be linked into an application statically. + To do this, the symbol STATIC_BUILD should be defined and the + function Scintilla_RegisterClasses called to initialise Scintilla. + The Tidy.EXE target in the makefile demonstrates building a + statically linked version of SciTE. +

+

Building Scintilla with GTK+ on Linux

+

+ On Linux, Scintilla and SciTE have been built with GCC and linked with GTK+ 1.20. + GTK+ 1.0x will not work (and when it did it was very slow). + The current make file only supports static linking between SciTE and Scintilla. + The Makefile_gtk file is used to build SciTE, it can be invoked as:
+ make -f Makefile_gtk
+ The SciTEGTK.properties file is better than the SciTEGlobal.properties for use + on Linux/GTK+ as it specifies fonts that are likely to be installed. Under Linux, + SciTE reads its SciTEGlobal.properties file from the user's home directory. +

+ + diff --git a/Pythonwin/scintilla_src/ScintillaDownload.html b/Pythonwin/scintilla_src/ScintillaDownload.html new file mode 100644 index 0000000000..9f9343cb68 --- /dev/null +++ b/Pythonwin/scintilla_src/ScintillaDownload.html @@ -0,0 +1,51 @@ + + + + +Scintilla and SciTE + + + + + +
+ + Scintilla and SciTE + + +
+

+

Download.

+

+ The license for using Scintilla is + similar to that of Python containing very few restrictions. +

+

Release 1.0

+

+ You can download a ZIP file (190K) + containing the C++ source code and executables for Scintilla and SciTE. + While a release version, it is likely that there are still bugs in the code + that could lead to loss of data. Please make sure you save often and back + up your source code.
+ After downloading the file, unzip it, and run SciTE.EXE. The files required to run + SciTE are SciTE.EXE, Scintilla.DLL, and + SciTEGlobal.properties + and these are best located in one directory on the path. + To see a demonstration of some more of Scintilla's features open the + demo.pyro file which changes SciTE into demonstration mode. +

+ There is a GTK+/Linux version. + In the Linux tradition, this download only contains source code + and it is exactly the same source code as in the Windows version. + To build it, you will have to have a c++ compiler + (I use gcc 2.8, but egcs should do just as well) and GTK+ 1.2.0 installed.
+ The Makefile_gtk file is used to build SciTE, it can be invoked as:
+ make -f Makefile_gtk
+ The SciTEGTK.properties file is better than the SciTEGlobal.properties for use + on Linux/GTK+ as it specifies fonts that are likely to be installed. Under Linux, + SciTE reads its SciTEGlobal.properties file from the user's home directory. +

+ A previous release, 0.82, can also be downloaded. +

+ + diff --git a/Pythonwin/scintilla_src/ScintillaHistory.html b/Pythonwin/scintilla_src/ScintillaHistory.html new file mode 100644 index 0000000000..ed2e9e187d --- /dev/null +++ b/Pythonwin/scintilla_src/ScintillaHistory.html @@ -0,0 +1,91 @@ + + + + +Scintilla and SciTE + + + + + +
+ + Scintilla and SciTE + + +
+

+

History of Scintilla and SciTE

+

Release 1.0

+

Released on 17 May 1999.

+

Changed name of "Tide" to "SciTE" to avoid clash with a TCL based + IDE. "SciTE" is a SCIntilla based Text Editor and is Latin meaning + something like "understanding in a neat way" and is also an Old + English version of the word "shit".

+

There is a SCI_AUTOCSTOPS message for defining a string of characters + that will stop autocompletion mode. Autocompletion mode is cancelled when + any cursor movement occurs apart from backspace.

+

GTK+ version now splits horizontally as well as vertically and all dialogs + cancel when the escape key is pressed. +

+

Beta release 0.93

+

Released on 12 May 1999.

+

A bit more robust than 0.92 and supports SCI_MARKERNEXT + message. +

+

Beta release 0.92

+

Released on 11 May 1999.

+

GTK+ version now contains all features of Windows version + with some very small differences. Executing programs works much better now.

+

New palette code to allow more colours to be displayed in 256 colour + screen modes. A line number column can be displayed to the left of the + selection margin.

+

The code that maps from line numbers to text positions and back + has been completely rewritten to be faster, and to allow markers to move + with the text.

+

Beta release 0.91

+

Released on 30 April 1999, containing fixes to text measuring to make Scintilla work + better with bitmap fonts. Also some small fixes to make compiling work with Visual C++. +

+

Beta release 0.90

+

Released on 29 April 1999, containing working GTK+/Linux version. +

+

The Java, C++ and Python lexers recognise operators as distinct from + default allowing them to be highlighted. +

+

Beta release 0.82

+

Released on 1 April 1999, to fix a problem with handling the Enter key in PythonWin. + Also fixes some problems with cmd key mapping. +

+

Beta release 0.81

+

Released on 30th March 1999, containing bug fixes and a few more features.

+

Static linking supported and Tidy.EXE, a statically linked version of Tide.EXE. + Changes to compiler flags in the makefiles to optimise for size.

+

Scintilla supports a 'savepoint' in the undo stack which can be set by the + container when the document is saved. + Notifications are sent to the container when the savepoint is entered or left, allowing + the container to to display a dirty indicator and change its menues.

+

When Scintilla is set to read-only mode, a notification is sent to the + container should the user try to edit the document. This can be used to check the + document out of a version control system.

+

There is an API for setting the appearence of indicators.

+

The keyboard mapping can be redefined or removed so it can be + implemented completely by the container. All of the keyboard commands are now + commands which can be sent by the container.

+

A home command like Visual C++ with one hit going to the start of the text on the + line and the next going to the left margin is available. I do not personally like this + but my fingers have become trained to it by much repetition.

+

SCI_MARKERDELETEALL has an argument in wParam which is the number of the type + marker to delete with -1 performing the old action of removing all marker types.

+

Tide now understands both the file name and line numbers in error messages in + most cases.

+

Tide remembers the current lines of files in the recently used list.

+

Tide has a Find in Files command.

+

Beta release 0.80

+

This was the first public release on 14th March 1999, containing a mostly working Win32 + Scintilla DLL and Tide EXE.

+

Beta releases of SciTE were called Tide

+

+

+ + diff --git a/Pythonwin/scintilla_src/ScintillaRelated.html b/Pythonwin/scintilla_src/ScintillaRelated.html new file mode 100644 index 0000000000..ab87b51388 --- /dev/null +++ b/Pythonwin/scintilla_src/ScintillaRelated.html @@ -0,0 +1,75 @@ + + + + +Scintilla and SciTE + + + + +
+ + Scintilla and SciTE + + +
+

+

Related Sites

+

Editing Components

+

+ Jedit + is a good Open Source syntax colouring editor writen in and for Java. +

+

+ GTK+, the GIMP Toolkit, contains a + rich text editing widget.
+ Gedit is an editor for GTK+/GNOME.
+ GtkEditor is a source + code editing widget based on the GTK+ text widget.
+ GIDE is an IDE based on GTK+. +

+

+ CodeGuru has source code for several Win32 MFC based editors. +

+

Paper Documents

+

+Data Structures in a Bit-Mapped Text Editor, Wilfred J. Hanson, +Byte January 1987 +

+

+Text Editors: Algorithms and Architectures, Ray Valdés, +Dr. Dobbs Journal April 1993 +

+

+Macintosh User Interface Guidelines and TextEdit chapters of Inside Macintosh +

+

Development Tools

+

+ Scintilla and SciTE were developed using the Mingw32 version of EGCS. + + Mumit Khan's GNU Win32 site is a good starting point for EGCS and + Mingw32 information and downloads. +

+

+ AStyle + is a source code formatter for C++ and Java code. + SciTE will be extended to make it easy to integrate filters like AStyle. +

+

+ Python + is my favourite programming language. + Scintilla was started after I tried to improve the editor built into + PythonWin, a Win32 IDE for Python, but was frustrated + by the limitations of the Windows Richedit control which PythonWin uses. +

+

+ CodeMagic + is a free generic IDE for Win32. Strongly Perl focused but customisable + for other languages. Has more user interface features than SciTE. +

+

+ Coding frenzy musical inspiration provided by the + Flower Kings. +

+ + diff --git a/Pythonwin/scintilla_src/ScintillaToDo.html b/Pythonwin/scintilla_src/ScintillaToDo.html new file mode 100644 index 0000000000..38a094f05d --- /dev/null +++ b/Pythonwin/scintilla_src/ScintillaToDo.html @@ -0,0 +1,82 @@ + + + + +Scintilla and SciTE + + + + + +
+ + Scintilla and SciTE + + +
+

+

Bugs and To Do List

+

Scintilla Bugs

+

Drag and Drop does not work

+

At the end of italics style runs characters can be chopped off. + An example is using Verdana 12 point italics for strings makes + an ending double quote half visible and an ending single quote invisible. + This is hard to solve completely, may be better to avoid these situations + by, for example, choosing a font like Times New Roman for strings.

+

Dragging over bold text in some fonts will ripple because of the + difference in size between drawing all of a string at once and drawing it in + parts.

+ +

SciTE Bugs

+

When a file is opened by the user typing the filename, the case + of the name on disk is not checked. This can lead to files being saved + with wrong capitalisation which upsets Java.

+

File pattern expressions in the properties file only seem to work for + the *.extension form - for example, makefiles are not displayed correctly.

+

There has been a report of SciTE exiting as soon as the mouse is + moved over it when launched Start | Run. I have not yet reproduced this. +

+ +

GTK+ Version Bugs

+

The caret does not blink. This is considered an asset by some. A future version + will allow blinking or nonblinking carets with a choice of caret colour + on both GTK+ and Windows.

+ +

Scintilla To Do

+

Columnar selection.

+

Printing support.

+

Simple pattern based styling.

+

Regular expressions in find functions.

+

Performance.

+

Line wrapping.

+

Different height lines based upon tallest text on the line rather than on + the tallest style possible.

+

SciTE To Do

+

Printing support.

+

HTML styling. Even better would be styling that understands embedded + scripts and styles them properly.

+

Remember last chosen items as defaults such as the file type in the + open file dialog.

+

Support simple auto-completion / call tips.

+

Should check if current file has meen modified by another process and reload it.

+

Allow adding new items to tools menu.

+ +

Directions

+

+ The main point of this development is Scintilla, and this is where most effort + will go. SciTE will get new features, but only when they make my life easier - I am not + intending to make it grow up to be a huge full-function IDE like Visual Cafe. +

+

+ For GTK+ support, much new code has been added with many #ifdef statements + to choose between Windows and GTK+. This is ugly, and will be replaced wherever + easy with the platform portability classes specified in Platform.h and + implemented in either PlatWin.cc and PlatGTK.cc depending on the platform. +

+

+ If you are interested in contributing code, do not feel any need to make it cross + platform. Just code it for your platform and I'll either reimplement for the other + platform or ensure that there is no effect on the other platform. +

+ + diff --git a/Pythonwin/scintilla_src/WinDefs.h b/Pythonwin/scintilla_src/WinDefs.h new file mode 100644 index 0000000000..eb9cf364d5 --- /dev/null +++ b/Pythonwin/scintilla_src/WinDefs.h @@ -0,0 +1,181 @@ +// Scintilla source code edit control +// WinDefs.h - the subset of definitions from Windows needed by Scintilla for GTK+ +// Copyright 1998-1999 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef __WINDEFS_H__ +#define __WINDEFS_H__ + +#define WORD short +#define WPARAM long +#define LPARAM long + +#define HWND GtkWidget* +#define UINT unsigned int + +/* RTF control */ +#define EM_CANPASTE (1074) +#define EM_CANUNDO (198) +#define EM_CHARFROMPOS (215) +#define EM_DISPLAYBAND (1075) +#define EM_EMPTYUNDOBUFFER (205) +#define EM_EXGETSEL (1076) +#define EM_EXLIMITTEXT (1077) +#define EM_EXLINEFROMCHAR (1078) +#define EM_EXSETSEL (1079) +#define EM_FINDTEXT (1080) +#define EM_FINDTEXTEX (1103) +#define EM_FINDWORDBREAK (1100) +#define EM_FMTLINES (200) +#define EM_FORMATRANGE (1081) +#define EM_GETCHARFORMAT (1082) +#define EM_GETEVENTMASK (1083) +#define EM_GETFIRSTVISIBLELINE (206) +#define EM_GETHANDLE (189) +#define EM_GETLIMITTEXT (213) +#define EM_GETLINE (196) +#define EM_GETLINECOUNT (186) +#define EM_GETMARGINS (212) +#define EM_GETMODIFY (184) +#define EM_GETIMECOLOR (1129) +#define EM_GETIMEOPTIONS (1131) +#define EM_GETOPTIONS (1102) +#define EM_GETOLEINTERFACE (1084) +#define EM_GETPARAFORMAT (1085) +#define EM_GETPASSWORDCHAR (210) +#define EM_GETPUNCTUATION (1125) +#define EM_GETRECT (178) +#define EM_GETSEL (176) +#define EM_GETSELTEXT (1086) +#define EM_GETTEXTRANGE (1099) +#define EM_GETTHUMB (190) +#define EM_GETWORDBREAKPROC (209) +#define EM_GETWORDBREAKPROCEX (1104) +#define EM_GETWORDWRAPMODE (1127) +#define EM_HIDESELECTION (1087) +#define EM_LIMITTEXT (197) +#define EM_LINEFROMCHAR (201) +#define EM_LINEINDEX (187) +#define EM_LINELENGTH (193) +#define EM_LINESCROLL (182) +#define EM_PASTESPECIAL (1088) +#define EM_POSFROMCHAR (214) +#define EM_REPLACESEL (194) +#define EM_REQUESTRESIZE (1089) +#define EM_SCROLL (181) +#define EM_SCROLLCARET (183) +#define EM_SELECTIONTYPE (1090) +#define EM_SETBKGNDCOLOR (1091) +#define EM_SETCHARFORMAT (1092) +#define EM_SETEVENTMASK (1093) +#define EM_SETHANDLE (188) +#define EM_SETIMECOLOR (1128) +#define EM_SETIMEOPTIONS (1130) +#define EM_SETLIMITTEXT (197) +#define EM_SETMARGINS (211) +#define EM_SETMODIFY (185) +#define EM_SETOLECALLBACK (1094) +#define EM_SETOPTIONS (1101) +#define EM_SETPARAFORMAT (1095) +#define EM_SETPASSWORDCHAR (204) +#define EM_SETPUNCTUATION (1124) +#define EM_SETREADONLY (207) +#define EM_SETRECT (179) +#define EM_SETRECTNP (180) +#define EM_SETSEL (177) +#define EM_SETTABSTOPS (203) +#define EM_SETTARGETDEVICE (1096) +#define EM_SETWORDBREAKPROC (208) +#define EM_SETWORDBREAKPROCEX (1105) +#define EM_SETWORDWRAPMODE (1126) +#define EM_STREAMIN (1097) +#define EM_STREAMOUT (1098) +#define EM_UNDO (199) + +#define WM_NULL (0) +#define WM_CLEAR (771) +#define WM_COMMAND (273) +#define WM_COPY (769) +#define WM_CUT (768) +#define WM_GETTEXT (13) +#define WM_GETTEXTLENGTH (14) +#define WM_NOTIFY (78) +#define WM_PASTE (770) +#define WM_SETTEXT (12) +#define WM_UNDO (772) + +#define EN_CHANGE (768) + +#define VK_DOWN GDK_Down +#define VK_UP GDK_Up +#define VK_LEFT GDK_Left +#define VK_RIGHT GDK_Right +#define VK_HOME GDK_Home +#define VK_END GDK_End +#define VK_PRIOR GDK_Page_Up +#define VK_NEXT GDK_Page_Down +#define VK_DELETE GDK_Delete +#define VK_INSERT GDK_Insert +#define VK_ESCAPE GDK_Escape +#define VK_BACK GDK_BackSpace +#define VK_TAB GDK_Tab +#define VK_RETURN GDK_Return + +#define LPSTR char * +#define LONG long +#define LPDWORD (long *) + +/* SELCHANGE structure */ +#define SEL_EMPTY (0) +#define SEL_TEXT (1) +#define SEL_OBJECT (2) +#define SEL_MULTICHAR (4) +#define SEL_MULTIOBJECT (8) + +/* FINDREPLACE structure */ +#define FR_MATCHCASE (0x4) +#define FR_WHOLEWORD (0x2) + +#define SHIFT_PRESSED 1 +#define LEFT_CTRL_PRESSED 2 +#define LEFT_ALT_PRESSED 4 + +typedef struct _charrange { + LONG cpMin; + LONG cpMax; +} CHARRANGE; + +typedef struct _textrange { + CHARRANGE chrg; + LPSTR lpstrText; +} TEXTRANGE; + +typedef struct _findtextex { + CHARRANGE chrg; + LPSTR lpstrText; + CHARRANGE chrgText; +} FINDTEXTEX; + +typedef struct tagNMHDR { + HWND hwndFrom; + UINT idFrom; + UINT code; +} NMHDR; + +// MessageBox +#define MB_OK (0L) +#define MB_YESNO (0x4L) +#define MB_YESNOCANCEL (0x3L) +#define MB_ICONWARNING (0x30L) +#define IDOK (1) +#define IDCANCEL (2) +#define IDYES (6) +#define IDNO (7) + +#define MAKELONG(a, b) ((a) | ((b) << 16)) +#define LOWORD(x) (x & 0xffff) +#define HIWORD(x) (x >> 16) + +#define InterlockedExchange(i, v) *i = v + +#endif diff --git a/Pythonwin/scintilla_src/makefile_mh b/Pythonwin/scintilla_src/makefile_mh new file mode 100644 index 0000000000..e0f4331889 --- /dev/null +++ b/Pythonwin/scintilla_src/makefile_mh @@ -0,0 +1,66 @@ +# Marks Hammond's version for Pythonwin. +# Builds the DLL into the same directory as the Pythonwin binaries. +# The scintilla directory should be under the Pythonwin directory. +# Thus, your tree should look like: +# Pythonwin\*.cpp - existing Pythonwin sources +# Pythonwin\build - Where pythonwin binaries go. +# Pythonwin\scintilla - Directory of this file and scintilla sources. +# +# This makefile will place binaries and obj files in the Pythonwin build tree. + +# Make file for Scintilla and Tide, Windows Visual C++ version +# Copyright 1998-1999 by Neil Hodgson +# The License.txt file describes the conditions under which this software may be distributed. +# This makefile is for using Visual C++ and nmake. + +TEMP_DIR=../Build/Temp/Scintilla +BUILD_DIR=../Build + +CC = cl +O = obj +PROG = Tide.EXE +PROGSTATIC = Tidy.EXE +COMPONENT = $(BUILD_DIR)/Scintilla.DLL +LDBASE = KERNEL32.lib USER32.lib GDI32.lib COMDLG32.lib WINMM.lib COMCTL32.lib ADVAPI32.lib + +!ifdef DEBUG +CPPFLAGS = /TP /MD /Zi /Fo$*.$(O) +LDFLAGS = $(LDBASE) /DEBUG +!else +CPPFLAGS = /TP /MD /Ox /Fo$*.$(O) +LDFLAGS = $(LDBASE) +!endif + +OBJS = Tide.$(O) KeyWords.$(O) PropSet.$(O) +OBJSSTATIC = Tidy.$(O) KeyWords.$(O) PropSet.$(O) ScintillaS.$(O) Document.$(O) CallTip.$(O) + +ALL: before $(COMPONENT) + +before: + @if not exist $(TEMP_DIR) mkdir "$(TEMP_DIR)" + @if not exist $(BUILD_DIR) mkdir "$(BUILD_DIR)" + +$(TEMP_DIR)/Tide.$(O): Tide.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/Tidy.$(O): Tide.cc + cl $(CPPFLAGS) /D STATIC_BUILD /c Tide.cc +$(TEMP_DIR)/KeyWords.$(O): KeyWords.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/PropSet.$(O): PropSet.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/Scintilla.$(O): Scintilla.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/ScintillaS.$(O): Scintilla.cc + cl $(CPPFLAGS) /D STATIC_BUILD /c Scintilla.cc /FoScintillaS.$(O) +$(TEMP_DIR)/Document.$(O): Document.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/CallTip.$(O): CallTip.cc + cl $(CPPFLAGS) /c $(*B).cc +$(TEMP_DIR)/PlatWin.$(O): PlatWin.cc + cl $(CPPFLAGS) /c $(*B).cc + +$(COMPONENT): $(TEMP_DIR)/Scintilla.$(O) $(TEMP_DIR)/PlatWin.$(O) $(TEMP_DIR)/Document.$(O) $(TEMP_DIR)/CallTip.$(O) $(TEMP_DIR)/ScintRes.res + link $** $(LDFLAGS) /DLL /OUT:$(COMPONENT) + +$(TEMP_DIR)/ScintRes.res: ScintRes.rc + rc /fo$*.res ScintRes.rc diff --git a/Pythonwin/stdafx.cpp b/Pythonwin/stdafx.cpp new file mode 100644 index 0000000000..05161d4433 --- /dev/null +++ b/Pythonwin/stdafx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// stdafx.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/Pythonwin/stdafx.h b/Pythonwin/stdafx.h new file mode 100644 index 0000000000..2adb9f70d5 --- /dev/null +++ b/Pythonwin/stdafx.h @@ -0,0 +1,56 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +//#define HIER_LIST Yay - no more - tree control now used! +#define WIN32_LEAN_AND_MEAN + +#include // MFC core and standard components + +#if (_MFC_VER < 0x0600) +// See pythonpsheet.cpp for more details! +#define _WIN32_IE 0x0300 // Screw up with header sizes and MFC!! +#endif + +#include // MFC extensions +#include // common controls. +#include // rich edit support. +#include // control view support. +#include // private MFC stuff! +#include // for collection templates. +#include // multi-threading +#include "limits.h" + +// allow memory leaks to give me the line number. +//#define new DEBUG_NEW + +/* dont really need to undef these anymore, but helpful to +programmers who forget to use the new names. */ +#undef INCREF +#undef DECREF +extern "C" { +#include "Python.h" +} +#include "modsupport.h" +#include "traceback.h" +#include "pythonrun.h" +#include "import.h" // Python: for dynamicattach routines. +#include "graminit.h" + +#include "pywintypes.h" +// dont need all of these for all, but it cant hurt (and keep the speed up!) + +#include "win32ui.h" + +#include "win32uiExt.h" + +#include "pythonframe.h" +#include "pythonview.h" // App: Edit View. +#include "pythondoc.h" + +#include "win32assoc.h" +#include "win32cmd.h" +#include "win32app.h" + +// --- EOF --- // diff --git a/Pythonwin/stdafxole.cpp b/Pythonwin/stdafxole.cpp new file mode 100644 index 0000000000..228d5a9bb7 --- /dev/null +++ b/Pythonwin/stdafxole.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// stdafx.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafxole.h" +#include "olectl.h" diff --git a/Pythonwin/stdafxole.h b/Pythonwin/stdafxole.h new file mode 100644 index 0000000000..7e1768e4bc --- /dev/null +++ b/Pythonwin/stdafxole.h @@ -0,0 +1,37 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#define WIN32_LEAN_AND_MEAN + +#include // MFC core and standard components +#include // MFC extensions +#include // common controls. +#include // rich edit support. +#include // control view support. +#include // private MFC stuff! +#include +#include + + +#include "limits.h" + +// allow memory leaks to give me the line number. +//#define new DEBUG_NEW + +extern "C" { +#include "Python.h" +} +#include "modsupport.h" +#include "traceback.h" +#include "pythonrun.h" + +// dont need all of these for all, but it cant hurt (and keep the speed up!) + +#include "win32ui.h" +#include "win32assoc.h" +#include "win32cmd.h" +#include "win32win.h" + +// --- EOF --- // diff --git a/Pythonwin/stdafxpw.cpp b/Pythonwin/stdafxpw.cpp new file mode 100644 index 0000000000..defa190757 --- /dev/null +++ b/Pythonwin/stdafxpw.cpp @@ -0,0 +1,7 @@ +// stdafxpw.cpp : source file that includes just the standard includes +// Python for Windows version. +// stdafxpw.pch will be the pre-compiled header +// stdafxpw.obj will contain the pre-compiled type information + +#include "stdafxpw.h" + diff --git a/Pythonwin/stdafxpw.h b/Pythonwin/stdafxpw.h new file mode 100644 index 0000000000..4786427df2 --- /dev/null +++ b/Pythonwin/stdafxpw.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// +#define HIER_LIST +#define WIN32_LEAN_AND_MEAN + +#include // MFC core and standard components +#include // MFC extensions +#include "afxpriv.h" // for mru class. + +#include "limits.h" + +// allow memory leaks to give me the line number. +#define new DEBUG_NEW diff --git a/Pythonwin/win32ImageList.cpp b/Pythonwin/win32ImageList.cpp new file mode 100644 index 0000000000..45a79cec87 --- /dev/null +++ b/Pythonwin/win32ImageList.cpp @@ -0,0 +1,258 @@ +/* win32ImageList : implementation file + + Image List object. + + Created Feb 1997, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32ImageList.h" + +///////////////////////////////////////////////////////////////////////// +// +// ImageList +PythonImageList::PythonImageList() +{ +} +PythonImageList::~PythonImageList() +{ + Python_delete_assoc(this); +} +#ifdef _DEBUG +void PythonImageList::Dump( CDumpContext &dc ) const +{ + CImageList::Dump(dc); + DumpAssocPyObject(dc, (void *)this); +} +#endif + +PyCImageList::PyCImageList() +{ +} +PyCImageList::~PyCImageList() +{ +} + +CImageList *PyCImageList::GetImageList(PyObject *self) +{ + return (CImageList *)GetGoodCppObject(self, &type); +} + +// @pymethod int|win32ui|CreateImageList|Creates an image list. +PyObject *PyCImageList_Create( PyObject *self, PyObject *args ) +{ + BOOL bRet; + int cx, cy, nInitial, nGrow, nBitmap; + char *strBitmap; + COLORREF crMask; + BOOL bMask; + CImageList *pList = new PythonImageList(); + if (PyArg_ParseTuple(args, "iiiii", + &cx, // @pyparm int|cx||Dimension of each image, in pixels. + &cy, // @pyparm int|cy||Dimension of each image, in pixels. + &bMask, // @pyparm int|mask||TRUE if the image contains a mask; otherwise FALSE. + &nInitial, // @pyparm int|initial||Number of images that the image list initially contains. + &nGrow)) {// @pyparm int|grow||Number of images by which the image list can grow when the system needs to resize the list to make room for new images. This parameter represents the number of new images the resized image list can contain. + bRet = pList->Create(cx, cy, bMask, nInitial, nGrow); + } else if (PyArg_ParseTuple(args, "iiii", + &nBitmap, // @pyparmalt1 int|bitmapId||Resource ID of the bitmap to be associated with the image list. + &cx, // @pyparmalt1 int|cx||Dimension of each image, in pixels. + &nGrow, // @pyparmalt1 int|grow||Number of images by which the image list can grow when the system needs to resize the list to make room for new images. This parameter represents the number of new images the resized image list can contain. + &crMask)) { // @pyparmalt1 int|crMask||Color used to generate a mask. Each pixel of this color in the specified bitmap is changed to black, and the corresponding bit in the mask is set to one. + bRet = pList->Create(nBitmap,cx,nGrow,crMask); + } else if (PyArg_ParseTuple(args, "siii", + &strBitmap, // @pyparmalt2 int|bitmapId||Resource ID of the bitmap to be associated with the image list. + &cx, // @pyparmalt2 int|cx||Dimension of each image, in pixels. + &nGrow, // @pyparmalt2 int|grow||Number of images by which the image list can grow when the system needs to resize the list to make room for new images. This parameter represents the number of new images the resized image list can contain. + &crMask)) { // @pyparmalt2 int|crMask||Color used to generate a mask. Each pixel of this color in the specified bitmap is changed to black, and the corresponding bit in the mask is set to one. + bRet = pList->Create(strBitmap,cx,nGrow,crMask); + } else { + GUI_BGN_SAVE; + delete pList; + GUI_END_SAVE; + RETURN_ERR("PyCImageList::Create() - bad argument list"); + } + if (!bRet) { + GUI_BGN_SAVE; + delete pList; + GUI_END_SAVE; + RETURN_ERR("PyCImage::Create failed"); + } + return ui_assoc_object::make( PyCImageList::type, pList )->GetGoodRet(); +} + +// @pymethod |PyCImageList|DeleteImageList|Deletes an image list. +PyObject *PyCImageList_DeleteImageList( PyObject *self, PyObject *args ) +{ + // @comm This frees all resources associated with an image list. + // No further operations on the object will be allowed. + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + CHECK_NO_ARGS2(args, "DeleteImageList"); + // Kill the C++ object. + GUI_BGN_SAVE; + Python_delete_assoc(pList); + BOOL ok = pList->DeleteImageList(); + delete pList; + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteImageList failed"); + RETURN_NONE; +} + +// @pymethod int|PyCImageList|Add|Adds an image to the list. +PyObject *PyCImageList_Add( PyObject *self, PyObject *args ) +{ + HICON hIcon; + int bmp1, bmp2, mask; + CImageList *pList = PyCImageList::GetImageList(self); + int rc; + if (!pList) + return NULL; + if (PyArg_ParseTuple(args, "(ii)", + &bmp1, &bmp2)) {// @pyparm (int,int)|bitmap, bitmapMask||2 Bitmaps to use (primary and mask) + if (!IsGdiHandleValid((HANDLE)bmp1) || !IsGdiHandleValid((HANDLE)bmp2)) + RETURN_ERR("One of the bitmap handles is invalid"); + GUI_BGN_SAVE; + rc = pList->Add(CBitmap::FromHandle((HBITMAP)bmp1), CBitmap::FromHandle((HBITMAP)bmp2)); + GUI_END_SAVE; + } else if (PyArg_ParseTuple(args, "ii", + &bmp1, // @pyparmalt1 int|bitmap||Bitmap to use + &mask)) { // @pyparmalt1 int|color||Color to use for the mask. + if (!IsGdiHandleValid((HANDLE)bmp1)) + RETURN_ERR("The bitmap handle is invalid"); + GUI_BGN_SAVE; + rc = pList->Add(CBitmap::FromHandle((HBITMAP)bmp1), (COLORREF)mask); + GUI_END_SAVE; + } else if (PyArg_ParseTuple(args, "i", + &hIcon)) {// @pyparmalt2 int|hIcon||Handle of an icon to add. + GUI_BGN_SAVE; + rc = pList->Add(hIcon); + GUI_END_SAVE; + } else { + RETURN_ERR("Add requires '(hbitmap, hbitmap)', 'hbitmap, color' or 'hicon'"); + } + if (rc==-1) + RETURN_ERR("Add failed"); + return Py_BuildValue("i", rc); + // @rdesc Zero-based index of the first new image. +} +// @pymethod |PyCImageList|Destroy|Destroys the underlying CImageList +PyObject *PyCImageList_Destroy( PyObject *self, PyObject *args ) +{ + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + CHECK_NO_ARGS2(args, "Destroy"); + GUI_BGN_SAVE; + delete pList; + GUI_END_SAVE; + // @comm This method actually calls delete() on the CImageList - you + // should ensure that no controls still require access to this list. + RETURN_NONE; +} + +// @pymethod int|PyCImageList|GetBkColor|Retrieves the background color of an Image List. +PyObject *PyCImageList_GetBkColor( PyObject *self, PyObject *args ) +{ + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + CHECK_NO_ARGS2(args, "GetBkColor"); + GUI_BGN_SAVE; + int rc = pList->GetBkColor(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod |PyCImageList|SetBkColor|Sets the background color for an Image List. +PyObject *PyCImageList_SetBkColor( PyObject *self, PyObject *args ) +{ + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + int col; + // @pyparm int|color||The new background color. + if (!PyArg_ParseTuple( args, "i:SetBkColor", &col)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetBkColor(col); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetBkColor failed"); + RETURN_NONE; +} + +// @pymethod int|PyCImageList|GetSafeHandle|Retrieves the HIMAGELIST for the object +PyObject *PyCImageList_GetSafeHandle( PyObject *self, PyObject *args ) +{ + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + CHECK_NO_ARGS2(args, "GetSafeHandle"); + return Py_BuildValue("i", pList->GetSafeHandle()); +} + +// @pymethod int|PyCImageList|GetImageCount|Retrieves the number of images in an image list. +PyObject *PyCImageList_GetImageCount( PyObject *self, PyObject *args ) +{ + CImageList *pList; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + CHECK_NO_ARGS2(args, "GetImageCount"); + GUI_BGN_SAVE; + int rc = pList->GetImageCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod iiii(iiii)|PyCImageList|GetImageInfo|Retrieves information about an image. +PyObject *PyCImageList_GetImageInfo( PyObject *self, PyObject *args ) +{ + CImageList *pList; + int nIndex; + if (!(pList=PyCImageList::GetImageList(self))) + return NULL; + // @pyparm int|index||Index of image. + if (!PyArg_ParseTuple(args, "i:GetImageInfo", &nIndex)) + return NULL; + IMAGEINFO info; + GUI_BGN_SAVE; + BOOL ok = pList->GetImageInfo(nIndex, &info); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetImageInfo failed"); + return Py_BuildValue("iiii(iiii)", info.hbmImage, info.hbmMask, + info.Unused1, info.Unused2, + info.rcImage.left,info.rcImage.top,info.rcImage.right,info.rcImage.bottom ); + // @rdesc The return info is a tuple describing an IMAGELIST structure. +} + +// @object PyCImageList|A Python type encapsulating an MFC CImageList class. +static struct PyMethodDef PyCImageList_methods[] = { + {"Add", PyCImageList_Add, 1 }, // @pymeth Add|Adds an icon to the image list. + {"Destroy", PyCImageList_Destroy, 1}, // @pymeth Destroy|Destroys the underlying MFC imagelist object. + {"DeleteImageList",PyCImageList_DeleteImageList, 1}, // @pymeth DeleteImageList|Deletes an image list. + {"GetBkColor", PyCImageList_GetBkColor, 1}, // @pymeth GetBkColor|Retrieves the background color of an Image List. + {"GetSafeHandle", PyCImageList_GetSafeHandle, 1}, // @pymeth GetSafeHandle|Retrieves the HIMAGELIST for the object + {"GetImageCount", PyCImageList_GetImageCount, 1}, // @pymeth GetImageCount|Retrieves the number of images in an image list. + {"GetImageInfo", PyCImageList_GetImageInfo, 1}, // @pymeth GetImageInfo|Retrieves information about an image. + {"SetBkColor", PyCImageList_SetBkColor, 1}, // @pymeth SetBkColor|Sets the background color for an Image List. + {NULL, NULL } +}; + +ui_type_CObject PyCImageList::type("PyCImageList", + &ui_assoc_CObject::type, + RUNTIME_CLASS(CImageList), + sizeof(PyCImageList), + PyCImageList_methods, + GET_PY_CTOR(PyCImageList)); diff --git a/Pythonwin/win32ImageList.h b/Pythonwin/win32ImageList.h new file mode 100644 index 0000000000..8b4607b203 --- /dev/null +++ b/Pythonwin/win32ImageList.h @@ -0,0 +1,38 @@ +/* win32ImageList : header + + Image List object. + + Created May 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 + +*/ + +class PythonImageList : public CImageList +{ +public: + PythonImageList(); + ~PythonImageList(); +#ifdef _DEBUG + virtual void Dump( CDumpContext &dc ) const; +#endif +}; + +/////////////////////////////////////////////////////////////////////// +// ImageList +// + +class PYW_EXPORT PyCImageList : public ui_assoc_CObject{ +public: + MAKE_PY_CTOR(PyCImageList) + static CImageList *GetImageList(PyObject *self); + static ui_type_CObject type; +protected: + PyCImageList(); + virtual ~PyCImageList(); +}; diff --git a/Pythonwin/win32RichEdit.cpp b/Pythonwin/win32RichEdit.cpp new file mode 100644 index 0000000000..8521b3fd2c --- /dev/null +++ b/Pythonwin/win32RichEdit.cpp @@ -0,0 +1,212 @@ +/* win32RichEdit : implementation file + + Created March 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32control.h" +#include "win32RichEdit.h" + +typedef CPythonViewFramework< CRichEditView > CPythonRichEditView; +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +class CProtectedRichEditView : public CRichEditView +{ +public: + void WrapChanged(void) {CRichEditView::WrapChanged();} + void SetDocument( CDocument *pDoc ) {if (pDoc) ASSERT_VALID(pDoc);m_pDocument=pDoc;} +}; + +CRichEditView *GetRichEditViewPtr(PyObject *self) +{ + // need to only rtti check on CView, as CPythonEditView is not derived from CPythonView. + return (CRichEditView *)PyCWnd::GetPythonGenericWnd(self, &PyCRichEditView::type); +} + + +///////////////////////////////////////////////////////////////////// +// +// Rich Edit Document +// +////////////////////////////////////////////////////////////////////// +// @pymethod |PyCRichEditDoc|OnCloseDocument|Call the MFC OnCloseDocument handler. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_re_doc_on_close(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,OnCloseDocument); + CRichEditDoc *pDoc; + if (!(pDoc=(CRichEditDoc *)PyCRichEditDoc::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + pDoc->CRichEditDoc::OnCloseDocument(); // @pyseemfc CRichEditDoc|OnCloseDocument + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCRichEditDoc|A class which implements a CRichEditView object. Derived from . +static struct PyMethodDef PyCRichEditDoc_methods[] = { + {"OnCloseDocument", ui_re_doc_on_close, 1}, // @pymeth OnCloseDocument|Call the MFC OnCloseDocument handler. + {NULL, NULL} +}; + +ui_type_CObject PyCRichEditDoc::type("PyCRichEditDoc", + &PyCDocument::type, + RUNTIME_CLASS(CRichEditDoc), + sizeof(PyCRichEditDoc), + PyCRichEditDoc_methods, + GET_PY_CTOR(PyCRichEditDoc) ); + +///////////////////////////////////////////////////////////////////// +// +// Rich Edit View object +// +////////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|CreateRichEditView|Creates a PyRichEditView object. +PyObject * PyCRichEditView::create(PyObject *self, PyObject *args) +{ + PyObject *doc = Py_None; + // @pyparm |doc|None|The document to use with the view, or None for NULL. + if (!PyArg_ParseTuple(args, "|O:CreateEditView", &doc)) + return NULL; + CDocument *pDoc = NULL; + if (doc != Py_None) { + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + pDoc = PyCDocument::GetDoc( doc ); + if (!pDoc) return NULL; + } + CPythonRichEditView *pView = new CPythonRichEditView(); + ((CProtectedRichEditView *)pView)->SetDocument(pDoc); + return ui_assoc_object::make( PyCRichEditView::type, pView ); +} + +/////////////////////////////////////// +// +// Rich Edit View Methods +// +// inherited from CtlView +// +/////////////////////////////////////// +// @pymethod |PyCRichEditView|GetRichEditCtrl|Returns the underlying rich edit control object. +static PyObject * +PyCRichEditView_get_rich_edit_ctrl(PyObject *self, PyObject *args) +{ + CRichEditView *pView = GetRichEditViewPtr(self); + if (!pView) + return NULL; + CRichEditCtrl &ed = pView->GetRichEditCtrl(); + return ui_assoc_object::make( UITypeFromCObject(&ed), &ed )->GetGoodRet(); +} + +// @pymethod None|PyCRichEditView|SetWordWrap|Sets the wordwrap state for the control. +static PyObject * +PyCRichEditView_set_word_wrap(PyObject *self, PyObject *args) +{ + CRichEditView *pView = GetRichEditViewPtr(self); + if (!pView) + return NULL; + int wrap; + // @pyparm int|wordWrap||The new word-wrap state. + if (!PyArg_ParseTuple(args, "i:SetWordWrap", &wrap)) + return NULL; + // @pyseemfc CRichEditCtrl|m_nWordWrap + pView->m_nWordWrap = wrap; + RETURN_NONE; +} + +// @pymethod None|PyCRichEditView|WrapChanged|Calls the underlying WrapChanged method. +static PyObject * +PyCRichEditView_wrap_changed(PyObject *self, PyObject *args) +{ + CProtectedRichEditView *pView = (CProtectedRichEditView *)GetRichEditViewPtr(self); + if (!pView) + return NULL; + if (!PyArg_ParseTuple(args, ":WrapChanged")) + return NULL; + GUI_BGN_SAVE; + pView->WrapChanged(); + GUI_END_SAVE; + // @pyseemfc CRichEditCtrl|WrapChanged + RETURN_NONE; +} + +// @pymethod None|PyCRichEditView|SaveTextFile|Saves the contents of the control as a test file +// @comm Theere is no equivilent MFC method. This is implemented in this module for performance reasons. +static PyObject * +PyCRichEditView_save_text_file(PyObject *self, PyObject *args) +{ + // Ported from Python code! + char *fileName; + if (!PyArg_ParseTuple(args, "s:SaveTextFile", &fileName)) + return NULL; + + // Changing mode here allows us to save Unix or PC + FILE *f = fopen(fileName,"wb"); + if (f==NULL) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + CProtectedRichEditView *pView = (CProtectedRichEditView *)GetRichEditViewPtr(self); + CRichEditCtrl &ctrl = pView->GetRichEditCtrl(); + GUI_BGN_SAVE; + // The RTF control always has an "extra" \n + long lineCount = ctrl.GetLineCount(); + for (long i=0;i and . +static struct PyMethodDef PyCRichEditView_methods[] = { + {"GetRichEditCtrl", PyCRichEditView_get_rich_edit_ctrl, 1 }, // @pymeth GetRichEditCtrl|Returns the underlying rich edit control object. + {"SetWordWrap", PyCRichEditView_set_word_wrap, 1}, // @pymeth SetWordWrap|Sets the wordwrap state for the control. + {"WrapChanged", PyCRichEditView_wrap_changed, 1}, // @pymeth WrapChanged|Calls the underlying WrapChanged method. + {"SaveTextFile", PyCRichEditView_save_text_file, 1}, // @pymeth SaveTextFile|Saves the control to a text file + {NULL, NULL} +}; + +PyCCtrlView_Type PyCRichEditView::type("PyCRichEditView", &PyCCtrlView::type, &PyCRichEditCtrl::type, RUNTIME_CLASS(CRichEditView), sizeof(PyCRichEditView), PyCRichEditView_methods, GET_PY_CTOR(PyCRichEditView)); + diff --git a/Pythonwin/win32RichEdit.h b/Pythonwin/win32RichEdit.h new file mode 100644 index 0000000000..09b5edd103 --- /dev/null +++ b/Pythonwin/win32RichEdit.h @@ -0,0 +1,25 @@ +// win32RichText.h : header file +// +// +///////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// +// PyCRichEditDoc object +class PYW_EXPORT PyCRichEditDoc : public PyCDocument { +protected: +public: + MAKE_PY_CTOR(PyCRichEditDoc); + static ui_type_CObject type; +}; + + +// PyCRichEditView +class PYW_EXPORT PyCRichEditView : public PyCCtrlView { +public: + MAKE_PY_CTOR(PyCRichEditView) + static PyCCtrlView_Type type; + static PyObject *create(PyObject *self, PyObject *args); +protected: + PyCRichEditView() {return;} +}; + diff --git a/Pythonwin/win32RichEditDocTemplate.cpp b/Pythonwin/win32RichEditDocTemplate.cpp new file mode 100644 index 0000000000..596d46f7b6 --- /dev/null +++ b/Pythonwin/win32RichEditDocTemplate.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "win32win.h" +#include "win32doc.h" +#include "win32control.h" +#include "win32RichEdit.h" +#include "win32template.h" +#include "win32RichEditDocTemplate.h" +#include "pythondoc.h" +#include "pythonRichEditDoc.h" + +// @doc + +// @pymethod |win32ui|CreateRichEditDocTemplate|Creates a document template object. +PyObject * +PyCRichEditDocTemplate::create(PyObject *self, PyObject *args) +{ + UINT idResource; + // @pyparm int|idRes||The ID for resources for documents of this type. + if (!PyArg_ParseTuple(args,"i:CreateRichEditDocTemplate", &idResource)) + return NULL; + + CPythonDocTemplate *pMFCTemplate = new CPythonDocTemplate(idResource); + return ui_assoc_object::make(PyCRichEditDocTemplate::type, pMFCTemplate); +} + + +// @pymethod |PyCRichEditDocTemplate|DoCreateRichEditDoc|Creates an underlying document object. +PyObject * +PyCRichEditDocTemplate::DoCreateRichEditDoc(PyObject *self, PyObject *args) +{ + // @pyparm string|fileName|None|The name of the file to load. + return DoCreateDocHelper(self, args, RUNTIME_CLASS(CPythonRichEditDoc), PyCRichEditDoc::type); +} + + +// @object PyCRichEditDocTemplate|A document template class for OLE functionality. Encapsulates an MFC class +static struct PyMethodDef PyCRichEditDocTemplate_methods[] = { + {"DoCreateRichEditDoc",PyCRichEditDocTemplate::DoCreateRichEditDoc, 1}, // @pymeth DoCreateRichEditDoc|Creates an underlying document object. + {NULL, NULL} +}; + +ui_type_CObject PyCRichEditDocTemplate::type("PyCRichEditDocTemplate", + &PyCDocTemplate::type, + RUNTIME_CLASS(CDocTemplate), + sizeof(PyCRichEditDocTemplate), + PyCRichEditDocTemplate_methods, + GET_PY_CTOR(PyCRichEditDocTemplate) ); + diff --git a/Pythonwin/win32RichEditDocTemplate.h b/Pythonwin/win32RichEditDocTemplate.h new file mode 100644 index 0000000000..52b4f79fea --- /dev/null +++ b/Pythonwin/win32RichEditDocTemplate.h @@ -0,0 +1,11 @@ + +class PyCRichEditDocTemplate : public PyCDocTemplate { +protected: +public: + + static PyObject *create(PyObject *self, PyObject *args); + static PyObject *DoCreateRichEditDoc(PyObject *self, PyObject *args); + + static ui_type_CObject type; + MAKE_PY_CTOR(PyCRichEditDocTemplate) +}; diff --git a/Pythonwin/win32app.cpp b/Pythonwin/win32app.cpp new file mode 100644 index 0000000000..499762032b --- /dev/null +++ b/Pythonwin/win32app.cpp @@ -0,0 +1,405 @@ +/* + + win32 app data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32ui.h" +#include "win32doc.h" +#include "win32template.h" + +extern CWnd *GetWndPtr(PyObject *self); + +PyObject *PyCWinApp::pExistingAppObject = NULL; +char *errmsgAlreadyInit = "The application has already been initialised"; + +///////////////////////////////////////////////////////////////////// +// +// CProtectedWinApp Application helpers. +// +////////////////////////////////////////////////////////////////////// +CString CProtectedWinApp::GetRecentFileName(int index) +{ + if (index>=0 && index < _AFX_MRU_MAX_COUNT) { + return (*m_pRecentFileList)[index]; + } + else { + ASSERT(0); + return CString(); + } +} + +void CProtectedWinApp::RemoveRecentFile(int index) +{ + if (index>=0 && index < _AFX_MRU_MAX_COUNT) { + m_pRecentFileList->Remove(index); + } +} + +PyObject *CProtectedWinApp::MakePyDocTemplateList() +{ + PyObject *retList = PyList_New(0); + if (retList==NULL) + return NULL; + POSITION posTempl = m_pDocManager ? m_pDocManager->GetFirstDocTemplatePosition() : NULL; + while (posTempl) { + CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(posTempl); + ASSERT(pTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate))); + PyObject *newListItem = ui_assoc_object::make(PyCDocTemplate::type, pTemplate)->GetGoodRet(); + if (newListItem==NULL) { + Py_DECREF(retList); + return NULL; + } + PyList_Append(retList, newListItem); + Py_DECREF(newListItem); + } + return retList; +} + +// FindOpenDocument - if the C++ framework has a document with this name open, +// then return a pointer to it, else NULL. +CDocument *CProtectedWinApp::FindOpenDocument(const char *lpszFileName) +{ + POSITION posTempl = m_pDocManager->GetFirstDocTemplatePosition(); + CDocument* pOpenDocument = NULL; + + char szPath[_MAX_PATH]; + if (!GetFullPath(szPath, lpszFileName)) + strcpy(szPath, lpszFileName); + + while (posTempl) { + CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(posTempl); + ASSERT(pTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate))); + // go through all documents + POSITION posDoc = pTemplate->GetFirstDocPosition(); + while (posDoc) { + CDocument* pDoc = pTemplate->GetNextDoc(posDoc); + if (lstrcmpi(pDoc->GetPathName(), szPath) == 0) + return pDoc; + } + } + return NULL; +} + +CProtectedDocManager *CProtectedWinApp::GetDocManager() +{ + CProtectedDocManager *ret = (CProtectedDocManager *)m_pDocManager; + if (!ret->IsKindOf(RUNTIME_CLASS(CDocManager))) + RETURN_ERR("There is not a valid Document Manager"); + return ret; +} + +extern BOOL bDebuggerPumpStopRequested; + +///////////////////////////////////////////////////////////////////// +// +// Application object +// +////////////////////////////////////////////////////////////////////// +PyCWinApp::PyCWinApp() +{ + ASSERT(pExistingAppObject== NULL); +} + +PyCWinApp::~PyCWinApp() +{ + XDODECREF(pExistingAppObject); + pExistingAppObject = NULL; +} + +// @pymethod |PyCWinApp|AddDocTemplate|Adds a template to the application list. +static PyObject * +ui_app_add_doc_template(PyObject *self, PyObject *args) +{ + PyObject *obTemplate; + if (!PyArg_ParseTuple(args,"O:AddDocTemplate", + &obTemplate)) // @pyparm |template||The template to be added. + return NULL; + + if (!ui_base_class::is_uiobject(obTemplate, &PyCDocTemplate::type)) + RETURN_TYPE_ERR("The paramater must be a template object"); + + CDocTemplate *pTempl = PyCDocTemplate::GetTemplate(obTemplate); + if (pTempl==NULL) + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + // walk all templates in the application looking for it. + CDocTemplate* pTemplate; + POSITION pos = pApp->m_pDocManager ? pApp->m_pDocManager->GetFirstDocTemplatePosition() : NULL; + while (pos != NULL) { + pTemplate = pApp->m_pDocManager->GetNextDocTemplate(pos); + if (pTemplate==pTempl) + RETURN_ERR("The template is already in the application list"); + } + GUI_BGN_SAVE; + pApp->AddDocTemplate(pTempl); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWinApp|RemoveDocTemplate|Removes a template to the application list. +static PyObject * +ui_app_remove_doc_template(PyObject *self, PyObject *args) +{ + // @comm Note that MFC does not provide an equivilent function. + PyObject *obTemplate; + if (!PyArg_ParseTuple(args,"O:RemoveDocTemplate", + &obTemplate)) // @pyparm |template||The template to be removed. Must have previously been added by . + return NULL; + + if (!ui_base_class::is_uiobject(obTemplate, &PyCDocTemplate::type)) + RETURN_TYPE_ERR("The paramater must be a template object"); + + CDocTemplate *pTempl = PyCDocTemplate::GetTemplate(obTemplate); + if (pTempl==NULL) + return NULL; + GUI_BGN_SAVE; + BOOL ok = PyCDocTemplate::RemoveDocTemplateFromApp( pTempl ); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("The template is not in the application template list"); + RETURN_NONE; +} + + +static PyObject * +ui_init_mdi_instance(PyObject *self, PyObject *args) +{ + RETURN_NONE; +} + +// @pymethod |PyCWinApp|OpenDocumentFile|Opens a document file by name. +static PyObject * +ui_open_document_file(PyObject *self, PyObject *args) +{ + char *fileName; + if (!PyArg_ParseTuple(args, "s:OpenDocumentFile", + &fileName )) // @pyparm string|fileName||The name of the document to open. + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + + if (((CProtectedWinApp *)pApp)->GetMainFrame()->GetSafeHwnd()==0) + RETURN_ERR("There is no main frame in which to create the document"); + + GUI_BGN_SAVE; + CDocument *pDoc = pApp->OpenDocumentFile(fileName); + GUI_END_SAVE; + if (PyErr_Occurred()) + return NULL; + if (pDoc==NULL) + RETURN_NONE; + return ui_assoc_object::make(PyCDocument::type, pDoc)->GetGoodRet(); +} + +// @pymethod |PyCWinApp|FindOpenDocument|Returns an existing document with the specified file name. +static PyObject * +ui_find_open_document(PyObject *self, PyObject *args) +{ + char *fileName; + // @pyparm string|fileName||The fully qualified filename to search for. + if (!PyArg_ParseTuple(args,"s", &fileName)) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + // Let MFC framework search for a filename for us. + GUI_BGN_SAVE; + CDocument *pDoc=pApp->FindOpenDocument(fileName); + GUI_END_SAVE; + if (pDoc==NULL) + RETURN_NONE; + return ui_assoc_object::make(PyCDocument::type, pDoc)->GetGoodRet(); +} + +// @pymethod |win32ui|GetApp|Retrieves the application object. +PyObject * +ui_get_app(PyObject *self, PyObject *args) +{ + // @comm There will only ever be one application object per application. + CHECK_NO_ARGS2(args,GetApp); + CWinApp *pApp = GetApp(); + if (pApp==NULL) return NULL; + return ui_assoc_object::make(PyCWinApp::type, pApp)->GetGoodRet(); +} + +// @pymethod |PyCWinApp|OnFileNew|Calls the underlying OnFileNew MFC method. +static PyObject * +ui_on_file_new(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,OnFileNew); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + GUI_BGN_SAVE; + pApp->OnFileNew(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCWinApp|OnFileOpen|Calls the underlying OnFileOpen MFC method. +static PyObject * +ui_on_file_open(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,OnFileNew); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + GUI_BGN_SAVE; + pApp->OnFileOpen(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCWinApp|LoadCursor|Loads a cursor. +static PyObject *ui_load_cursor(PyObject *self, PyObject *args) +{ + UINT cid; + char *csid; + HCURSOR hc; + if ( PyArg_ParseTuple(args, "i", + &cid)) // @pyparm int|cursorId||The ID of the cursor to load. + hc = GetApp()->LoadCursor(cid); + else if (PyArg_ParseTuple(args, "s", + &csid)) // @pyparmalt1 string|cursorId||The ID of the cursor to load. + hc = GetApp()->LoadCursor(csid); + else + RETURN_TYPE_ERR("The first param must be an integer or a string"); + if (hc==0) + RETURN_API_ERR("LoadCursor"); + return PyInt_FromLong((long)hc); +} + +// @pymethod int|PyCWinApp|LoadStandardCursor|Loads a standard cursor. +static PyObject *ui_load_standard_cursor(PyObject *self, PyObject *args) +{ + UINT cid; + char *csid; + HCURSOR hc; + if ( PyArg_ParseTuple(args, "i", + &cid)) // @pyparm int|cursorId||The ID of the cursor to load. + hc = GetApp()->LoadStandardCursor(MAKEINTRESOURCE(cid)); + else if (PyArg_ParseTuple(args, "s", // @pyparmalt1 string|cursorId||The ID of the cursor to load. + &csid)) + hc = GetApp()->LoadStandardCursor(csid); + else + RETURN_TYPE_ERR("The first param must be an integer or a string"); + if (hc==0) + RETURN_API_ERR("LoadStandardCursor"); + return PyInt_FromLong((long)hc); +} + +// @pymethod int|PyCWinApp|LoadOEMCursor|Loads an OEM cursor. +static PyObject *ui_load_oem_cursor(PyObject *self, PyObject *args) +{ + UINT cid; + HCURSOR hc; + if ( !PyArg_ParseTuple(args, "i", + &cid)) // @pyparm int|cursorId||The ID of the cursor to load. + return NULL; + hc = GetApp()->LoadOEMCursor(cid); + if (hc==0) + RETURN_API_ERR("LoadOEMCursor"); + return PyInt_FromLong((long)hc); +} + +// @pymethod int|PyCWinApp|LoadIcon|Loads an icon resource. +static PyObject * +ui_load_icon(PyObject *self, PyObject *args) +{ + int idResource; + // @pyparm int|idResource||The ID of the icon to load. + if (!PyArg_ParseTuple(args,"i:LoadIcon", &idResource)) + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + return Py_BuildValue("i", pApp->LoadIcon(idResource)); +} + +// @pymethod int|PyCWinApp|LoadStandardIcon|Loads an icon resource. +static PyObject * +ui_load_standard_icon(PyObject *self, PyObject *args) +{ + char *resName; + // @pyparm string|resourceName||The name of the standard icon to load. + if (!PyArg_ParseTuple(args,"s:LoadStandardIcon", &resName)) + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + return Py_BuildValue("i", pApp->LoadStandardIcon(resName)); +} + + +// @pymethod int|PyCWinApp|Run|Starts the message pump. Advanced users only +static PyObject * +ui_app_run(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "Run"); + GUI_BGN_SAVE; + long rc = AfxGetApp()->CWinApp::Run(); + GUI_END_SAVE; + + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWinApp|IsInproc|Returns a flag to indicate if the created CWinApp was in the DLL, or an external EXE. +static PyObject * +ui_app_isinproc(PyObject *self, PyObject *args) +{ + extern BOOL PyWin_bHaveMFCHost; + CHECK_NO_ARGS2(args, IsInproc); + return PyInt_FromLong(!PyWin_bHaveMFCHost); +} + +// @pymethod [,...]|PyCWinApp|GetDocTemplateList|Returns a list of all document templates. +static PyObject * +ui_app_get_doc_template_list(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, GetDocTemplateList); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + return pApp->MakePyDocTemplateList(); +} + +extern PyObject *ui_init_dlg_instance(PyObject *self, PyObject *args); + +// @object PyCWinApp|An application class. Encapsulates an MFC class +static struct PyMethodDef PyCWinApp_methods[] = { + {"AddDocTemplate", ui_app_add_doc_template, 1 }, // @pymeth AddDocTemplate|Adds a template to the application list. + {"FindOpenDocument", ui_find_open_document, 1}, // @pymeth FindOpenDocument|Returns an existing document with the specified file name. + {"GetDocTemplateList", ui_app_get_doc_template_list, 1}, // @pymeth GetDocTemplateList|Returns a list of all document templates in use. + {"InitMDIInstance", ui_init_mdi_instance, 1}, + {"InitDlgInstance", ui_init_dlg_instance, 1}, // @pymeth InitDlgInstance|Calls critical InitInstance processing for a dialog based application. + {"LoadCursor", ui_load_cursor, 1}, //@pymeth LoadCursor|Loads a cursor. + {"LoadStandardCursor", ui_load_standard_cursor,1}, //@pymeth LoadStandardCursor|Loads a standard cursor. + {"LoadOEMCursor", ui_load_oem_cursor, 1}, //@pymeth LoadOEMCursor|Loads an OEM cursor. + {"LoadIcon", ui_load_icon, 1}, // @pymeth LoadIcon|Loads an icon resource. + {"LoadStandardIcon", ui_load_standard_icon, 1}, // @pymeth LoadStandardIcon|Loads an icon resource. + {"OpenDocumentFile", ui_open_document_file, 1}, // @pymeth OpenDocumentFile|Opens a document file by name. + {"OnFileNew", ui_on_file_new, 1}, // @pymeth OnFileNew|Calls the underlying OnFileNew MFC method. + {"OnFileOpen", ui_on_file_open, 1}, // @pymeth OnFileOpen|Calls the underlying OnFileOpen MFC method. + {"RemoveDocTemplate", ui_app_remove_doc_template, 1}, // @pymeth RemoveDocTemplate|Removes a template to the application list. + {"Run", ui_app_run, 1}, // @pymeth Run|Starts the main application message pump. + {"IsInproc", ui_app_isinproc, 1}, // @pymeth IsInproc|Returns a flag to indicate if the created CWinApp was in the DLL, or an external EXE. + {NULL, NULL} +}; +ui_type_CObject PyCWinApp::type("PyCWinApp", + &PyCWinThread::type, + RUNTIME_CLASS(CWinApp), + sizeof(PyCWinApp), + PyCWinApp_methods, + GET_PY_CTOR(PyCWinApp) ); + +void PyCWinApp::cleanup() +{ + // total hack! + while (pExistingAppObject) + DODECREF(pExistingAppObject); // this may delete it. +} diff --git a/Pythonwin/win32assoc.cpp b/Pythonwin/win32assoc.cpp new file mode 100644 index 0000000000..82d756d200 --- /dev/null +++ b/Pythonwin/win32assoc.cpp @@ -0,0 +1,363 @@ +/* + + win32 assoc object + + Created August 1994, Mark Hammond (MHammond@skippinet.com.au) + +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" + +CAssocManager PyCCmdTarget::handleMgr; + +CAssocManager::CAssocManager() +{ + lastLookup = NULL; + lastObject = NULL; +#ifdef _DEBUG + cacheLookups = cacheHits = 0; +#endif +} +CAssocManager::~CAssocManager() +{ +#ifdef _DEBUG + char buf[256]; + if (cacheLookups) { + // cant use TRACE, as CWinApp may no longer be valid. + wsprintf(buf, "AssocManager cache hit ratio is %d percent\n", cacheHits * 100 / cacheLookups); + OutputDebugString(buf); + } +#endif +} + +// +// CAssocManager::cleanup +// +// This should never detect objects. +void CAssocManager::cleanup(void) +{ + POSITION pos; + ui_assoc_object *ob; + void *assoc; + ASSERT_VALID(&map); + m_critsec.Lock(); + for(pos=map.GetStartPosition();pos;) { + map.GetNextAssoc(pos, (void *&)assoc, (void *&)ob); + ob->cleanup(); + // not sure if I should do this!! + //PyMem_DEL(ob); + } + m_critsec.Unlock(); +} +void CAssocManager::Assoc(void *handle, ui_assoc_object *object, void *oldHandle) +{ + m_critsec.Lock(); + if (oldHandle) { + // if window previously closed, this may fail when the Python object + // destructs - but this is not a problem. + map.RemoveKey(oldHandle); + if (oldHandle==lastLookup) + lastLookup = 0; // set cache invalid. + } + if (handle) + map.SetAt(handle, object); + if (handle==lastLookup) + lastObject = object; + m_critsec.Unlock(); +} + +// +// CAssocManager::GetAssocObject +// +ui_assoc_object *CAssocManager::GetAssocObject(const void * handle) +{ + if (handle==NULL) return NULL; // no possible association for NULL! + ui_assoc_object *ret; + m_critsec.Lock(); +#ifdef _DEBUG + cacheLookups++; +#endif + // implement a basic 1 item cache. + if (lastLookup==handle) { + ret = lastObject; +#ifdef _DEBUG + ++cacheHits; +#endif + } + else { + if (!map.Lookup((void *)handle, (void *&)ret)) + ret = NULL; + lastLookup = handle; + lastObject = ret; + } + m_critsec.Unlock(); + return ret; +} + +/*static*/void *ui_assoc_object::GetGoodCppObject(PyObject *&self, ui_type *ui_type_check) +{ + // first, call is_uiobject, which may modify the "self" pointer. + // this is to support a Python class instance being passed in, + // and auto-convert it to the classes AttachedObject. + if (ui_type_check && !is_uiobject(self, ui_type_check)) { + CString csRet = "object is not a "; + csRet += ui_type_check->tp_name; + TRACE("GetGoodCppObject fails RTTI\n"); + const char *ret = csRet; + RETURN_TYPE_ERR((char *)ret); + } + ui_assoc_object *s = (ui_assoc_object *)self; + if (s->assoc==NULL) + RETURN_ERR("The object has been destroyed."); +#ifdef _DEBUG + // These sorts of errors are C developers problems, and + // should not be possible to be triggered by Python. + // Therefore we restrict the type checking code to debug + if (!s->CheckCppObject(ui_type_check)) + return NULL; +#endif // _DEBUG + return s->assoc; +} + +void *ui_assoc_object::GetGoodCppObject(ui_type *ui_type_check) const +{ + // Get a checked association. + PyObject *temp = (PyObject *)this; + void *ret = GetGoodCppObject(temp, ui_type_check); + ASSERT(this==(ui_assoc_object *)temp); // Called with this->, and this needs to be changed! + return ret; +} + +bool ui_assoc_CObject::CheckCppObject(ui_type *ui_type_check) const +{ + if (!ui_assoc_object::CheckCppObject(ui_type_check)) return false; + CObject *pObj = (CObject *)assoc; + ASSERT_VALID(pObj); // NULL has already been handled before now. + if (ui_type_check==NULL) return true; // Cant check anything! + + ui_type_CObject *pTyp = (ui_type_CObject *)ui_type_check; + if (pTyp->pCObjectClass==NULL) { + // Type must be intermediate - ie, has child classes that + // all objects should be one of. This may indicate we are + // missing a child type (eg, a CommonDialog derived class) + RETURN_ERR("Internal error - attempt to create an object of an abstract class"); + } + if (!pObj->IsKindOf(pTyp->pCObjectClass)) { + TRACE2("ui_assoc_CObject::GetGoodCppObject fails due to RTTI - looking for %s, got %s\n", pTyp->pCObjectClass->m_lpszClassName, pObj->GetRuntimeClass()->m_lpszClassName); + RETURN_ERR("Internal error - C++ RTTI failed"); + } + return true; +} + +// @pymethod |PyAssocObject|AttachObject|Attaches a Python object for lookup of "virtual" functions. +PyObject * +ui_assoc_object::AttachObject(PyObject *self, PyObject *args) +{ + PyObject *ob; + ui_assoc_object *pAssoc = (ui_assoc_object *)self; + if (pAssoc==NULL) return NULL; + if (!PyArg_ParseTuple(args, "O:AttachObject", &ob )) + return NULL; + // Possibility for recursion here if we re-attach the + // same instance to the same win32ui type object. + // decref of the instance may trigger instance delete, + // which may trigger AttachObject(None), which will + // attempt to decref etc. + // So set the instance to NULL _before_ we decref it! + PyObject *old = pAssoc->virtualInst; + pAssoc->virtualInst = NULL; + XDODECREF(old); + if (ob!=Py_None) { + pAssoc->virtualInst = ob; + DOINCREF(ob); + } + RETURN_NONE; +} + +// @object PyAssocObject|An internal class. +static struct PyMethodDef PyAssocObject_methods[] = { + {"AttachObject", ui_assoc_object::AttachObject, 1 }, // @pymeth AttachObject|Attaches a Python object for lookup of "virtual" functions. + {NULL, NULL} +}; + +ui_type ui_assoc_object::type("(abstract) PyAssocObject", + &ui_base_class::type, + sizeof(ui_assoc_object), + PyAssocObject_methods, + NULL); + +ui_assoc_object::ui_assoc_object() +{ + assoc=0; + virtualInst=NULL; +} +ui_assoc_object::~ui_assoc_object() +{ + KillAssoc(); +} + +// handle is invalid - therefore release all refs I am holding for it. +// ASSUMES WE HOLD THE PYTHON LOCK as for all Python object destruction. +void ui_assoc_object::KillAssoc() +{ +#ifdef TRACE_ASSOC + CString rep = repr(); + const char *szRep = rep; + TRACE("Destroying association with %p and %s",this,szRep); +#endif + // note that _any_ of these may cause this to be deleted, as the reference + // count may drop to zero. If any one dies, and later ones will fail. Therefore + // I incref first, and decref at the end. + // Note that this _always_ recurses when this happens as the destructor also + // calls us to cleanup. Forcing an INCREF/DODECREF in that situation causes death + // by recursion, as each dec back to zero causes a delete. + BOOL bDestructing = ob_refcnt==0; + if (!bDestructing) + Py_INCREF(this); + DoKillAssoc(bDestructing); // kill all map entries, etc. + SetAssocInvalid(); // let child do whatever to detect + if (!bDestructing) + DODECREF(this); +} +// the virtual version... +// ASSUMES WE HOLD THE PYTHON LOCK as for all Python object destruction. +void ui_assoc_object::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + Py_XDECREF(virtualInst); + virtualInst = NULL; +// virtuals.DeleteAll(); + handleMgr.Assoc(0,this,assoc); +} + +// return an object, given an association, if we have one. +/* static */ ui_assoc_object *ui_assoc_object::GetPyObject(void *search) +{ + return (ui_assoc_object *)handleMgr.GetAssocObject(search); +} + +PyObject *ui_assoc_object::GetGoodRet() +{ + if (this==NULL) return NULL; + if (virtualInst) { + DODECREF(this); + DOINCREF(virtualInst); + return virtualInst; + } else + return this; +} + +/*static*/ ui_assoc_object *ui_assoc_object::make( ui_type &makeType, void *search ) +{ + ASSERT(search); // really only a C++ problem. + ui_assoc_object* ret = (ui_assoc_object*)handleMgr.GetAssocObject(search); + if (ret) { + if (!ret->is_uiobject(&makeType)) + RETURN_ERR("Internal error - existing object is not of same type as requested new object"); + DOINCREF( ret ); + return ret; + } + ret = (ui_assoc_object*) ui_base_class::make( makeType ); // may fail if unknown class. + if (ret) { + ASSERT(ret->ob_type == &makeType); // Created object must be of the type we expect. + // do NOT keep a reference to the Python object, or it will + // remain forever. The destructor must remove itself from the map. +#ifdef TRACE_ASSOC + TRACE_ASSOC (" Associating 0x%x with 0x%x", search, ret); +#endif + // if I have an existing handle, remove it. + handleMgr.Assoc(search, ret,NULL); + ret->assoc = search; + } + return ret; +} + +CString ui_assoc_object::repr() +{ + CString csRet; + char *buf = csRet.GetBuffer(128); + sprintf(buf, " - assoc is %p, vf=%s", assoc, virtualInst ? "True" : "False"); + csRet.ReleaseBuffer(); + return ui_base_class::repr() + csRet; +} +#ifdef _DEBUG +void ui_assoc_object::Dump( CDumpContext &dc ) const +{ + ui_base_class::Dump(dc); + dc << "assoc=" << assoc; +} +#endif + +/*int ui_assoc_object::setattr(char *name, PyObject *v) +{ + // v may be NULL or None. + return virtuals.AddVirtualHandler(name, v); +}*/ + +// @object PyAssocCObject|An internal class. +static struct PyMethodDef PyAssocCObject_methods[] = { + {NULL, NULL} +}; + +ui_type_CObject ui_assoc_CObject::type("PyAssocCObject", + &ui_assoc_object::type, + RUNTIME_CLASS(CObject), + sizeof(ui_assoc_CObject), + PyAssocCObject_methods, + NULL); + +ui_assoc_CObject::ui_assoc_CObject() +{ + bManualDelete = FALSE; // default not explicit delete on object. +} + +ui_assoc_CObject::~ui_assoc_CObject() +{ + if (bManualDelete) { + bManualDelete = FALSE; + CObject *pO = (CObject *)GetGoodCppObject(&type); // get pointer before killing it. + KillAssoc(); // stop recursion - disassociate now. + delete pO; +// PyErr_Clear(); + } +} +#ifdef _DEBUG + +void ui_assoc_CObject::Dump( CDumpContext &dc ) const +{ + // skip over ui_assoc, as we print the assoc in a much better format! + ui_base_class::Dump(dc); +#if !defined(_MAC) && !defined(_AFX_PORTABLE) + // use SEH (structured exception handling) to catch even GPFs + // that result from partially valid objects. + try +#endif + { + CObject *pOb = (CObject *)GetGoodCppObject(NULL); + dc << ", CObject is "; + if (pOb) { + if (AfxIsValidAddress(pOb, sizeof(CObject))) + pOb->Dump(dc); + else + afxDump << ""; + } + else + dc << ""; + } +#if !defined(_MAC) && !defined(_AFX_PORTABLE) + catch(int code) { + // short form for trashed objects + afxDump << ""; + } + catch(...) { + // short form for trashed objects + afxDump << ""; + } +#endif +} +#endif diff --git a/Pythonwin/win32assoc.h b/Pythonwin/win32assoc.h new file mode 100644 index 0000000000..c1b26735a2 --- /dev/null +++ b/Pythonwin/win32assoc.h @@ -0,0 +1,101 @@ +// +// Association classes. +// +#pragma once +// afxmt.h is often not included by default in stdafx.h +// Try and include it here - it wont hurt if stfafx.h has already done it! +#include // CCriticalSection, etc + +// Handle Manager maps between pointers of some sort, and an associated +// Python objects. + +typedef void *ASSOC_CPP; + +class ui_assoc_object; +//////////////////// + +class CAssocManager +#ifdef _DEBUG + : public CObject // CObject for diag only. +#endif +{ +public: + CAssocManager(); + ~CAssocManager(); + void Assoc(void *assoc, ui_assoc_object *PyObject, void *oldAssoc=NULL); + ui_assoc_object *GetAssocObject(const void * handle); + + void cleanup(void); // only to be called at the _very_ end +private: + CMapPtrToPtr map; + const void *lastLookup; + ui_assoc_object *lastObject; + CCriticalSection m_critsec; +#ifdef _DEBUG + int cacheLookups; + int cacheHits; +#endif +}; + +// +// ui_assoc_object +// +class PYW_EXPORT ui_assoc_object : public ui_base_class{ +public: // some probably shouldnt be, but... + PyObject *GetGoodRet(); + static ui_assoc_object *make( ui_type &makeType, void * search ); + + // Given a C++ object, return a PyObject associated (map lookup) + static ui_assoc_object *GetPyObject(void *search); + + // Return the C++ object associated with this Python object. + // Do as much type checking as possible. + // Static version may have "self" pointer changed if it does + // auto conversion from Instance to Object. + static void *GetGoodCppObject(PyObject *&self, ui_type *ui_type_check); + + // Call this when the C++ object dies, or otherwise becomes invalid. + void KillAssoc(); // maps to a virtual with some protection wrapping. + + // virtuals for Python support + virtual CString repr(); + + // methods + static PyObject *AttachObject(PyObject *self, PyObject *args); + + PyObject *virtualInst; + + static ui_type type; + static CAssocManager handleMgr; +#ifdef _DEBUG + virtual void Dump( CDumpContext &dc ) const; +#endif +protected: + void *GetGoodCppObject(ui_type *ui_type_check=NULL) const; + virtual bool CheckCppObject(ui_type *ui_type_check) const {return true;} + // Does the actual killing. + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); // does the actual work. + // Called during KillAssoc - normally zeroes association. + // Override to keep handle after destruction (eg, the association + // with a dialog is valid after the Window's window has closed). + virtual void SetAssocInvalid() { assoc = 0; } + + ui_assoc_object(); // ctor/dtor + virtual ~ui_assoc_object(); + void *assoc; +}; + +class PYW_EXPORT ui_assoc_CObject : public ui_assoc_object { + // create an object +public: + static ui_type_CObject type; +#ifdef _DEBUG + virtual void Dump( CDumpContext &dc ) const; +#endif + BOOL bManualDelete; // set to TRUE if the C++ object should be deleted when finished. +protected: + ui_assoc_CObject(); + ~ui_assoc_CObject(); + // Perform some basic type checking + virtual bool CheckCppObject(ui_type *ui_type_check) const; +}; diff --git a/Pythonwin/win32bitmap.cpp b/Pythonwin/win32bitmap.cpp new file mode 100644 index 0000000000..ed5e30ffb1 --- /dev/null +++ b/Pythonwin/win32bitmap.cpp @@ -0,0 +1,665 @@ +/* win32bitmap : implementation file + + Created October 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32gdi.h" +#include "win32bitmap.h" + +#include "win32dll.h" +#include "win32dc.h" + +#include "dibapi.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +/* + * Dib Header Marker - used in writing DIBs to files + */ +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') + + +ui_bitmap::ui_bitmap() +{ + pPal = NULL; + sizeBitmap = CSize(0,0); +} +ui_bitmap::~ui_bitmap() +{ + ClearSupportData(); +} +void ui_bitmap::ClearSupportData() +{ + delete pPal; + pPal = NULL; + sizeBitmap = CSize(0,0); +} + +// @pymethod |win32ui|CreateBitmap|Creates a bitmap object. +PyObject *ui_bitmap::create( PyObject *self, PyObject *args ) +{ + return ui_assoc_object::make( ui_bitmap::type, new CBitmap); +} + +// @pymethod |win32ui|CreateBitmapFromHandle|Creates a bitmap object from a HBITMAP. +PyObject *ui_bitmap::create_from_handle( PyObject *self, PyObject *args ) +{ + PyObject *pObj; + int handle; + if (!PyArg_ParseTuple(args, "i", &handle)) + return NULL; + CBitmap *pBitmap = new CBitmap; +// if (!pBitmap->Attach((HGDIOBJ)handle)) { + if (!pBitmap->Attach((HBITMAP)handle)) { + delete pBitmap; + RETURN_ERR("Attach failed!"); + } + pObj = ui_assoc_object::make( ui_bitmap::type, pBitmap); + if (pObj) { + BITMAP bm; + ui_bitmap *pDIB = (ui_bitmap *) pObj; + pBitmap->GetBitmap(&bm); + pDIB->sizeBitmap = CSize (bm.bmWidth, bm.bmHeight); + } + return pObj; +} + +// @pymethod |PyBitmap|LoadBitmap|Loads a bitmap from a DLL object. +static PyObject *ui_bitmap_load_bitmap( PyObject *self, PyObject *args ) +{ + int idRes; + HINSTANCE hModLoad; + PyObject *obDLL = NULL; + ui_bitmap *pUIBitmap = (ui_bitmap *)self; + if (!PyArg_ParseTuple(args,"i|O", + &idRes, // @pyparm int|idRes||The resource ID of the bitmap + &obDLL)) // @pyparm |obDLL|None|The DLL object to load from. + return NULL; + if (obDLL && obDLL!=Py_None) { + // passed a DLL object. + if (!ui_base_class::is_uiobject(obDLL, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL"); + if (!(hModLoad = ((dll_object *)obDLL)->GetDll())) + RETURN_ERR("Can not load from an uninitialised PyDLL object"); + } else + hModLoad = AfxGetInstanceHandle(); + + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); + if (!pBitmap) + return NULL; + + HMODULE save = AfxGetResourceHandle(); + AfxSetResourceHandle (hModLoad); + BOOL bOK = pBitmap->LoadBitmap(idRes); + AfxSetResourceHandle (save); + if (!bOK) + RETURN_ERR("LoadBitmap failed"); + // clear any palette and size info we have. + pUIBitmap->ClearSupportData(); + pUIBitmap->pPal = NULL; + BITMAP bm; + if (pBitmap->GetObject(sizeof(bm), &bm)==0) + RETURN_ERR("GetObject failed on bitmap"); + pUIBitmap->sizeBitmap = CSize( bm.bmWidth, bm.bmHeight ); + + RETURN_NONE; +} + +// @pymethod |PyBitmap|CreateCompatibleBitmap|Creates a bitmap compatible with the specified device context. +static PyObject *ui_bitmap_create_compatible_bitmap( PyObject *self, PyObject *args ) +{ + int width, height; + PyObject *obDC; + + if (!PyArg_ParseTuple(args,"Oii", + &obDC, // @pyparm |dc||Specifies the device context. + &width, // @pyparm int|width||The width (in bits) of the bitmap + &height)) // @pyparm int|height||The height (in bits) of the bitmap. + return NULL; + CDC *pDC = ui_dc_object::GetDC(obDC); + if (pDC==NULL) + return NULL; + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); + if (!pBitmap) + return NULL; + if (!pBitmap->CreateCompatibleBitmap(pDC, width, height)) + RETURN_ERR("CreateCompatibleDC failed"); + RETURN_NONE; +} + +////////////////////////////////////////////////////////////////////// +// +// Load BMP format file +// +// +// @pymethod |PyBitmap|LoadBitmapFile|Loads a bitmap (.BMP) format +// from a file object. +PyObject *ui_bitmap_load_bitmap_file( PyObject *self, PyObject *args ) +{ + ui_bitmap *pDIB = (ui_bitmap *)self; // the python object + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); // the assoc window object. + if (!pBitmap) + return NULL; + PyObject *fileObject; + if (!PyArg_ParseTuple(args,"O", &fileObject)) // @pyparm file[.read]|fileObject||The file object to load the .BMP format file from. + return NULL; + + PyObject *reader = PyObject_GetAttrString(fileObject, "read"); + if (reader == NULL) + return NULL; + PyObject *seeker = PyObject_GetAttrString(fileObject, "seek"); + if (reader == NULL) + return NULL; + + args = Py_BuildValue("(i)", sizeof(BITMAPFILEHEADER)); + if (args == NULL) { + DODECREF(reader); + DODECREF(seeker); + return NULL; + } + + PyObject *result = gui_call_object(reader, args); + DODECREF(args); + if (result==NULL) { + DODECREF(reader); + DODECREF(seeker); + return NULL; + } + if (!PyString_Check(result)) { + DODECREF(result); + DODECREF(seeker); + DODECREF(reader); + PyErr_SetString(PyExc_TypeError, + "object.readline() returned non-string"); + return NULL; + } + int len = PyString_Size(result); + if (len != sizeof(BITMAPFILEHEADER)) { + DODECREF(seeker); + DODECREF(reader); + DODECREF(result); + PyErr_SetString(PyExc_EOFError, + "EOF when reading DIB header"); + return NULL; + } + BITMAPFILEHEADER bmFileHeader; + memcpy( &bmFileHeader, PyString_AsString(result), len); + DODECREF(result); // dont need this anymore + if (bmFileHeader.bfType != DIB_HEADER_MARKER) { + DODECREF(reader); + PyErr_SetString(PyExc_TypeError, + "File is not a DIB format file"); + return NULL; + } + + // read the bits themself. +/* int bitsSize = bmFileHeader.bfSize - sizeof(BITMAPFILEHEADER); + args = Py_BuildValue("(i)", bitsSize); + if (args == NULL) { + DODECREF(reader); + return NULL; + } +*/ +/* Attempt to load wierd bitmap format. +if (bmFileHeader.bfOffBits) { + PyObject *args = Py_BuildValue("(i)", bmFileHeader.bfOffBits); + result = gui_call_object(seeker, args); + DODECREF(args); + if (result==NULL) { + DODECREF(reader); + DODECREF(seeker); + return NULL; + } + DODECREF(result); + } +*/ + DODECREF(seeker); // done with this. + + result = gui_call_object(reader, NULL); + if (result==NULL) { + DODECREF(reader); + return NULL; + } + len = PyString_Size(result); +/* if (len != bitsSize) { + DODECREF(reader); + DODECREF(result); + err_setstr(EOFError, + "EOF when reading DIB bits"); + return NULL; + } +*/ + char *pBits = new char[len]; + // XXX - need memory exception handler. + memcpy( pBits, PyString_AsString(result), len); + DODECREF(result); // dont need this. + DODECREF(reader); // or this. + + // kill old palette + delete pDIB->pPal; + // create the palette. + pDIB->pPal = new CPalette; + if (pDIB->pPal == NULL) + { + // we must be really low on memory + delete pBits; + RETURN_MEM_ERR("Allocating new palette"); + } + if (::CreateDIBPalette(pBits, pDIB->pPal) == NULL) + { + // DIB may not have a palette + delete pDIB->pPal; + pDIB->pPal = NULL; + } + HBITMAP bitmap = DIBToBitmap( pBits, pDIB->pPal ); + if (!bitmap) { + delete pBits; + RETURN_API_ERR("CreateDIBitmap"); + } + pBitmap->Attach(bitmap); + pDIB->sizeBitmap = CSize( ::DIBWidth(pBits), ::DIBHeight(pBits)); + delete pBits; + + RETURN_NONE; +} + +////////////////////////////////////////////////////////////////////// +// +// Load PPM format file +// +// +// @pymethod |PyBitmap|LoadPPMFile|Loads a bitmap in Portable Pix Map (PPM) format +// from a file object. +PyObject *ui_bitmap_load_ppm_file( PyObject *self, PyObject *args ) +{ + ui_bitmap *pDIB = (ui_bitmap *)self; + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); // the assoc window object. + if (!pBitmap) + return NULL; + PyObject *fileObject; + int rows, cols; + const int bitsPerPixel=24; + if (!PyArg_ParseTuple(args,"O(ii)", + &fileObject, // @pyparm file[.read]|fileObject||The file object to load the PPM format file from. + &cols, // @pyparm int|cols||The number of columns in the bitmap. + &rows)) // @pyparm int|rows||The number of rows in the bitmap. + return NULL; + + PyObject *reader = PyObject_GetAttrString(fileObject, "read"); + if (reader == NULL) + return NULL; + + PyObject *result = gui_call_object(reader, NULL); + if (result==NULL) { + DODECREF(reader); + return NULL; + } + int lenRead = PyString_Size(result); + // work out size of bitmap + int headerSize = sizeof(BITMAPINFOHEADER); + // Windows requires bitmap bits aligned to a "long", which is 32 bits! + int imageBytesPerScan = cols * bitsPerPixel/8; + int blocksPerScan = imageBytesPerScan / 4; + if (imageBytesPerScan % 4) // if not on 32 bit boundary, inc size. + ++blocksPerScan; + + int memBytesPerScan = blocksPerScan*4; + + int memSize = rows * memBytesPerScan; + int totalSize = headerSize + memSize; + if (lenRead!=rows*imageBytesPerScan) { + DODECREF(reader); + DODECREF(result); + RETURN_ERR("loading PBM - bytes read from file is not consistant with the bitmap size given"); + } + char *pBits = new char[totalSize]; + // XXX - need mem exception + // copy the data in. Windows wants scan lines bottom up. + // and also wants RGB values reversed. + char *pImg = PyString_AsString(result); + char *pMem = ((char *)pBits)+headerSize+memSize-memBytesPerScan; + BITMAPINFOHEADER *pInfo = (BITMAPINFOHEADER *)pBits; + for (int row=0;rowpPal; + pDIB->pPal = NULL; + + // set up the BITMAPINFOHEADER structure. + pInfo->biSize=sizeof(BITMAPINFOHEADER); + pInfo->biWidth=cols; + pInfo->biHeight=rows; + pInfo->biPlanes=1; + pInfo->biBitCount=bitsPerPixel; + pInfo->biCompression=BI_RGB; + pInfo->biSizeImage=0; // doco says may be zero for BI_RGB. + pInfo->biXPelsPerMeter=0; + pInfo->biYPelsPerMeter=0; + pInfo->biClrUsed=0; //?? + pInfo->biClrImportant=0; + + HBITMAP bitmap = DIBToBitmap( pBits, NULL ); + pBitmap->Attach(bitmap); + pDIB->sizeBitmap = CSize(cols, rows); + delete pBits; + + RETURN_NONE; +} + + +// @pymethod (cx,cy)|PyBitmap|GetSize|Returns the size of the bitmap object. +static PyObject *ui_bitmap_get_size( PyObject *self, PyObject *args ) +{ + ui_bitmap *pDIB = (ui_bitmap *)self; + return Py_BuildValue("(ii)", pDIB->sizeBitmap.cx, pDIB->sizeBitmap.cy); +} + +// @pymethod int|PyBitmap|GetHandle|Returns the HBITMAP for a bitmap object +static PyObject *ui_bitmap_get_handle( PyObject *self, PyObject *args ) +{ +// ui_bitmap *pDIB = (ui_bitmap *)self; +// return Py_BuildValue("i", (HBITMAP)pDIB); + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); + return Py_BuildValue ("i", (HBITMAP) *pBitmap); +} + +// @pymethod |PyBitmap|Paint|Paint a bitmap. +static PyObject *ui_bitmap_paint( PyObject *self, PyObject *args ) +{ + ui_bitmap *pDIB = (ui_bitmap *)self; + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); // the assoc window object. + if (!pBitmap) + return NULL; + + CRect rDest = CFrameWnd::rectDefault; + CRect rSrc = CFrameWnd::rectDefault; + DWORD dwROP = SRCCOPY; + PyObject *dcobject; + if (!PyArg_ParseTuple(args,"O|(iiii)(iiii)i", + // @pyparm |dcObject||The DC object to paint the bitmap to. + &dcobject, + // @pyparm (left,top,right,bottom)|rectDest|(0,0,0,0)|The destination rectangle to paint to. + &rDest.left, &rDest.top, &rDest.right, &rDest.bottom, + // @pyparm (left,top,right,bottom)|rectSrc|(0,0,0,0)|The source rectangle to paint from. + &rSrc.left, &rSrc.top, &rSrc.right, &rSrc.bottom, + &dwROP)) + return NULL; + if (rDest==CFrameWnd::rectDefault) { + rDest.left=rDest.top = 0; + rDest.right = pDIB->sizeBitmap.cx; + rDest.bottom = pDIB->sizeBitmap.cy; + } + + if (rSrc==CFrameWnd::rectDefault) + rSrc = rDest; + if (!ui_base_class::is_uiobject(dcobject, &ui_dc_object::type)) + RETURN_TYPE_ERR("O param must be a PyCDC object"); + CDC *pDC = ui_dc_object::GetDC(dcobject); + if (pDC==NULL) + return NULL; +// #define PAINT_DIB +#ifdef PAINT_DIB + if (!::PaintDIB(pDC->m_hDC, &rDest, pDIB->pBits, + &rSrc, pDIB->pPal, dwROP)) + RETURN_ERR("Painting of DIB failed"); +#else + HBITMAP bitmap = (HBITMAP)pBitmap->GetSafeHandle(); + if (bitmap==NULL) + RETURN_ERR("There is no windows bitmap associated with the object"); + + BOOL bRes = ::PaintDDB(pDC->m_hDC, &rDest, bitmap, &rSrc, pDIB->pPal, dwROP); + if (!bRes) + RETURN_ERR("Painting of DDB failed"); +#endif + RETURN_NONE; +} + +#define DICTADD(D,ST,M,TYPE) PyDict_SetItemString (D, #M, Py_BuildValue (TYPE, ST.M)) + +// @pymethod dict|PyBitmap|GetInfo|Returns the BITMAP structure info +static PyObject *ui_bitmap_info( PyObject *self, PyObject *args ) +{ + if (!PyArg_ParseTuple(args,"")) + return NULL; + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); + BITMAP bm; + if (pBitmap->GetObject(sizeof(bm), &bm)==0) + RETURN_ERR("GetObject failed on bitmap"); + + PyObject *d = PyDict_New(); + + // @rdesc A dictionary of integers, keyed by the following strings: + DICTADD (d, bm, bmType, "i"); // bmType + DICTADD (d, bm, bmWidth, "i"); // bmWidth + DICTADD (d, bm, bmHeight, "i"); // bmHeight + DICTADD (d, bm, bmWidthBytes, "i"); // bmWidthBytes + DICTADD (d, bm, bmPlanes, "i"); // bmPlanes + DICTADD (d, bm, bmBitsPixel, "i"); // bmBitsPixel + return d; +} + +// @pymethod tuple|PyBitmap|GetBitmapBits|Returns the bitmap bits. +static PyObject *ui_get_bitmap_bits( PyObject *self, PyObject *args ) +{ + if (!PyArg_ParseTuple(args,"")) + return NULL; + CBitmap *pBitmap = ui_bitmap::GetBitmap( self ); + BITMAP bm; + if (pBitmap->GetObject(sizeof(bm), &bm)==0) + RETURN_ERR("GetObject failed on bitmap"); + UINT cnt = bm.bmHeight*bm.bmWidthBytes*bm.bmPlanes; + HGLOBAL hMem = GlobalAlloc(GHND, cnt); + if (!hMem) { + RETURN_ERR("GlobalAlloc failed on bitmap"); + } + LPBYTE lpbDst=(LPBYTE)GlobalLock(hMem); + HBITMAP handle = (HBITMAP)pBitmap->GetSafeHandle(); + DWORD bytes = GetBitmapBits(handle, cnt, (LPVOID)lpbDst); + if (bytes != (DWORD)cnt) { + GlobalUnlock(hMem); + RETURN_ERR("GetBitmapBits failed on bitmap"); + } + PyObject* rc = PyTuple_New(cnt); + for (UINT i = 0; i < cnt; i++) { + PyTuple_SetItem(rc, i, Py_BuildValue("i", (int)lpbDst[i])); + } + GlobalUnlock(hMem); + return rc; +} + +// @pymethod None|PyBitmap|SaveBitmapFile|Saves a bitmap to a file. +static PyObject *ui_bitmap_save_bitmap_file( PyObject *self, PyObject *args ) +{ + PyObject *dcobject; + const char *pszFile; + if (!PyArg_ParseTuple(args,"Os", + &dcobject, // @pyparm |dcObject||The DC object that has rendered the bitmap. + &pszFile)) // @pyparm string|Filename||The file to save the bitmap to + return NULL; + CDC *pDC = ui_dc_object::GetDC(dcobject); + if (pDC==NULL) + return NULL; + HDC hDC = pDC->m_hDC; + + CBitmap *pBitmap = ui_bitmap::GetBitmap(self); + HBITMAP hBmp = (HBITMAP)pBitmap->GetSafeHandle(); + + BITMAP bmp; + PBITMAPINFO pbmi; + WORD cClrBits; + + // Retrieve the bitmap's color format, width, and height. + if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) + RETURN_ERR("GetObject failed"); + + // Convert the color format to a count of bits. + cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); + if (cClrBits == 1) + cClrBits = 1; + else if (cClrBits <= 4) + cClrBits = 4; + else if (cClrBits <= 8) + cClrBits = 8; + else if (cClrBits <= 16) + cClrBits = 16; + else if (cClrBits <= 24) + cClrBits = 24; + else cClrBits = 32; + + // Allocate memory for the BITMAPINFO structure. (This structure + // contains a BITMAPINFOHEADER structure and an array of RGBQUAD + // data structures.) + + if (cClrBits != 24) + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD) * (1<< cClrBits)); + + // There is no RGBQUAD array for the 24-bit-per-pixel format. + + else + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER)); + + // Initialize the fields in the BITMAPINFO structure. + + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = bmp.bmWidth; + pbmi->bmiHeader.biHeight = bmp.bmHeight; + pbmi->bmiHeader.biPlanes = bmp.bmPlanes; + pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; + if (cClrBits < 24) + pbmi->bmiHeader.biClrUsed = (1<bmiHeader.biCompression = BI_RGB; + + // Compute the number of bytes in the array of color + // indices and store the result in biSizeImage. + pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 + * pbmi->bmiHeader.biHeight * cClrBits; + + // Set biClrImportant to 0, indicating that all of the + // device colors are important. + pbmi->bmiHeader.biClrImportant = 0; + + HANDLE hf; // file handle + BITMAPFILEHEADER hdr; // bitmap file-header + PBITMAPINFOHEADER pbih; // bitmap info-header + LPBYTE lpBits; // memory pointer + DWORD dwTotal; // total count of bytes + DWORD cb; // incremental count of bytes + BYTE *hp; // byte pointer + DWORD dwTmp; + + pbih = (PBITMAPINFOHEADER) pbmi; + lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); + + if (!lpBits) + RETURN_ERR("GlobalAlloc failed"); + + // Retrieve the color table (RGBQUAD array) and the bits + // (array of palette indices) from the DIB. + if (!GetDIBits(hDC, hBmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, + DIB_RGB_COLORS)) + { + RETURN_ERR("GetDIBits failed"); + } + + // Create the .BMP file. + hf = CreateFile(pszFile, + GENERIC_READ | GENERIC_WRITE, + (DWORD) 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + (HANDLE) NULL); + if (hf == INVALID_HANDLE_VALUE) + RETURN_ERR("CreateFile"); + hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" + // Compute the size of the entire file. + hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof(RGBQUAD) + pbih->biSizeImage); + hdr.bfReserved1 = 0; + hdr.bfReserved2 = 0; + + // Compute the offset to the array of color indices. + hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof (RGBQUAD); + + // Copy the BITMAPFILEHEADER into the .BMP file. + if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), + (LPDWORD) &dwTmp, NULL)) + { + RETURN_ERR("WriteFile failed"); + } + + // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. + if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + + pbih->biClrUsed * sizeof (RGBQUAD), + (LPDWORD) &dwTmp, ( NULL))) + RETURN_ERR("WriteFile failed"); + + // Copy the array of color indices into the .BMP file. + dwTotal = cb = pbih->biSizeImage; + hp = lpBits; + if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) + RETURN_ERR("WriteFile failed"); + + // Close the .BMP file. + if (!CloseHandle(hf)) + RETURN_ERR("CloseHandle failed"); + + // Free memory. + GlobalFree((HGLOBAL)lpBits); + + return Py_None; +} + +///////////////////////////////////////////////////////////////////// +// +// ui_bitmap +// +// @object PyBitmap|A bitmap class, derived from a . +static struct PyMethodDef ui_bitmap_methods[] = { + {"CreateCompatibleBitmap", ui_bitmap_create_compatible_bitmap, 1}, // @pymeth CreateCompatibleBitmap|Creates a bitmap compatible with the specified device context. + {"GetSize", ui_bitmap_get_size, 1}, // @pymeth GetSize|Gets the size of the bitmap object, in pixels. + {"GetHandle", ui_bitmap_get_handle, 1}, // @pymeth GetHandle|Returns the HBITMAP for a bitmap. + {"LoadBitmap", ui_bitmap_load_bitmap, 1}, // @pymeth LoadBitmap|Loads a bitmap from a DLL object. + {"LoadBitmapFile", ui_bitmap_load_bitmap_file,1}, // @pymeth LoadBitmapFile|Loads a bitmap from a file object. + {"LoadPPMFile", ui_bitmap_load_ppm_file, 1}, // @pymeth LoadPPMFile|Loads a bitmap from a file object containing a PPM format bitmap. + {"Paint", ui_bitmap_paint, 1}, // @pymeth Paint|Paints a bitmap to a windows DC. + {"GetInfo", ui_bitmap_info, 1}, // @pymeth GetInfo|Returns the BITMAP structure info. + {"GetBitmapBits", ui_get_bitmap_bits, 1}, // @pymeth GetBitmapBits|Returns the bitmap bits. + {"SaveBitmapFile", ui_bitmap_save_bitmap_file, 1}, // @pymeth SaveBitmapFile|Saves a bitmap to a file. + {NULL, NULL} /* sentinel */ +}; + +ui_type_CObject ui_bitmap::type("PyBitmap", + &PyCGdiObject::type, + RUNTIME_CLASS(CBitmap), + sizeof(ui_bitmap), + ui_bitmap_methods, + GET_PY_CTOR(ui_bitmap)); diff --git a/Pythonwin/win32bitmap.h b/Pythonwin/win32bitmap.h new file mode 100644 index 0000000000..56edfe638f --- /dev/null +++ b/Pythonwin/win32bitmap.h @@ -0,0 +1,23 @@ +// win32bitmap.h : header file +// +// +///////////////////////////////////////////////////////// +// +// ui_bitmap +class ui_bitmap : public PyCGdiObject { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(ui_bitmap) + static PyObject *create (PyObject *self, PyObject *args); + static PyObject *create_from_handle (PyObject *self, PyObject *args); + + void ClearSupportData(); + + CPalette *pPal; + CSize sizeBitmap; +protected: + ui_bitmap(); + ~ui_bitmap(); +private: +}; + diff --git a/Pythonwin/win32brush.cpp b/Pythonwin/win32brush.cpp new file mode 100644 index 0000000000..80cdf81b2b --- /dev/null +++ b/Pythonwin/win32brush.cpp @@ -0,0 +1,98 @@ +// -*- Mode: C++; tab-width: 4 -*- +// +// Python brush wrapper. +// +// Created Dec 1995, by Sam Rushing (rushing@nightmare.com) +// +// 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 "win32gdi.h" +#include "win32brush.h" + +// this returns a pointer that should not be stored. +CBrush *PyCBrush::GetBrush(PyObject *self) +{ + return (CBrush *)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|GetHalftoneBrush|Creates a new halftone brush object. +PyObject *ui_get_halftone_brush(PyObject *self, PyObject *args) +{ + PyCBrush *pb = (PyCBrush *)ui_assoc_object::make (PyCBrush::type, CDC::GetHalftoneBrush()); + pb->m_deleteObject = FALSE; // this is a temp object + return pb; +} + +// @pymethod |win32ui|CreateBrush|Creates a new brush object. +PyObject * +PyCBrush::create (PyObject *self, PyObject *args) +{ + int n_brush_style; + int n_hatch; + long cr_color; + LOGBRUSH lp; + // Quick exist to make a empty brush + if (PyArg_ParseTuple(args, "")) { + // @comm If called with no arguments, an uninitialized brush is created. + return ui_assoc_object::make (PyCBrush::type, new CBrush); + } + PyErr_Clear(); + if (!PyArg_ParseTuple (args, "iil", + &n_brush_style, // @pyparmalt1 int|style||The brush style. + &cr_color, // @pyparmalt1 int|color||The brush color. + &n_hatch)) {// @pyparmalt1 long|hatch||The brush hatching. + return NULL; + } + lp.lbStyle = n_brush_style; + lp.lbColor = cr_color; + lp.lbHatch = n_hatch; + + CBrush *pBrush = new CBrush; + if (!pBrush->CreateBrushIndirect (&lp)) { + RETURN_ERR ("CreateBrushIndirect call failed"); + } + return ui_assoc_object::make (PyCBrush::type, pBrush); +} + +// @pymethod |PyCBrush|CreateSolidBrush|Initializes a brush with a specified solid color. +static PyObject *PyCBrush_CreateSolidBrush(PyObject *self, PyObject *args) +{ + int color; + if (!PyArg_ParseTuple(args, "i:CreateSolidBrush", &color)) + return NULL; + CBrush *pBrush = PyCBrush::GetBrush(self); + if (pBrush==NULL) return NULL; + if (!pBrush->CreateSolidBrush(color)) + RETURN_ERR("CreateSolidBrush failed"); + RETURN_NONE; +} + +// @pymethod int|PyCBrush|GetSafeHandle|Retrieves the HBRUSH for the brush as an integer +static PyObject *PyCBrush_GetSafeHandle(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "GetSafeHandle"); + CBrush *pBrush = PyCBrush::GetBrush(self); + if (pBrush==NULL) return NULL; + return PyInt_FromLong((long)pBrush->GetSafeHandle()); +} + +// @object PyCBrush|An object encapsulating an MFC PyCBrush class. +static struct PyMethodDef PyCBrush_methods[] = { + {"CreateSolidBrush", PyCBrush_CreateSolidBrush, 1}, // @pymeth CreateSolidBrush|Initializes a brush with a specified solid color. + {"GetSafeHandle", PyCBrush_GetSafeHandle, 1}, // @pymeth GetSafeHandle|Retrieves the HBRUSH for the brush as an integer + {NULL, NULL} +}; + +ui_type_CObject PyCBrush::type ("PyCBrush", + &PyCGdiObject::type, + RUNTIME_CLASS(CBrush), + sizeof(PyCBrush), + PyCBrush_methods, + GET_PY_CTOR(PyCBrush)); diff --git a/Pythonwin/win32brush.h b/Pythonwin/win32brush.h new file mode 100644 index 0000000000..9a6ef5f610 --- /dev/null +++ b/Pythonwin/win32brush.h @@ -0,0 +1,14 @@ +// -*- Mode: C++; tab-width: 4 -*- +// font class + +class PyCBrush : public PyCGdiObject { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCBrush) + CBrush *GetBrush() {return GetBrush(this);} + static CBrush *GetBrush(PyObject *self); + + static PyObject *create (PyObject *self, PyObject *args); +protected: +private: +}; diff --git a/Pythonwin/win32cmd.cpp b/Pythonwin/win32cmd.cpp new file mode 100644 index 0000000000..78767dbf22 --- /dev/null +++ b/Pythonwin/win32cmd.cpp @@ -0,0 +1,263 @@ +/* + + win32 command target + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + + @doc +*/ +#include "stdafx.h" + +CCmdTarget *GetCCmdTargetPtr(PyObject *self) +{ + return (CCmdTarget *)ui_assoc_object::GetGoodCppObject( self, &PyCCmdTarget::type); +} + +///////////////////////////////////////////////////////////////////// +// +// Command target object +// +////////////////////////////////////////////////////////////////////// +// @pymethod |PyCCmdTarget|BeginWaitCursor| +// Displays the cursor as an hourglass. This can be used when you expect a +// command to take a noticeable time to execute (eg, when a document +// loads or saves itself to a file.). +// The actions of BeginWaitCursor are not always effective outside of a single +// message handler as other actions, such as OnSetCursor handling, could change +// the cursor. +// Call EndWaitCursor to restore the previous cursor. +static PyObject * +PyCCmdTarget_begin_wait_cursor(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CCmdTarget *pCC = GetCCmdTargetPtr(self); + if (!pCC) + return NULL; + pCC->BeginWaitCursor(); // @pyseemfc CWnd|BeginWaitCursor + RETURN_NONE; +} + +// @pymethod |PyCCmdTarget|EndWaitCursor|Ends a wait cursor. Should only be called after . +static PyObject * +PyCCmdTarget_end_wait_cursor(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CCmdTarget *pCC = GetCCmdTargetPtr(self); + if (!pCC) + return NULL; + pCC->EndWaitCursor(); + RETURN_NONE; +} + +// @pymethod |PyCCmdTarget|RestoreWaitCursor|Restores the appropriate hourglass cursor after the system cursor has changed. +static PyObject * +PyCCmdTarget_restore_wait_cursor(PyObject *self, PyObject *args) +{ + // @comm Call this function to restore the appropriate hourglass cursor after + // the system cursor has changed (for example, after a message box has opened + // and then closed while in the middle of a lengthy operation). + CHECK_NO_ARGS(args); + CCmdTarget *pCC = GetCCmdTargetPtr(self); + if (!pCC) + return NULL; + pCC->RestoreWaitCursor(); + RETURN_NONE; +} + +// @pymethod object|PyCCmdTarget|HookOleEvent|Hook an OLE Event. +static PyObject * +PyCCmdTarget_hook_ole_event(PyObject *self, PyObject *args) +{ + PyCCmdTarget *s = (PyCCmdTarget *)self; + // @rdesc The return value is the previous handler, or None. + return add_hook_list(s, args,&s->pOleEventHookList); +} + +// @pymethod object|PyCCmdTarget|HookCommand|Hook a windows command handler. +static PyObject * +PyCCmdTarget_hook_command(PyObject *self, PyObject *args) +{ + // @comm obHandler will be called as the application receives command notification messages with the specified ID. + // Command notification messages are usually sent in response to menu or toolbar commands. + // When updating a user interface element, Pythonwin will first check if a + // handler has been installed via . If so, this alone + // determines the state of the interface object. If no Update handler exists, + // PythonWin will automatically enable a menu/toolbar item if a command handler exists + // The handler will be called with 2 arguments + // * The command id being handled. + // * The command notification code. + // If the handler returns TRUE, then the command will be passed on to the + // default handler, otherwise the message will be consumed. + // This method is best suited to handling messages from user interface + // elements, such as menus, toolbars, etc. To handle notification messages from a control, + // you should use + + // @pyparm object|obHandler||The handler for the command message. This must be a callable object. + // @pyparm int|id||The ID of the command to be handled, or zero to handle all command messages. + PyCCmdTarget *s = (PyCCmdTarget *)self; + // @rdesc The return value is the previous handler, or None. + return add_hook_list(s, args,&s->pCommandHookList); +} +// @pymethod object|PyCCmdTarget|HookCommandUpdate|Hook a windows command update handler. +static PyObject * +PyCCmdTarget_hook_command_update(PyObject *self, PyObject *args) +{ + // @comm The handler object passed will be called as + // the application updates user interface elements + // with the specified ID. + // See for a description + // of the rules used to determine command routing and updating. + + // @pyparm object|obHandler||The handler for the command message. This must be a callable object. + // @pyparm int|id||The ID of the command to be handled. + PyCCmdTarget *s = (PyCCmdTarget *)self; + // @rdesc The return value is the previous handler, or None. + return add_hook_list(s, args,&s->pCommandUpdateHookList); +} + + +// @pymethod object|PyCCmdTarget|HookNotify|Hook a windows command handler. +static PyObject * +PyCCmdTarget_hook_notify(PyObject *self, PyObject *args) +{ + // @comm obHandler will be called as the application receives control notification messages. + // These may also be handled via , but this method is specific + // to control notifications, and therefore provides more information. + // + // The handler will be called with 2 arguments + // * A tuple describing standard notification information. + // * A tuple describing extra notification params, or an integer containing the address of the first byte of the extended information. + // If the handler returns TRUE, then the command will be passed on to the + // default handler, otherwise the message will be consumed. + // + // Certain notification codes are recognised internally, and these are converted to a Python tuple. + // If the extra information is not recognised, the address is passed. These addresses could be + // extracted using and the struct module, or using + // Sam Rushing's calldll/dynwin module. (It would be possible to extend Pythonwin so a program + // can install certain knowledge about handlers, but this has not been implemented.) + // @pyparm object|obHandler||The handler for the command message. This must be a callable object. + // @pyparm int|id||The ID of the command to be handled, or zero to handle all command messages. + PyCCmdTarget *s = (PyCCmdTarget *)self; + // @rdesc The return value is the previous handler, or None. + return add_hook_list(s, args,&s->pNotifyHookList); +} + + + +// @object PyCCmdTarget|An abstract command target class. Encapsulates an MFC class +static struct PyMethodDef PyCCmdTarget_methods[] = { + {"BeginWaitCursor", PyCCmdTarget_begin_wait_cursor, 1}, // @pymeth BeginWaitCursor|Displays the cursor as an hourglass. + {"EndWaitCursor", PyCCmdTarget_end_wait_cursor, 1}, // @pymeth EndWaitCursor|End a wait cursor. + {"HookCommand", PyCCmdTarget_hook_command, 1}, // @pymeth HookCommand|Hook a command handler. + {"HookCommandUpdate", PyCCmdTarget_hook_command_update, 1}, // @pymeth HookCommandUpdate|Hook a windows command update handler. + {"HookOleEvent", PyCCmdTarget_hook_ole_event, 1}, // @pymeth HookOleEvent|Hooks an OLE event. + {"HookNotify", PyCCmdTarget_hook_notify, 1}, // @pymeth HookNotify|Hook a control notification handler. + {"RestoreWaitCursor", PyCCmdTarget_restore_wait_cursor, 1}, // @pymeth RestoreWaitCursor|Restores the appropriate hourglass cursor after the system cursor has changed. + {NULL, NULL } +}; + +ui_type_CObject PyCCmdTarget::type("PyCCmdTarget", + &ui_assoc_CObject::type, + RUNTIME_CLASS(CCmdTarget), + sizeof(PyCCmdTarget), + PyCCmdTarget_methods, + NULL); +PyCCmdTarget::PyCCmdTarget() +{ + pOleEventHookList=NULL; + pCommandHookList=NULL; + pNotifyHookList=NULL; + pCommandUpdateHookList=NULL; +// virtuals.SetOwner(this); +} +PyCCmdTarget::~PyCCmdTarget() +{ +} + +// handle is invalid - therefore release all refs I am holding for it. +void PyCCmdTarget::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + free_hook_list(this,&pNotifyHookList); + free_hook_list(this,&pOleEventHookList); + free_hook_list(this,&pCommandHookList); + free_hook_list(this,&pCommandUpdateHookList); + ui_assoc_object::DoKillAssoc(bDestructing); +} + +CString PyCCmdTarget::repr() +{ + CString csRet; + char *buf = csRet.GetBuffer(64); + int numCmd = pCommandHookList ? pCommandHookList->GetCount() : 0; + int numNotify = pNotifyHookList ? pNotifyHookList->GetCount() : 0; + int numCmdUpdate = pCommandUpdateHookList ? pCommandUpdateHookList->GetCount() : 0; + int numOle = pOleEventHookList ? pOleEventHookList->GetCount() : 0; + sprintf(buf, ", notify=%d,ch/u=%d/%d", numNotify, numCmd, numCmdUpdate); + csRet.ReleaseBuffer(); + return ui_assoc_object::repr() + csRet; +} +///////////////////////////////////////////////////////////////////// +// +// add_hook_list +// +// keep a reference to the hooked object. +// Return old handler, or None +PyObject *add_hook_list(PyObject *hookedObject, PyObject *args, CMapWordToPtr **ppList) +{ + CMapWordToPtr *&pList = *ppList; + if (pList==NULL) + pList = new CMapWordToPtr(); + PyObject *method; + int message; + if (!PyArg_ParseTuple(args,"Oi",&method, &message)) + return NULL; + if (method!=Py_None && !PyCallable_Check(method)) + RETURN_ERR("The parameter must be a callable object or None"); + + void *oldMethod = NULL; + // note I maybe decref, then maybe incref. I assume object wont be destroyed + // (ie, ref go to zero) between the 2 calls!) + if (pList->Lookup(message, oldMethod)) { + pList->RemoveKey(message); +// DODECREF((PyObject *)oldMethod); + DODECREF(hookedObject); + } + if (method!=Py_None) { + Py_INCREF(method); + pList->SetAt(message,method); + Py_INCREF(hookedObject); + } + if (oldMethod) + return (PyObject *)oldMethod; + else + RETURN_NONE; +// RETURN_NONE; +} +// +// free_hook_list +// +// this is a bit nasty! This function is called when the window itself +// is closed. As all the hooks into the window are decref'd, it is possible +// (actually, likely!) that one of the member DODECREFS will also cause the +// window object itself to destruct (as the member function in my list was the +// last remaining (indirect) reference to the window) which also calls this. +// Therefore I set the list value to NULL before freeing the members, so +// the recursive call is not harmful. +void free_hook_list(PyObject *hookedObject, CMapWordToPtr **ppList) +{ + CMapWordToPtr *pList = *ppList; + if (pList==NULL) + return; // nothing to do. + *ppList = NULL; + POSITION pos; + void *method; + WORD message; + // Iterate through the entire map + for( pos = pList->GetStartPosition(); pos != NULL; ) { + pList->GetNextAssoc( pos, message, method ); + Py_XDECREF((PyObject *)method); + Py_XDECREF(hookedObject); + } + delete pList; +} + diff --git a/Pythonwin/win32cmd.h b/Pythonwin/win32cmd.h new file mode 100644 index 0000000000..91bf638f88 --- /dev/null +++ b/Pythonwin/win32cmd.h @@ -0,0 +1,28 @@ +// +// Command target header file +// + + +// +// Command target +// +class PYW_EXPORT PyCCmdTarget : public ui_assoc_CObject{ +friend CVirtualHelper::CVirtualHelper(const char *iname, const void *iassoc, EnumVirtualErrorHandling veh); +public: // some probably shouldnt be, but... + CMapWordToPtr *pNotifyHookList; + CMapWordToPtr *pCommandHookList; + CMapWordToPtr *pOleEventHookList; + CMapWordToPtr *pCommandUpdateHookList; + + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); + + // virtuals for Python support + virtual CString repr(); + + static ui_type_CObject type; +protected: + PyCCmdTarget(); + virtual ~PyCCmdTarget(); +}; +extern void free_hook_list(PyObject *objectHooked, CMapWordToPtr **ppList); +extern PyObject *add_hook_list(PyObject *objectHooked, PyObject *args, CMapWordToPtr **ppList); diff --git a/Pythonwin/win32cmdui.cpp b/Pythonwin/win32cmdui.cpp new file mode 100644 index 0000000000..1b6b7ee16c --- /dev/null +++ b/Pythonwin/win32cmdui.cpp @@ -0,0 +1,276 @@ +/* + + win32 CmdUI implementation. + + Created March 1995, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32cmdui.h" + +inline void*GetPythonOleProcAddress(const char *procName) +{ +#ifdef _DEBUG + HMODULE hMod = GetModuleHandle("PythonCOM15_d.dll"); +#else + HMODULE hMod = GetModuleHandle("PythonCOM15.dll"); +#endif + if (hMod) { + void *rc = GetProcAddress(hMod, procName); + if (rc==NULL) + RETURN_ERR("The Python COM extensions do not export the required functions"); + return rc; + } else { + RETURN_ERR("The PythonCOM module must be imported before OLE functions can be used"); + } +} + +static BOOL (*pfnMakeOlePythonCall)(PyObject *handler, DISPPARAMS FAR* params, VARIANT FAR* pVarResult, + EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr, PyObject *addnlArgs) = NULL; + +// General command handler for Python. +BOOL +Python_OnCmdMsg (CCmdTarget *obj, UINT nID, int nCode, + void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo) +{ + // Let MFC deal with the file menu. + if (nCode==CN_UPDATE_COMMAND_UI && nID==ID_FILE_MRU_FILE1) + return FALSE; + +#ifndef _AFX_NO_OCC_SUPPORT + // OLE control events are a special case + if (nCode == CN_EVENT) + { + AFX_EVENT *pEvent = (AFX_EVENT*)pExtra; + PyObject *method; + + // check if obj is really a CWnd (a CDocument could end up here) + if ( ! ( obj->IsKindOf( RUNTIME_CLASS( CWnd ) ) ) ) { + // better quit, otherwise we're in trouble + return FALSE; + } + else { + // everything's fine + + CWnd *control = ((CWnd *)obj)->GetDlgItem(nID); + PyCCmdTarget *pObj = (PyCCmdTarget *) ui_assoc_CObject::GetPyObject(control); + if (pObj && pObj->pOleEventHookList && + pObj->pOleEventHookList->Lookup ((unsigned short)pEvent->m_dispid, (void *&)method)) { + if (pfnMakeOlePythonCall==NULL) { + pfnMakeOlePythonCall = (BOOL (*)(PyObject *, DISPPARAMS FAR* , VARIANT FAR* ,EXCEPINFO FAR* , UINT FAR*, PyObject * )) + GetPythonOleProcAddress("PyCom_MakeOlePythonCall"); + + ASSERT(pfnMakeOlePythonCall); + } + if (pfnMakeOlePythonCall==NULL) return FALSE; + VARIANT result; + VariantInit(&result); + CEnterLeavePython _celp; + (*pfnMakeOlePythonCall)(method, pEvent->m_pDispParams, &result, pEvent->m_pExcepInfo, pEvent->m_puArgError, NULL); + VariantClear(&result); + if (PyErr_Occurred()) // if any Python exception, pretend it was OK + gui_print_error(); + return TRUE; + } + } + } +#endif // !_AFX_NO_OCC_SUPPORT + + PyCCmdTarget *pObj = (PyCCmdTarget *) ui_assoc_CObject::GetPyObject(obj); + BOOL rc = FALSE; // default not handled. + // Give Python code the chance to handle other stuff. + if (pObj != NULL && + pObj->is_uiobject (&PyCCmdTarget::type)) { + + if (nCode == CN_UPDATE_COMMAND_UI) { + CCmdUI *pUI = (CCmdUI *)pExtra; + PyObject *method; + if (pObj->pCommandUpdateHookList && + pObj->pCommandUpdateHookList->Lookup (nID, (void *&)method)) { + // I have a specific user interface element. + // create a PyCCmdUI object. + PyObject *ob = ui_assoc_object::make( PyCCmdUI::type, pUI ); + if (ob==NULL) { + OutputDebugString("Could not make object for CCmdUI handler"); + return FALSE; + } + { + CEnterLeavePython _celp; + Python_callback (method, ob); + if (PyErr_Occurred()) // if any Python exception, pretend it was OK + gui_print_error(); + // object is no longer valid. + GUI_BGN_SAVE; + Python_delete_assoc(ob); + GUI_END_SAVE; + DODECREF(ob); + } + rc = TRUE; + } else if (pObj->pCommandHookList && + pObj->pCommandHookList->Lookup (nID, (void *&)method)) { + // we have a handler for the command itself, but not the + // user interface element. Enable the element. + pUI->Enable(); + rc = TRUE; // did handle it. + } // else RC remains FALSE. + } else { // is the command itself. + // allow either a general or specific handler to be called + PyObject *method = NULL; + if (pObj->pCommandHookList) { + pObj->pCommandHookList->Lookup (nID, (void *&)method); + if (method==NULL) pObj->pCommandHookList->Lookup (0, (void *&)method); + } + if (method) { + // perform the callback. + CEnterLeavePython _celp; + rc = Python_callback (method, nID, nCode); + if (rc==-1) { // if any Python exception, pretend it was OK + char buf[128]; + wsprintf(buf, "Error in Command Message handler for command ID %u, Code %d", nID, nCode); + PyErr_SetString(ui_module_error, buf); + gui_print_error(); + rc = TRUE; // to avoid other code handling it. + } else + rc = !rc; + } + } + } + return rc; +} + + +PyCCmdUI::PyCCmdUI() +{ +} +PyCCmdUI::~PyCCmdUI() +{ +} + +CCmdUI *PyCCmdUI::GetCCmdUIPtr(PyObject *self) +{ + return (CCmdUI *)GetGoodCppObject( self, &type); +} + +// @pymethod |PyCCmdUI|Enable|Enables or disables the user-interface item for this command. +static PyObject * +PyCCmdUI_Enable(PyObject *self, PyObject *args) +{ + BOOL bEnable = TRUE; + if (!PyArg_ParseTuple(args,"|i:Enable", &bEnable)) // @pyparm int|bEnable|1|TRUE if the item should be enabled, false otherwise. + return NULL; + + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(self); + if (!pCU) + return NULL; + GUI_BGN_SAVE; + pCU->Enable(bEnable); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCCmdUI|SetCheck|Sets the check state of the user-interface item for this command. +static PyObject * +PyCCmdUI_SetCheck(PyObject *self, PyObject *args) +{ + int state = 1; + if (!PyArg_ParseTuple(args,"|i:SetCheck", &state)) // @pyparm int|state|1|0 for unchecked, 1 for checked, or 2 for indeterminate. + return NULL; + + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(self); + if (!pCU) + return NULL; + GUI_BGN_SAVE; + pCU->SetCheck(state); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCCmdUI|SetRadio|Like the SetCheck member function, but operates on radio groups. +static PyObject * +PyCCmdUI_SetRadio(PyObject *self, PyObject *args) +{ + BOOL bOn = TRUE; + if (!PyArg_ParseTuple(args,"|i:SetRadio", &bOn)) // @pyparm int|bOn|1|TRUE if the item should be enabled, false otherwise. + return NULL; + + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(self); + if (!pCU) + return NULL; + GUI_BGN_SAVE; + pCU->SetRadio(bOn); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCCmdUI|SetText|Sets the text for the user-interface item for this command. +static PyObject * +PyCCmdUI_SetText(PyObject *self, PyObject *args) +{ + char *txt; + if (!PyArg_ParseTuple(args,"s:SetText", &txt)) // @pyparm string|text||The text for the interface element. + return NULL; + + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(self); + if (!pCU) + return NULL; + GUI_BGN_SAVE; + pCU->SetText(txt); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCCmdUI|ContinueRouting|Tells the command-routing mechanism to continue routing the current message down the chain of handlers. +static PyObject * +PyCCmdUI_ContinueRouting(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, ContinueRouting); + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(self); + if (!pCU) + return NULL; + GUI_BGN_SAVE; + pCU->ContinueRouting(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCCmdUI|A class for manipulating user-interface elements. Encapsulates an MFC class +static struct PyMethodDef PyCCmdUI_methods[] = { + {"Enable", PyCCmdUI_Enable, 1},// @pymeth Enable|Enables or disables the user-interface item for this command. + {"SetCheck", PyCCmdUI_SetCheck, 1},// @pymeth SetCheck|Sets the check state of the user-interface item for this command. + {"SetRadio", PyCCmdUI_SetRadio, 1},// @pymeth SetRadio|Like the SetCheck member function, but operates on radio groups. + {"SetText", PyCCmdUI_SetText, 1},// @pymeth SetText|Sets the text for the user-interface item for this command. + {"ContinueRouting", PyCCmdUI_ContinueRouting,1},// @pymeth ContinueRouting|Tells the command-routing mechanism to continue routing the current message down the chain of handlers. + {NULL, NULL } +}; + +PyObject * +PyCCmdUI::getattr(char *name) +{ + if (strcmp(name, "m_nIndex")==0) { // @prop int|m_nIndex| + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(this); + if (!pCU) + return NULL; + return PyInt_FromLong(pCU->m_nIndex); + } else if (strcmp(name, "m_nID")==0) { // @prop int|m_nID| + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(this); + if (!pCU) + return NULL; + return PyInt_FromLong(pCU->m_nID); + } + return ui_assoc_object::getattr(name); +} + + +ui_type PyCCmdUI::type("PyCCmdUI", + &ui_assoc_object::type, + sizeof(PyCCmdUI), + PyCCmdUI_methods, + GET_PY_CTOR(PyCCmdUI)); + diff --git a/Pythonwin/win32cmdui.h b/Pythonwin/win32cmdui.h new file mode 100644 index 0000000000..f0345eba44 --- /dev/null +++ b/Pythonwin/win32cmdui.h @@ -0,0 +1,13 @@ +// CCmdUI definition. +// +class PyCCmdUI : public ui_assoc_object{ +public: + MAKE_PY_CTOR(PyCCmdUI); + static ui_type type; + static CCmdUI *GetCCmdUIPtr(PyObject *self); +protected: + PyCCmdUI(); + virtual ~PyCCmdUI(); + virtual PyObject *getattr(char *name); + +}; diff --git a/Pythonwin/win32context.cpp b/Pythonwin/win32context.cpp new file mode 100644 index 0000000000..1012cf877e --- /dev/null +++ b/Pythonwin/win32context.cpp @@ -0,0 +1,53 @@ +/* + + win32 context code + + This is _not_ a Python type, just a utility class. + + Created January 1996, Mark Hammond (MHammond@skippinet.com.au) +*/ + +#include "stdafx.h" +#include "win32template.h" +///////////////////////////////////////////////////////////////////// +// +// PythonCreateContext class. +// +////////////////////////////////////////////////////////////////////// +PythonCreateContext::PythonCreateContext() +{ + m_PythonObject = NULL; + m_pNewViewClass = NULL; + m_pCurrentDoc = NULL; + m_pNewDocTemplate = NULL; +} +PythonCreateContext::~PythonCreateContext() +{ + ReleasePythonObject(); +} +#define TEMPLATE_ATTR "template" +void PythonCreateContext::SetPythonObject(PyObject *ob) +{ + ASSERT(ob); + m_PythonObject = ob; + Py_INCREF(m_PythonObject); + // now try and get the template object. + + if (ob!=Py_None) { + PyObject *pTempl = PyObject_GetAttrString(ob, TEMPLATE_ATTR); + if (pTempl==NULL) + PyErr_SetString(ui_module_error, "Warning - CreateContext object has no " TEMPLATE_ATTR " attribute"); + else { + m_pNewDocTemplate = PyCDocTemplate::GetTemplate(pTempl); + } + } + if (PyErr_Occurred()) + gui_print_error(); +} +void PythonCreateContext::ReleasePythonObject() +{ + Py_XDECREF(m_PythonObject); + m_PythonObject = NULL; + m_pNewDocTemplate = NULL; +} + diff --git a/Pythonwin/win32control.cpp b/Pythonwin/win32control.cpp new file mode 100644 index 0000000000..9d31fa689e --- /dev/null +++ b/Pythonwin/win32control.cpp @@ -0,0 +1,2458 @@ +/* win32control : implementation file + + Control object - base class for listboxes, editbox's, prompts, etc. + + Created August 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32control.h" + +#include "win32gdi.h" +#include "win32bitmap.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +#define IS_LB_ERR(rc) (rc==LB_ERR||rc==LB_ERRSPACE) +#define IS_CB_ERR(rc) (rc==CB_ERR||rc==CB_ERRSPACE) + + +/////////////////////////////////////////////////////////////////////////// +// +// The control objects. +// + +/////////////////////////////////////////////////////////////////////////// +// +// The control objects. +// +ui_control_object::ui_control_object() +{ +} +ui_control_object::~ui_control_object() +{ +} + +///////////////////////////////////////////////////////////////////// +// +// ui_control_object +// +// @object PyCControl|A windows abstract control. Derived from a object. +static struct PyMethodDef ui_control_object_methods[] = { + {NULL, NULL} /* sentinel */ +}; + +ui_type_CObject ui_control_object::type("PyCControl", + &PyCWnd::type, + RUNTIME_CLASS(CObject), + sizeof(ui_control_object), + ui_control_object_methods, + NULL); + + +///////////////////////////////////////////////////////////////////// +// +// PyCButton +// +static CButton *GetButton(PyObject *self) +{ + // note we can only ask for a CWnd, if the LB is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CButton *)PyCWnd::GetPythonGenericWnd(self); +} +PyCButton::PyCButton() +{ +} +PyCButton::~PyCButton() +{ +} +// @pymethod |win32ui|CreateButton|Creates a button object. creates the actual control. +PyObject * +PyCButton_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CButton *pBut = new CButton(); + return ui_assoc_object::make( PyCButton::type, pBut ); +} + +// @pymethod |PyCButton|CreateWindow|Creates the window for a new button object. +static PyObject * +PyCButton_create_window(PyObject *self, PyObject *args) +{ + char *caption; + int style, id; + PyObject *obParent; + RECT rect; + if (!PyArg_ParseTuple(args, "si(iiii)Oi:CreateWindow", + &caption, // @pyparm string|caption||The caption (text) for the button. + &style, // @pyparm int|style||The style for the button. Use any of the win32con.BS_* constants. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the button. + &obParent, // @pyparm |parent||The parent window of the button. Usually a . + &id )) // @pyparm int|id||The buttons control ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pBut->Create(caption, style, rect, pParent, id ); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CButton::Create"); + RETURN_NONE; +} + +// @pymethod int|PyCButton|GetCheck|Retrieves the check state of a radio button or check box. +static PyObject * +PyCButton_get_check(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + int rc = pBut->GetCheck(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} +// @pymethod |PyCButton|SetCheck|Sets or resets the state of a radio button or check box. +static PyObject * +PyCButton_set_check(PyObject *self, PyObject *args) +{ + int check; + if (!PyArg_ParseTuple(args, "i", &check)) // @pyparm int|idCheck||The ID of the button. + return NULL; + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + pBut->SetCheck(check); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCButton|GetState|Returns the state of the button. +static PyObject * +PyCButton_get_state(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + int rc = pBut->GetState(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} +// @pymethod int|PyCButton|SetState|Sets the state of the button. +static PyObject * +PyCButton_set_state(PyObject *self, PyObject *args) +{ + int state; + if (!PyArg_ParseTuple(args, "i", &state)) // @pyparm int|bHighlight||The new state for the button. + return NULL; + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + pBut->SetState(state); + GUI_END_SAVE; + return Py_BuildValue("i", state); + // @comm Highlighting affects the exterior of a button control. It has no effect on the check state of a radio button or check box. +} +// @pymethod int|PyCButton|GetButtonStyle|Gets the style of the button. +static PyObject * +PyCButton_get_style(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + int rc = pBut->GetButtonStyle(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} +// @pymethod int|PyCButton|SetButtonStyle|Sets the style of the button. +static PyObject * +PyCButton_set_style(PyObject *self, PyObject *args) +{ + int style; + BOOL bRedraw = TRUE; + if (!PyArg_ParseTuple(args, "i|i", + &style, // @pyparm int|style||The new style for the button. + &bRedraw))// @pyparm int|bRedraw|1|Should the button be redrawn? + return NULL; + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + pBut->SetButtonStyle(style, bRedraw); + GUI_END_SAVE; + return Py_BuildValue("i", style); +} +// @pymethod int|PyCButton|SetBitmap|Set the button's bitmap +static PyObject * +PyCButton_set_bitmap(PyObject *self, PyObject *args) +{ + int hBitmap; + if (!PyArg_ParseTuple(args, "i", + &hBitmap)) // @pyparm int|hBitmap|1|Handle of the new bitmap + return NULL; + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + HBITMAP rc = pBut->SetBitmap((HBITMAP) hBitmap); + GUI_END_SAVE; + return Py_BuildValue("i", (int)rc); +} +// @pymethod int|PyCButton|GetBitmap|Get the button's bitmap +static PyObject * +PyCButton_get_bitmap(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CButton *pBut = GetButton(self); + if (!pBut) + return NULL; + GUI_BGN_SAVE; + HBITMAP rc = pBut->GetBitmap(); + GUI_END_SAVE; + return Py_BuildValue("i", (int)rc); +} + + +// @object PyCButton|A windows button. Encapsulates an MFC class. Derived from . +static struct PyMethodDef PyCButton_methods[] = { + {"CreateWindow", PyCButton_create_window,1}, // @pymeth CreateWindow|Creates the window for a new button object. + {"GetBitmap", PyCButton_get_bitmap, 1}, // @pymeth GetBitmap|Retrieves the bitmap associated with the button. + {"SetBitmap", PyCButton_set_bitmap, 1}, // @pymeth SetBitmap|Sets the bitmap of a button. + {"GetCheck", PyCButton_get_check, 1}, // @pymeth GetCheck|Retrieves the check state of a radio button or check box. + {"SetCheck", PyCButton_set_check, 1}, // @pymeth SetCheck|Sets the check state of a radio button or check box. + {"GetState", PyCButton_get_state, 1}, // @pymeth GetState|Retrieves the state of a radio button or check box. + {"SetState", PyCButton_set_state, 1}, // @pymeth SetState|Sets the state of a radio button or check box. + {"GetButtonStyle", PyCButton_get_style, 1}, // @pymeth GetButtonStyle|Retrieves the style of a radio button or check box. + {"SetButtonStyle", PyCButton_set_style, 1}, // @pymeth SetButtonStyle|Sets the state of a radio button or check box. + {NULL, NULL} +}; + +ui_type_CObject PyCButton::type("PyCButton", + &ui_control_object::type, + RUNTIME_CLASS(CButton), + sizeof(PyCButton), + PyCButton_methods, + GET_PY_CTOR(PyCButton)); + +///////////////////////////////////////////////////////////////////// +// +// PyCListBox +// +static CListBox *GetListBox(PyObject *self) +{ + // note we can only ask for a CWnd, if the LB is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CListBox *)PyCWnd::GetPythonGenericWnd(self); +} + +PyCListBox::PyCListBox() +{ +} +PyCListBox::~PyCListBox() +{ +} +// @pymethod int|PyCListBox|AddString|Adds a string to a listbox. +static PyObject * +PyCListBox_add_string(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + PyObject *ob; + if (!PyArg_ParseTuple(args, "O", &ob )) // @pyparm any|object||Any object. If not a string, __str__, __repr__ or a default repr() will be used + return NULL; + //@pyseemfc CListBox|AddString + GUI_BGN_SAVE; + int rc=pLB->AddString( GetReprText(ob) ); + GUI_END_SAVE; + if (IS_LB_ERR(rc)) + RETURN_ERR("PyCListBox.AddString failed"); + return Py_BuildValue("i", rc); + //@rdesc The zero based index of the new string. +} +// @pymethod int|PyCListBox|DeleteString|Deletes an item from a listbox. +static PyObject * +PyCListBox_delete_string(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) // @pyparm int|pos||The zero based index of the item to delete. + return NULL; + GUI_BGN_SAVE; + int rc=pLB->DeleteString(pos); // @pyseemfc CListBox|DeleteString + GUI_END_SAVE; + if (IS_LB_ERR(rc)) + RETURN_ERR("PyCListBox.DeleteString failed"); + return Py_BuildValue("i", rc); + // @rdesc The count of the items remaining in the list. +} +// @pymethod int|PyCListBox|Dir|Fills a listbox with a directory listing. +static PyObject * +PyCListBox_dir(PyObject *self, PyObject *args) +{ + int attr; + char *szWild; + if (!PyArg_ParseTuple(args,"is", + &attr, // @pyparm int|attr||The attributes of the files to locate + &szWild)) // @pyparm string|wild||A file specification string - eg, *.* + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc=pLB->Dir(attr, szWild); // @pyseemfc CListBox|Dir + GUI_END_SAVE; + if (IS_LB_ERR(rc)) + RETURN_ERR("PyCListBox.Dir failed"); + return Py_BuildValue("i", rc); + // @rdesc The index of the last file name added to the list. +} + +// @pymethod int|PyCListBox|InsertString|Insert a string into a listbox. +static PyObject * +PyCListBox_insert_string(PyObject *self, PyObject *args) +{ + int pos; + PyObject *ob; + if (!PyArg_ParseTuple(args, "iO", + &pos, // @pyparm int|pos||The zero based index in the listbox to insert the new string + &ob)) // @pyparm any|object||The object to be added to the listbox + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc=pLB->InsertString( pos, GetReprText(ob) ); // @pyseemfc CListBox|InsertString + GUI_END_SAVE; + if (IS_LB_ERR(rc)) + RETURN_ERR("PyCListBox.InsertString failed"); + return Py_BuildValue("i", rc); + // @rdesc The zero based index of the new string added. +} +// @pymethod |PyCListBox|ResetContent|Clear all the items from a listbox. +static PyObject * +PyCListBox_reset_content(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + pLB->ResetContent(); // @pyseemfc CListBox|ResetContent + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCListBox|GetCaretIndex|Returns the index of the item which has focus. +static PyObject * +PyCListBox_get_caret_index(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); //pyseemfc CListBox|GetCaretIndex + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetCaretIndex(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); + // @rdesc The zero-based index of the item that has the focus rectangle in a list box. + //If the list box is a single-selection list box, the return value is the index of the item that is selected, if any. + +} + +// @pymethod int|PyCListBox|GetCount|Returns the count of items in the listbox. +static PyObject * +PyCListBox_get_count(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetCount + // @rdesc Returns the number of items currently in the listbox. +} +// @pymethod int|PyCListBox|GetCurSel|Returns the index of the currently selected item. +static PyObject * +PyCListBox_get_cur_sel(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetCurSel(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetCurSel + //@comm Should not be called for a multiple selection listbox. +} +// @pymethod object|PyCListBox|GetItemData|Retrieves the application-specific object associated with an item. +PyObject *PyCListBox_GetItemData( PyObject *self, PyObject *args ) +{ + int item; + if (!PyArg_ParseTuple( args, "i:GetItemData", + &item)) // @pyparm int|item||The index of the item whose data is to be retrieved. + + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) return NULL; + GUI_BGN_SAVE; + long rc = pLB->GetItemData(item); + GUI_END_SAVE; + PyObject *ret = PyWin_GetPythonObjectFromLong(rc); + // inc ref count for return value. + Py_XINCREF(ret); + return ret; +} + +// @pymethod int|PyCListBox|GetItemValue|Retrieves the application-specific value associated with an item. +PyObject *PyCListBox_GetItemValue( PyObject *self, PyObject *args ) +{ + int item; + if (!PyArg_ParseTuple( args, "i:GetItemValue", + &item)) // @pyparm int|item||The index of the item whose data is to be retrieved. + + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) return NULL; + GUI_BGN_SAVE; + long rc = (long)pLB->GetItemData(item); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCListBox|GetSel|Returns the selection state of a specified item. +static PyObject * +PyCListBox_get_sel(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) // @pyparm int|index||The index of the item to return the state for. + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetSel(pos); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetSel + //@rdesc A +ve number if the item is selected, else zero. +} +// @pymethod int|PyCListBox|GetSelCount|Returns the number of selected items in a multiple selection listbox. +static PyObject * +PyCListBox_get_sel_count(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int ret=pLB->GetSelCount(); // @pyseemfc CListBox|GetSelCount + GUI_END_SAVE; + if (ret==LB_ERR) + RETURN_ERR("Listbox is a single selection listbox"); + return Py_BuildValue("i", ret); +} +// @pymethod list|PyCListBox|GetSelItems|Returns a list of the indexes of the currently selected items in a multiple selection listbox. +static PyObject * +PyCListBox_get_sel_items(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + int numItems; + { + GUI_BGN_SAVE; + numItems = pLB->GetSelCount(); // @pyseemfc CListBox|GetSelCount + GUI_END_SAVE; + } + if (numItems==0) + return PyList_New(0); // return an empty list + if (numItems==LB_ERR) + RETURN_ERR("Listbox is a single selection listbox"); + int *rgItems = new int[numItems]; + if (rgItems==NULL) + RETURN_ERR("Memory error"); + GUI_BGN_SAVE; + int rc = pLB->GetSelItems(numItems,rgItems); + GUI_END_SAVE; + if (rc!=numItems) { // @pyseemfc CListBox|GetSelItems + delete rgItems; + RETURN_ERR("GetSelItems failed!"); + } + PyObject *list; + if ((list = PyList_New(numItems)) == NULL) { + delete rgItems; + return NULL; + } + for (int i=0;iGetSelCount(); // @pyseemfc CListBox|GetSelCount + GUI_END_SAVE; + } + if (numItems==0) + return PyList_New(0); // return an empty list + if (numItems==LB_ERR) + RETURN_ERR("Listbox is a single selection listbox"); + int *rgItems = new int[numItems]; + if (rgItems==NULL) + RETURN_ERR("Memory error"); + GUI_BGN_SAVE; + int rc = pLB->GetSelItems(numItems,rgItems); + GUI_END_SAVE; + if (rc!=numItems) { // @pyseemfc CListBox|GetSelItems + delete rgItems; + RETURN_ERR("GetSelItems failed!"); + } + PyObject *list; + if ((list = PyList_New(numItems)) == NULL) { + delete rgItems; + return NULL; + } + for (int i=0;iGetText(rgItems[i], value); // @pyseemfc CListBox|GetText + GUI_END_SAVE; + PyList_SetItem( list, i, Py_BuildValue("s", value)); + } + + delete rgItems; + return list; +} + +// @pymethod string|PyCListBox|GetText|Returns the string for a specified item. +static PyObject * +PyCListBox_get_text(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) //@pyparm int|index||The index of the item to retrieve the text of + return NULL; + CString cs; + + GUI_BGN_SAVE; + int len = pLB->GetTextLen(pos); + if (len<0) { + GUI_BLOCK_THREADS; + RETURN_ERR("The item does not exist"); + } + pLB->GetText(pos, cs.GetBufferSetLength(len)); + cs.ReleaseBuffer(); + GUI_END_SAVE; + return Py_BuildValue("s", (const char *)cs); +} +// @pymethod int|PyCListBox|GetTextLen|Returns the length of the string for a specified item. +static PyObject * +PyCListBox_get_text_len(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) //@pyparm int|index||The index of the item to retrieve the length of the text. + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetTextLen(pos); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetTextLen +} + +// @pymethod int|PyCListBox|GetTopIndex|Returns the index of the top most visible item. +static PyObject * +PyCListBox_get_top_index(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetTopIndex(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetTopIndex + // @rdesc The zero based index of the top most visible item. +} + +// @pymethod |PyCListBox|SelectString|Searches for a list-box item that matches the specified string, and selects it. +static PyObject * +PyCListBox_select_string(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + char *string; + int after; + if (!PyArg_ParseTuple(args,"is", + &after, // @pyparm int|after||Contains the zero-based index of the item before the first item to be searched, or -1 for the entire listbox. + &string)) // @pyparm string|string||The string to search for. + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SelectString(after, string); + GUI_END_SAVE; + if (rc==LB_ERR) // @pyseemfc CListBox|SelectString + RETURN_ERR("The string does not exist"); + RETURN_NONE; + // @rdesc The return value is always None - an exception is raised if the string can not be located. +} + +// @pymethod |PyCListBox|SelItemRange|Selects an item range. +static PyObject * +PyCListBox_sel_item_range(PyObject *self, PyObject *args) +{ + int bSel, start, end; + if (!PyArg_ParseTuple(args, "iii", + &bSel, // @pyparm int|bSel||Should the selection specified be set or cleared? + &start, // @pyparm int|start||The zero based index of the first item to select. + &end)) // @pyparm int|end||The zero based index of the last item to select. + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SelItemRange(bSel, start, end); + GUI_END_SAVE; + if (rc==LB_ERR) + RETURN_ERR("SelItemRange failed"); + RETURN_NONE; +} + +// @pymethod |PyCListBox|SetCaretIndex|Sets the focus rectange to a specified item. +static PyObject * +PyCListBox_set_caret_index(PyObject *self, PyObject *args) +{ + int index; + BOOL bScroll = TRUE; + if (!PyArg_ParseTuple(args,"i|i", + &index, // @pyparm int|index||The zero based index of the item. + &bScroll))// @pyparm int|bScroll|1|Should the listbox scroll to the item? + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SetCaretIndex(index, bScroll); + GUI_END_SAVE; + if (rc==LB_ERR) // @pyseemfc CListBox|SetCaretIndex + RETURN_ERR("SetCaretIndex failed"); + RETURN_NONE; +} +// @pymethod |PyCListBox|SetSel|Selects an item in a multiple selection listbox. +static PyObject * +PyCListBox_set_sel(PyObject *self, PyObject *args) +{ + int index; + BOOL bSel = TRUE; + if (!PyArg_ParseTuple(args,"i|i", + &index, // @pyparm int|index||The zero based index of the item to select. + &bSel)) // @pyparm int|bSel|1|Should the item be selected or deselected? + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SetSel(index, bSel); + GUI_END_SAVE; + if (rc==LB_ERR) // @pyseemfc CListBox|SetSel + RETURN_ERR("SetSel failed"); + RETURN_NONE; +} +// @pymethod |PyCListBox|SetCurSel|Selects an item in a single selection listbox. +static PyObject * +PyCListBox_set_cur_sel(PyObject *self, PyObject *args) +{ + int index; + if (!PyArg_ParseTuple(args,"i",&index)) // @pyparm int|index||The zero based index of the item to select. + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SetCurSel(index); + GUI_END_SAVE; + if (rc==LB_ERR && index!=LB_ERR) // @pyseemfc CListBox|SetCurSel + RETURN_ERR("SetCurSel failed"); + RETURN_NONE; +} +// @pymethod int|PyCListBox|SetItemData|Sets the item's application-specific object value. +PyObject *PyCListBox_SetItemData( PyObject *self, PyObject *args ) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) return NULL; + int item; + PyObject *data; + if (!PyArg_ParseTuple( args, "iO:SetItemData", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &data)) // @pyparm object|Data||New value for the data. + return NULL; + if (data==Py_None) data = NULL; + GUI_BGN_SAVE; + BOOL ok = pLB->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemData failed"); + // @comm Note that a reference count is not added to the object. This it is your + // responsibility to make sure the object remains alive while in the list. + RETURN_NONE; +} + +// @pymethod int|PyCListBox|SetItemValue|Sets the item's application-specific value. +PyObject *PyCListBox_SetItemValue( PyObject *self, PyObject *args ) +{ + CListBox *pLB = GetListBox(self); + if (!pLB) return NULL; + int item; + int data; + if (!PyArg_ParseTuple( args, "ii:SetItemValue", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &data)) // @pyparm int|data||New value for the data. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pLB->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemValue failed"); + RETURN_NONE; +} + +// @pymethod |PyCListBox|SetTabStops|Sets the tab stops for a listbox. +static PyObject * +PyCListBox_set_tab_stops(PyObject *self, PyObject *args) +{ + CListBox *pLB = GetListBox(self); + int index; + BOOL rc; + if (!pLB) + return NULL; + // @pyparm int|eachTabStop||The position for each tab stop. + if (PyArg_ParseTuple(args,"i",&index)) { + GUI_BGN_SAVE; + rc = pLB->SetTabStops(index); + GUI_END_SAVE; + } + else { + PyObject *listOb; + PyErr_Clear(); + // @pyparmalt1 list of integers|tabStops||Each individual tab stop. + if (!PyArg_ParseTuple(args,"O",&listOb)) + return NULL; + if (!PyList_Check(listOb)) + RETURN_TYPE_ERR("Param must be a list object"); + int numChildren = PyList_Size(listOb); + int *pArray = new int[numChildren]; + int tabVal; + for (int child=0;childSetTabStops( numChildren, pArray ); + GUI_END_SAVE; + delete pArray; + } + + if (!rc) + RETURN_ERR("SetTabStops failed"); + RETURN_NONE; +} + + +// @pymethod |PyCListBox|SetTopIndex|Sets the top index (top most visible item) of the listbox. +static PyObject * +PyCListBox_set_top_index(PyObject *self, PyObject *args) +{ + int index; + if (!PyArg_ParseTuple(args,"i",&index)) // @pyparm int|index||The zero based index of the item to place at the top of the list. + return NULL; + CListBox *pLB = GetListBox(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->SetTopIndex(index); + GUI_END_SAVE; + if (rc==LB_ERR) // @pyseemfc CListBox|SetTopIndex + RETURN_ERR("SetTopIndex failed"); + RETURN_NONE; +} + +// @object PyCListBox|A windows listbox control. Encapsulates an MFC class. Derived from a object. +static struct PyMethodDef PyCListBox_methods[] = { + {"AddString", PyCListBox_add_string, 1}, // @pymeth AddString|Add a string to the listbox. + {"DeleteString", PyCListBox_delete_string, 1}, // @pymeth DeleteString|Delete a string from the listbox. + {"Dir", PyCListBox_dir, 1}, // @pymeth Dir|Fill a listbox with a file specification. + {"GetCaretIndex", PyCListBox_get_caret_index, 1}, // @pymeth GetCaretIndex|Get the index of the item with the focus rectangle. + {"GetCount", PyCListBox_get_count, 1}, // @pymeth GetCount|Get the count of items in the listbox. + {"GetCurSel", PyCListBox_get_cur_sel, 1}, // @pymeth GetCurSel|Get the current selection in a single selection listbox. + {"GetItemData", PyCListBox_GetItemData, 1}, // @pymeth GetItemData|Retrieves the application-specific object associated with a listbox entry + {"GetItemValue", PyCListBox_GetItemValue, 1}, // @pymeth GetItemValue|Retrieves the application-specific value associated with a listbox entry + {"GetSel", PyCListBox_get_sel, 1}, // @pymeth GetSel|Get the selected items in a multiple selection listbox. + {"GetSelCount", PyCListBox_get_sel_count, 1}, // @pymeth GetSelCount|Get the number of selected items in a multtiple selection listbox. + {"GetSelItems", PyCListBox_get_sel_items, 1}, // @pymeth GetSelItems|Get the index of the selected items in a multiple selection listbox. + {"GetSelTextItems", PyCListBox_get_sel_text_items, 1}, // @pymeth GetSelTextItems|Get the text of the selected items in a multiple selection listbox. + {"GetTopIndex", PyCListBox_get_top_index, 1}, // @pymeth GetTopIndex|Get the index of the topmost item. + {"GetText", PyCListBox_get_text, 1}, // @pymeth GetText|Get the text associated with an item. + {"GetTextLen", PyCListBox_get_text_len, 1}, // @pymeth GetTextLen|Get the length of an item + {"InsertString", PyCListBox_insert_string, 1}, // @pymeth InsertString|Insert a string into the listbox. + {"ResetContent", PyCListBox_reset_content, 1}, // @pymeth ResetContent|Remove all items from a listbox. + {"SetCaretIndex", PyCListBox_set_caret_index, 1}, // @pymeth SetCaretIndex|Set the focus rectange to a specified item. + {"SelectString", PyCListBox_select_string, 1}, // @pymeth SelectString|Select an item, based on a string. + {"SelItemRange", PyCListBox_sel_item_range, 1}, // @pymeth SelItemRange|Select a range of items in a multiple selection listbox. + {"SetCurSel", PyCListBox_set_cur_sel, 1}, // @pymeth SetCurSel|Set the current selection in a single selection listbox. + {"SetItemData", PyCListBox_SetItemData, 1}, // @pymeth SetItemData|Sets the application-specific object associated with a listbox entry + {"SetItemValue", PyCListBox_SetItemValue, 1}, // @pymeth SetItemValue|Sets the application-specific value associated with a listbox entry + {"SetSel", PyCListBox_set_sel, 1}, // @pymeth SetSel|Set the selection. + {"SetTabStops", PyCListBox_set_tab_stops, 1}, // @pymeth SetTabStops|Set the tab stops for a listbox. + {"SetTopIndex", PyCListBox_set_top_index, 1}, // @pymeth SetTopIndex|Set the top most visible item in a listbox. + {NULL, NULL} +}; + +ui_type_CObject PyCListBox::type("PyCListBox", + &ui_control_object::type, + RUNTIME_CLASS(CListBox), + sizeof(PyCListBox), + PyCListBox_methods, + GET_PY_CTOR(PyCListBox)); + +///////////////////////////////////////////////////////////////////// +// +// PyCComboBox +// +static CComboBox *GetCombo(PyObject *self) +{ + // note we can only ask for a CWnd, if the LB is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CComboBox *)PyCWnd::GetPythonGenericWnd(self); +} +PyCComboBox::PyCComboBox() +{ +} +PyCComboBox::~PyCComboBox() +{ +} +// @pymethod int|PyCComboBox|AddString|Adds a string to a combobox. +static PyObject * +PyCComboBox_add_string(PyObject *self, PyObject *args) +{ + CComboBox *pCB = GetCombo(self); + if (!pCB) + return NULL; + PyObject *ob; + if (!PyArg_ParseTuple(args, "O", &ob )) // @pyparm any|object||Any object. If not a string, __str__, __repr__ or a default repr() will be used + return NULL; + GUI_BGN_SAVE; + int rc=pCB->AddString( GetReprText(ob) ); + GUI_END_SAVE; + //@pyseemfc CComboBox|AddString + if (IS_CB_ERR(rc)) + RETURN_ERR("PyCComboBox.AddString failed"); + return Py_BuildValue("i", rc); + //@rdesc The zero based index of the new string. +} +// @pymethod int|PyCComboBox|DeleteString|Deletes an item from a combobox. +static PyObject * +PyCComboBox_delete_string(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) // @pyparm int|pos||The zero based index of the item to delete. + return NULL; + GUI_BGN_SAVE; + int rc = pLB->DeleteString(pos); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|DeleteString + // @rdesc The count of the items remaining in the list. +} +// @pymethod int|PyCComboBox|Dir|Fills the list portion of a combobox with a directory listing. +static PyObject * +PyCComboBox_dir(PyObject *self, PyObject *args) +{ + int attr; + char *szWild; + if (!PyArg_ParseTuple(args,"is", + &attr, // @pyparm int|attr||The attributes of the files to locate + &szWild)) // @pyparm string|wild||A file specification string - eg, *.* + return NULL; + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->Dir(attr, szWild); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|Dir + // @rdesc The index of the last file name added to the list. +} + +// @pymethod int|PyCComboBox|InsertString|Insert a string into a combobox. +static PyObject * +PyCComboBox_insert_string(PyObject *self, PyObject *args) +{ + int pos; + PyObject *ob; + if (!PyArg_ParseTuple(args, "iO", + &pos, // @pyparm int|pos||The zero based index in the combobox to insert the new string + &ob)) // @pyparm any|object||The object to be added to the combobox + return NULL; + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->InsertString( pos, GetReprText(ob) ); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|InsertString + // @rdesc The zero based index of the new string added. +} +// @pymethod |PyCComboBox|ResetContent|Clear all the items from a combobox. +static PyObject * +PyCComboBox_reset_content(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + pLB->ResetContent(); // @pyseemfc CComboBox|ResetContent + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCComboBox|GetCount|Returns the count of items in the combobox. +static PyObject * +PyCComboBox_get_count(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CComboBox *pCB = GetCombo(self); + if (!pCB) + return NULL; + GUI_BGN_SAVE; + int rc = pCB->GetCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CListBox|GetCount + // @rdesc Returns the number of items currently in the combobox. +} +// @pymethod int|PyCComboBox|GetCurSel|Returns the index of the currently selected item. +static PyObject * +PyCComboBox_get_cur_sel(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetCurSel(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|GetCurSel + //@comm Should not be called for a multiple selection listbox. +} +// @pymethod int|PyCComboBox|GetEditSel|Returns the selection of the edit control portion of a combo box. +static PyObject * +PyCComboBox_get_edit_sel(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc=pLB->GetEditSel();// @pyseemfc CComboBox|GetEditSel + GUI_END_SAVE; + if (IS_CB_ERR(rc)) + RETURN_ERR("GetEditSel failed"); + + return Py_BuildValue("i", rc); + // @rdesc A 32-bit value that contains the starting position in the low-order word and + // the position of the first nonselected character after the end of + // the selection in the high-order word. If this function is used on a combo box + // without an edit control, an exception is raised. +} +// @pymethod int|PyCComboBox|GetExtendedUI|Indicates if the combo has the extended interface. +static PyObject * +PyCComboBox_get_extended_ui(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + int rc = pLB->GetExtendedUI(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|GetExtendedUI + // @rdesc Nonzero if the combo box has the extended user interface; otherwise 0. +} + +// @pymethod object|PyCComboBox|GetItemData|Retrieves the application-specific object associated with an item. +PyObject *PyCComboBox_GetItemData( PyObject *self, PyObject *args ) +{ + int item; + if (!PyArg_ParseTuple( args, "i:GetItemData", + &item)) // @pyparm int|item||The index of the item whose data is to be retrieved. + + return NULL; + CComboBox *pLB = GetCombo(self); + if (!pLB) return NULL; + GUI_BGN_SAVE; + long rc = pLB->GetItemData(item); + GUI_END_SAVE; + PyObject *ret = PyWin_GetPythonObjectFromLong(rc); + // inc ref count for return value. + Py_XINCREF(ret); + return ret; +} + +// @pymethod int|PyCComboBox|GetItemValue|Retrieves the application-specific value associated with an item. +PyObject *PyCComboBox_GetItemValue( PyObject *self, PyObject *args ) +{ + int item; + if (!PyArg_ParseTuple( args, "i:GetItemValue", + &item)) // @pyparm int|item||The index of the item whose data is to be retrieved. + + return NULL; + CComboBox *pLB = GetCombo(self); + if (!pLB) return NULL; + GUI_BGN_SAVE; + long rc = (long)pLB->GetItemData(item); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod string|PyCComboBox|GetLBText|Gets the string from the list of a combo box. +static PyObject * +PyCComboBox_get_lb_text(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int pos; // @pyparm int|index||The index of the item to return the string for. + if (!PyArg_ParseTuple(args,"i",&pos)) + return NULL; + CString cs; + // Prevent MFC ASSERTing when empty - dont use the CString version. + GUI_BGN_SAVE; + int size = pLB->GetLBTextLen(pos); + if (size != LB_ERR) { + pLB->GetLBText(pos, cs.GetBufferSetLength(size)); + cs.ReleaseBuffer(); + } + GUI_END_SAVE; + if (IS_CB_ERR(size)) + RETURN_ERR("GetLBText failed - invalid index"); + return Py_BuildValue("s", (const char *)cs); + // @rdesc The requested string. If index does + // not specify a valid index, no exception is raised. +} +// @pymethod int|PyCComboBox|GetLBTextLen|Returns the length of a string in the list of a combobox. +static PyObject * +PyCComboBox_get_lb_text_len(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int pos; // @pyparm int|index||The index of the item to return the length of. + if (!PyArg_ParseTuple(args,"i",&pos)) + return NULL; + GUI_BGN_SAVE; + int rc=pLB->GetLBTextLen(pos); + GUI_END_SAVE; + if (IS_CB_ERR(rc)) + RETURN_ERR("PyCComboBox.GetLBTextLen failed"); + return Py_BuildValue("i", rc); // @pyseemfc CComboBox|GetLBTextLen + // @ rdesc Returns the length of the string (in bytes), or raises an exception on error. +} +// @pymethod int|PyCComboBox|LimitText|Limits the amount of text the edit portion of a combo box can hold. +static PyObject * +PyCComboBox_limit_text(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int pos; + if (!PyArg_ParseTuple(args,"i",&pos)) // @pyparm int|max||The maximum number of characters the user can enter. If zero, the size is set to (virtually) unlimited. + return NULL; + GUI_BGN_SAVE; + long rc = pLB->LimitText(pos); + GUI_END_SAVE; + if (rc==CB_ERR) // @pyseemfc CComboBox|LimitText + RETURN_ERR("Combo does not have an edit box"); + RETURN_NONE; +} + +// @pymethod |PyCComboBox|SelectString|Searches for a combobox item that matches the specified string, and selects it. +static PyObject * +PyCComboBox_select_string(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + char *string; + int after; + if (!PyArg_ParseTuple(args,"is", + &after, // @pyparm int|after||Contains the zero-based index of the item before the first item to be searched, or -1 for the entire combobox. + &string)) // @pyparm string|string||The string to search for. + return NULL; + GUI_BGN_SAVE; + long rc = pLB->SelectString(after, string); + GUI_END_SAVE; + if (rc==CB_ERR) // @pyseemfc CComboBoxBox|SelectString + RETURN_ERR("The string does not exist"); + RETURN_NONE; + // @rdesc The return value is always None - an exception is raised if the string can not be located. +} + +// @pymethod |PyCComboBox|SetCurSel|Selects an item in a combobox. +static PyObject * +PyCComboBox_set_cur_sel(PyObject *self, PyObject *args) +{ + int index; + if (!PyArg_ParseTuple(args,"i",&index)) // @pyparm int|index||The zero based index of the item to select. + return NULL; + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + GUI_BGN_SAVE; + long rc = pLB->SetCurSel(index); + GUI_END_SAVE; + if (rc==CB_ERR && index!=CB_ERR) // @pyseemfc CComboBox|SetCurSel + RETURN_ERR("SetCurSel failed"); + RETURN_NONE; +} +// @pymethod |PyCComboBox|SetEditSel|Sets the selection in the edit control portion of a combo box. +static PyObject * +PyCComboBox_set_edit_sel(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int start, end; + if (!PyArg_ParseTuple(args,"ii", + &start, // @pyparm int|start||Specifies the starting position. If the starting position is set to -1, then any existing selection is removed. + &end)) // @pyparm int|end||Specifies the ending position. If the ending position is set to -1, then all text from the starting position to the last character in the edit control is selected. + return NULL; + GUI_BGN_SAVE; + long rc = pLB->SetEditSel(start, end); + GUI_END_SAVE; + if (rc==CB_ERR) // @pyseemfc PyCComboBox|SetEditSel + RETURN_ERR("Combo is dropdown, or does not have an edit box"); + RETURN_NONE; + // @rdesc The return value is always None - an exception is raised if the combo is a dropdown style, or does not have an edit control. +} + +// @pymethod |PyCComboBox|SetExtendedUI|Selects the Extended UI mode for a combo box. +static PyObject * +PyCComboBox_set_extended_ui(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int flag = TRUE; // @pyparm int|bExtended|1|Indicates if the combo should have the extended user interface. + if (!PyArg_ParseTuple(args,"|i",&flag)) + return NULL; + GUI_BGN_SAVE; + long rc = pLB->SetExtendedUI(flag); + GUI_END_SAVE; + if (rc==CB_ERR) // @pyseemfc CListBox|SetExtendedUI + RETURN_ERR("SetExtendedUI failed"); + // @comm A combo box with the Extended UI flag set can be identified in the following ways:~ + // * Clicking the static control displays the list box only for combo boxes with the CBS_DROPDOWNLIST style.~ + // * Pressing the DOWN ARROW key displays the list box (F4 is disabled).~ + // * Scrolling in the static control is disabled when the item list is not visible (the arrow keys are disabled). + RETURN_NONE; +} +// @pymethod int|PyCComboBox|SetItemData|Sets the item's application-specific object value. +PyObject *PyCComboBox_SetItemData( PyObject *self, PyObject *args ) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) return NULL; + int item; + PyObject *data; + if (!PyArg_ParseTuple( args, "iO:SetItemData", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &data)) // @pyparm object|Data||New value for the data. + return NULL; + if (data==Py_None) data = NULL; + GUI_BGN_SAVE; + BOOL ok = pLB->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemData failed"); + // @comm Note that a reference count is not added to the object. This it is your + // responsibility to make sure the object remains alive while in the list. + RETURN_NONE; +} + +// @pymethod int|PyCComboBox|SetItemValue|Sets the item's application-specific value. +PyObject *PyCComboBox_SetItemValue( PyObject *self, PyObject *args ) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) return NULL; + int item; + int data; + if (!PyArg_ParseTuple( args, "ii:SetItemValue", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &data)) // @pyparm int|data||New value for the data. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pLB->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemValue failed"); + RETURN_NONE; +} +static PyObject * +// @pymethod |PyCComboBox|ShowDropDown|Shows or hides the listbox portion of a combo box. +PyCComboBox_show_drop_down(PyObject *self, PyObject *args) +{ + CComboBox *pLB = GetCombo(self); + if (!pLB) + return NULL; + int flag = TRUE; // @pyparm int|bShowIt|1|Indicates if the listbox should be shown or hidden. + if (!PyArg_ParseTuple(args,"|i",&flag)) + return NULL; + GUI_BGN_SAVE; + pLB->ShowDropDown(flag); + GUI_END_SAVE; + RETURN_NONE; +} +// @object PyCComboBox|A windows combo control. Encapsulates an MFC class. Derived from a object. +static struct PyMethodDef PyCComboBox_methods[] = { + {"AddString", PyCComboBox_add_string, 1}, // @pymeth AddString|Add a string to the listbox portion of a combo box. + {"DeleteString", PyCComboBox_delete_string, 1}, // @pymeth DeleteString|Delete a string to the listbox portion of a combo box. + {"Dir", PyCComboBox_dir, 1}, // @pymeth Dir|Fill the listbox portion of a combo with a file specification. + {"GetCount", PyCComboBox_get_count, 1}, // @pymeth GetCount|Get the count of items in the listbox portion of a combo box. + {"GetCurSel", PyCComboBox_get_cur_sel, 1}, // @pymeth GetCurSel|Get the current selection in the listbox portion of a combo box. + {"GetEditSel", PyCComboBox_get_edit_sel, 1}, // @pymeth GetEditSel|Gets the edit control selection from a combo box. + {"GetExtendedUI", PyCComboBox_get_extended_ui, 1}, // @pymeth GetExtendedUI|Gets the ExtendedUI flag for a combo box. + {"GetItemData", PyCComboBox_GetItemData, 1}, // @pymeth GetItemData|Retrieves the application-specific object associated with a combobox entry + {"GetItemValue", PyCComboBox_GetItemValue, 1}, // @pymeth GetItemValue|Retrieves the application-specific value associated with a combobox entry + {"GetLBText", PyCComboBox_get_lb_text, 1}, // @pymeth GetLBText|Gets the text from the edit control in a combo box. + {"GetLBTextLen", PyCComboBox_get_lb_text_len, 1}, // @pymeth GetLBTextLen|Gets the length of the text in the edit control of a combo box. + {"InsertString", PyCComboBox_insert_string, 1}, // @pymeth InsertString|Inserts a string into the listbox portion of a combo box. + {"LimitText", PyCComboBox_limit_text, 1}, // @pymeth LimitText|Limit the length of text in the edit control portion of a combo box. + {"ResetContent", PyCComboBox_reset_content, 1}, // @pymeth ResetContent|Remove all items from the listbox portion of a combo box. + {"SelectString", PyCComboBox_select_string, 1}, // @pymeth SelectString|Select a string in the listbox portion of a combo box. + {"SetCurSel", PyCComboBox_set_cur_sel, 1}, // @pymeth SetCurSel|Sets the current selection in the listbox portion of a combo box. + {"SetEditSel", PyCComboBox_set_edit_sel, 1}, // @pymeth SetEditSel|Sets the current selection in the edit control portion of a combo box. + {"SetExtendedUI", PyCComboBox_set_extended_ui, 1}, // @pymeth SetExtendedUI|Sets the ExtendedUI flag for a combo box. + {"SetItemData", PyCComboBox_SetItemData, 1}, // @pymeth SetItemData|Sets the application-specific object associated with a combobox entry + {"SetItemValue", PyCComboBox_SetItemValue, 1}, // @pymeth SetItemValue|Sets the application-specific value associated with a combobox entry + {"ShowDropDown", PyCComboBox_show_drop_down,1}, // @pymeth ShowDropDown|Shows the listbox portion of a combo box. + {NULL, NULL} +}; + +ui_type_CObject PyCComboBox::type("PyCComboBox", + &ui_control_object::type, + RUNTIME_CLASS(CComboBox), + sizeof(PyCComboBox), + PyCComboBox_methods, + GET_PY_CTOR(PyCComboBox)); + +///////////////////////////////////////////////////////////////////// +// +// PyCProgressCtrl +// +static CProgressCtrl *GetProgressCtrl(PyObject *self) +{ + // note we can only ask for a CWnd, if the PC is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CProgressCtrl *)PyCWnd::GetPythonGenericWnd(self); +} +PyCProgressCtrl::PyCProgressCtrl() +{ +} +PyCProgressCtrl::~PyCProgressCtrl() +{ +} + +// @pymethod |win32ui|CreateProgressCtrl|Creates a progress control object. creates the actual control. +PyObject * +PyCProgressCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CProgressCtrl *pPC = new CProgressCtrl(); + return ui_assoc_object::make( PyCProgressCtrl::type, pPC ); +} + +// @pymethod |PyCProgressCtrl|CreateWindow|Creates the actual control. +PyObject * +PyCProgressCtrl_create_window(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The style for the control. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the control. + &obParent, // @pyparm |parent||The parent window of the control. Usually a . + &id )) // @pyparm int|id||The control's ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pPC->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CProgressCtrl::Create"); + RETURN_NONE; +} +// @pymethod |PyCProgressCtrl|SetRange|Set the control's bounds +static PyObject * +PyCProgressCtrl_set_range(PyObject *self, PyObject *args) +{ + int nLower, nUpper; + if (!PyArg_ParseTuple(args, "ii", + &nLower,// @pyparm int|nLower|1|Specifies the lower limit of the range (default is zero). + &nUpper))// @pyparm int|nUpper|1|Specifies the upper limit of the range (default is 100). + return NULL; + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + GUI_BGN_SAVE; + pPC->SetRange(nLower, nUpper); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCProgressCtrl|SetPos|Set the control's position +static PyObject * +PyCProgressCtrl_set_pos(PyObject *self, PyObject *args) +{ + int nPos; + if (!PyArg_ParseTuple(args, "i", + &nPos))// @pyparm int|nPos|1|New position of the progress bar control. + return NULL; + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + GUI_BGN_SAVE; + int rc = pPC->SetPos(nPos); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCProgressCtrl|OffsetPos|Advances the progress bar control's current position by the increment specified +static PyObject * +PyCProgressCtrl_offset_pos(PyObject *self, PyObject *args) +{ + int nPos; + if (!PyArg_ParseTuple(args, "i", + &nPos))// @pyparm int|nPos|1|Amount to advance the position. + return NULL; + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + GUI_BGN_SAVE; + int rc = pPC->OffsetPos(nPos); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCProgressCtrl|SetStep|Specifies the step increment for a progress bar control. +static PyObject * +PyCProgressCtrl_set_step(PyObject *self, PyObject *args) +{ + int nStep; + if (!PyArg_ParseTuple(args, "i", + &nStep))// @pyparm int|nStep|1|New step increment. + return NULL; + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + GUI_BGN_SAVE; + int rc = pPC->SetStep(nStep); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCProgressCtrl|StepIt|Advances the current position for a progress bar control by the step increment. Returns previous position. +static PyObject * +PyCProgressCtrl_step(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CProgressCtrl *pPC = GetProgressCtrl(self); + if (!pPC) + return NULL; + GUI_BGN_SAVE; + int rc = pPC->StepIt(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @object PyCProgressCtrl|A windows progress bar control. Encapsulates an MFC class. Derived from . +static struct PyMethodDef PyCProgressCtrl_methods[] = { + {"CreateWindow", PyCProgressCtrl_create_window,1}, // @pymeth CreateWindow|Creates the window for a new progress bar object. + {"SetRange", PyCProgressCtrl_set_range, 1}, // @pymeth SetRange|Sets the lower and upper bounds for the progress bar. + {"SetPos", PyCProgressCtrl_set_pos, 1}, // @pymeth SetPos|Set the control's position + {"OffsetPos", PyCProgressCtrl_offset_pos, 1}, // @pymeth OffsetPos|Advances the progress bar control's current position by the increment specified. + {"SetStep", PyCProgressCtrl_set_step, 1}, // @pymeth SetStep|Specifies the step increment for a progress bar control. + {"StepIt", PyCProgressCtrl_step, 1}, // @pymeth StepIt|Advances the current position for a progress bar control by the step increment. Returns previous position. + {NULL, NULL} +}; + +ui_type_CObject PyCProgressCtrl::type("PyCProgressCtrl", + &ui_control_object::type, + RUNTIME_CLASS(CProgressCtrl), + sizeof(PyCProgressCtrl), + PyCProgressCtrl_methods, + GET_PY_CTOR(PyCProgressCtrl)); + + +///////////////////////////////////////////////////////////////////// +// +// PyCSliderCtrl +// +static CSliderCtrl *GetSliderCtrl(PyObject *self) +{ + // note we can only ask for a CWnd, if the SC is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CSliderCtrl *)PyCWnd::GetPythonGenericWnd(self); +} +PyCSliderCtrl::PyCSliderCtrl() +{ +} +PyCSliderCtrl::~PyCSliderCtrl() +{ +} + +// @pymethod |win32ui|CreateSliderCtrl|Creates a Slider control object. creates the actual control. +PyObject * +PyCSliderCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pPC = new CSliderCtrl(); + return ui_assoc_object::make( PyCSliderCtrl::type, pPC ); +} + +// @pymethod |PyCSliderCtrl|CreateWindow|Creates the actual control. +PyObject * +PyCSliderCtrl_create_window(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The style for the control. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the control. + &obParent, // @pyparm |parent||The parent window of the control. Usually a . + &id )) // @pyparm int|id||The control's ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pSC->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CSliderCtrl::Create"); + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|GetLineSize|Get the control's position +static PyObject * +PyCSliderCtrl_get_line_size(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetLineSize(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|SetLineSize|Set the control's line size. Returns the previous line size. +static PyObject * +PyCSliderCtrl_set_line_size(PyObject *self, PyObject *args) +{ + int nLineSize; + if (!PyArg_ParseTuple(args, "i", + &nLineSize))// @pyparm int|nLineSize|1|New line size of the Slider bar control + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->SetLineSize(nLineSize); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetPageSize|Get the control's position +static PyObject * +PyCSliderCtrl_get_page_size(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetPageSize(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|SetPageSize|Set the control's page size Returns the previous page size. +static PyObject * +PyCSliderCtrl_set_page_size(PyObject *self, PyObject *args) +{ + int nPageSize; + if (!PyArg_ParseTuple(args, "i", + &nPageSize))// @pyparm int|nPageSize|1|New page size of the Slider bar control. + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->SetPageSize(nPageSize); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetRangeMax|Get the control's Maximum +static PyObject * +PyCSliderCtrl_get_range_max(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetRangeMax(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetRangeMin|Get the control's Minimum +static PyObject * +PyCSliderCtrl_get_range_min(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetRangeMin(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetRange|Get the control's min and max +static PyObject * +PyCSliderCtrl_get_range(PyObject *self, PyObject *args) +{ + int nMin, nMax; + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->GetRange(nMin, nMax); + GUI_END_SAVE; + return Py_BuildValue("(ii)", nMin, nMax); +} + +// @pymethod int|PyCSliderCtrl|SetRangeMin|Set the control's minimum +static PyObject * +PyCSliderCtrl_set_range_min(PyObject *self, PyObject *args) +{ + int nRangeMin; + BOOL bRedraw = FALSE; + if (!PyArg_ParseTuple(args, "i|i", + &nRangeMin,// @pyparm int|nRangeMin|1|New minimum of the Slider bar control. + &bRedraw))// @pyparm int|bRedraw|1|Should slider be redrawn? + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetRangeMin(nRangeMin, bRedraw); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|SetRangeMax|Set the control's maximum +static PyObject * +PyCSliderCtrl_set_range_max(PyObject *self, PyObject *args) +{ + int nRangeMax; + BOOL bRedraw = FALSE; + if (!PyArg_ParseTuple(args, "i|i", + &nRangeMax,// @pyparm int|nRangeMax|1|New maximum of the Slider bar control. + &bRedraw))// @pyparm int|bRedraw|1|Should slider be redrawn? + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetRangeMax(nRangeMax, bRedraw); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|SetRange|Set the control's min and max +static PyObject * +PyCSliderCtrl_set_range(PyObject *self, PyObject *args) +{ + int nRangeMax; + int nRangeMin; + BOOL bRedraw = FALSE; + if (!PyArg_ParseTuple(args, "ii|i", + &nRangeMin,// @pyparm int|nRangeMin|1|New minimum of the Slider bar control. + &nRangeMax,// @pyparm int|nRangeMax|1|New maximum of the Slider bar control. + &bRedraw))// @pyparm int|bRedraw|1|Should slider be redrawn? + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetRange(nRangeMin, nRangeMax, bRedraw); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|GetSelection|Get the control's seledction start and end positions +static PyObject * +PyCSliderCtrl_get_selection(PyObject *self, PyObject *args) +{ + int nMin, nMax; + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->GetSelection(nMin, nMax); + GUI_END_SAVE; + return Py_BuildValue("(ii)", nMin, nMax); +} + +// @pymethod int|PyCSliderCtrl|SetSelection|Set the control's selection start and end positions +static PyObject * +PyCSliderCtrl_set_selection(PyObject *self, PyObject *args) +{ + int nRangeMax; + int nRangeMin; + if (!PyArg_ParseTuple(args, "ii", + &nRangeMin,// @pyparm int|nRangeMin|1|New start of the Slider's selection. + &nRangeMax))// @pyparm int|nRangeMax|1|New end of the Slider's selection. + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetSelection(nRangeMin, nRangeMax); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|GetChannelRect|Get the control's channel rectangle +static PyObject * +PyCSliderCtrl_get_channel_rect(PyObject *self, PyObject *args) +{ + RECT rect; + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->GetChannelRect(&rect); + GUI_END_SAVE; + return Py_BuildValue ("(iiii)", rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod int|PyCSliderCtrl|GetThumbRect|Get the control's thumb rectangle +static PyObject * +PyCSliderCtrl_get_thumb_rect(PyObject *self, PyObject *args) +{ + RECT rect; + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->GetThumbRect(&rect); + GUI_END_SAVE; + return Py_BuildValue ("(iiii)", rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod int|PyCSliderCtrl|GetPos|Get the control's position +static PyObject * +PyCSliderCtrl_get_pos(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetPos(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|SetPos|Set the control's position +static PyObject * +PyCSliderCtrl_set_pos(PyObject *self, PyObject *args) +{ + int nPos; + if (!PyArg_ParseTuple(args, "i", + &nPos))// @pyparm int|nPos|1|New position of the Slider bar control. + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetPos(nPos); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|GetNumTics|Get number of tics in the slider +static PyObject * +PyCSliderCtrl_get_num_tics(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + unsigned int rc = pSC->GetNumTics(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetTicArray|Get a tuple of slider tic positions +static PyObject * +PyCSliderCtrl_get_tic_array(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int n = pSC->GetNumTics(); + DWORD* array = pSC->GetTicArray(); + GUI_END_SAVE; + PyObject* rc = PyTuple_New(n); + for (int i = 0; i < n; i++) { + PyTuple_SetItem(rc, i, Py_BuildValue ("i", array[i])); + } + return rc; +} + +// @pymethod int|PyCSliderCtrl|GetTic|Get the position of the specified tic number +static PyObject * +PyCSliderCtrl_get_tic(PyObject *self, PyObject *args) +{ + int nTic; + if (!PyArg_ParseTuple(args, "i", + &nTic))// @pyparm int|nTic|1|Zero based index of the tic mark + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetTic(nTic); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|GetTicPos|Get the position of the specified tic number in client coordinates +static PyObject * +PyCSliderCtrl_get_tic_pos(PyObject *self, PyObject *args) +{ + int nTic; + if (!PyArg_ParseTuple(args, "i", + &nTic))// @pyparm int|nTic|1|Zero based index of the tic mark + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->GetTicPos(nTic); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|SetTic|Set a tic at the specified position +static PyObject * +PyCSliderCtrl_set_tic(PyObject *self, PyObject *args) +{ + int nTic; + if (!PyArg_ParseTuple(args, "i", + &nTic))// @pyparm int|nTic|1|Position of the desired tic mark + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + int rc = pSC->SetTic(nTic); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCSliderCtrl|SetTicFreq|Set the tic frequency +static PyObject * +PyCSliderCtrl_set_tic_freq(PyObject *self, PyObject *args) +{ + int nFreq; + if (!PyArg_ParseTuple(args, "i", + &nFreq))// @pyparm int|nFreq|1|Frequency of tic marks + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->SetTicFreq(nFreq); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|ClearSel|Clear the selection +static PyObject * +PyCSliderCtrl_clear_sel(PyObject *self, PyObject *args) +{ + BOOL bRedraw; + if (!PyArg_ParseTuple(args, "i", + &bRedraw))// @pyparm int|bRedraw|1|Redraw the control? + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->ClearSel(bRedraw); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|VerifyPos|Verify the position is between configured min and max +static PyObject * +PyCSliderCtrl_verify_pos(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->VerifyPos(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSliderCtrl|ClearTics|Clear the control's tic marks +static PyObject * +PyCSliderCtrl_clear_tics(PyObject *self, PyObject *args) +{ + BOOL bRedraw; + if (!PyArg_ParseTuple(args, "i", + &bRedraw))// @pyparm int|bRedraw|1|Redraw the control? + return NULL; + CSliderCtrl *pSC = GetSliderCtrl(self); + if (!pSC) + return NULL; + GUI_BGN_SAVE; + pSC->ClearTics(bRedraw); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCSliderCtrl|A windows Slider bar control. Encapsulates an MFC class. Derived from . +static struct PyMethodDef PyCSliderCtrl_methods[] = { + {"CreateWindow", PyCSliderCtrl_create_window,1}, // @pymeth CreateWindow|Creates the window for a new Slider bar object. + {"GetLineSize", PyCSliderCtrl_get_line_size, 1}, // @pymeth GetLineSize|Get the control's line size + {"SetLineSize", PyCSliderCtrl_set_line_size, 1}, // @pymeth SetLineSize|Set the control's line size + {"GetPageSize", PyCSliderCtrl_get_page_size, 1}, // @pymeth GetPageSize|Get the control's Page size + {"SetPageSize", PyCSliderCtrl_set_page_size, 1}, // @pymeth SetPageSize|Set the control's Page size + {"GetRangeMax", PyCSliderCtrl_get_range_max, 1}, // @pymeth GetRangeMax|Get the control's maximum + {"GetRangeMin", PyCSliderCtrl_get_range_min, 1}, // @pymeth GetRangeMin|Get the control's minimum + {"GetRange", PyCSliderCtrl_get_range, 1}, // @pymeth GetRange|Get the control's minimum and maximum + {"SetRangeMax", PyCSliderCtrl_set_range_max, 1}, // @pymeth GetRangeMax|Set the control's maximum + {"SetRangeMin", PyCSliderCtrl_set_range_min, 1}, // @pymeth GetRangeMin|Set the control's minimum + {"SetRange", PyCSliderCtrl_set_range, 1}, // @pymeth SetRange|Set the control's minimum and maximum + {"GetSelection", PyCSliderCtrl_get_selection, 1}, // @pymeth GetSelection|Get the selection start and end positions + {"SetSelection", PyCSliderCtrl_set_selection, 1}, // @pymeth SetSelection|Set the selection start and end positions + {"GetChannelRect", PyCSliderCtrl_get_channel_rect, 1}, // @pymeth GetChannelRect|Get the control's channel rect + {"GetThumbRect", PyCSliderCtrl_get_thumb_rect, 1}, // @pymeth GetThumbRect|Get the control's thumb rect + {"GetPos", PyCSliderCtrl_get_pos, 1}, // @pymeth GetPos|Get the control's position + {"SetPos", PyCSliderCtrl_set_pos, 1}, // @pymeth SetPos|Set the control's position + {"GetNumTics", PyCSliderCtrl_get_num_tics, 1}, // @pymeth GetNumTics|Get the number of tics in the control + {"GetTicArray", PyCSliderCtrl_get_tic_array, 1}, // @pymeth GetTicArray|Get the array of tic positions + {"GetTic", PyCSliderCtrl_get_tic, 1}, // @pymeth GetTic|Get the position of the specified tic + {"GetTicPos", PyCSliderCtrl_get_tic_pos, 1}, // @pymeth GetTicPos|Get the position of the specified tic in client coordinates + {"SetTic", PyCSliderCtrl_set_tic, 1}, // @pymeth SetTic|Set a tick at the position + {"SetTicFreq", PyCSliderCtrl_set_tic_freq, 1}, // @pymeth SetTicFreq|Set the tic mark frequency + {"ClearSel", PyCSliderCtrl_clear_sel, 1}, // @pymeth ClearSel|Clear any control selection + {"VerifyPos", PyCSliderCtrl_verify_pos, 1}, // @pymeth VerifyPos|Verify the positon between min and max + {"ClearTics", PyCSliderCtrl_clear_tics, 1}, // @pymeth ClearTics|Clear any tic marks from the control + {NULL, NULL} +}; + +ui_type_CObject PyCSliderCtrl::type("PyCSliderCtrl", + &ui_control_object::type, + RUNTIME_CLASS(CSliderCtrl), + sizeof(PyCSliderCtrl), + PyCSliderCtrl_methods, + GET_PY_CTOR(PyCSliderCtrl)); + + +///////////////////////////////////////////////////////////////////// +// +// PyCStatusBarCtrl +// +static CStatusBarCtrl *GetStatusBarCtrl(PyObject *self) +{ + // note we can only ask for a CWnd, if the PC is created from a resource based + // dialog. This is also the technique MFC uses (specifically appdlg.cpp) + return (CStatusBarCtrl *)PyCWnd::GetPythonGenericWnd(self); +} +PyCStatusBarCtrl::PyCStatusBarCtrl() +{ +} +PyCStatusBarCtrl::~PyCStatusBarCtrl() +{ +} + +// @pymethod |win32ui|CreateStatusBarCtrl|Creates a progress control object. creates the actual control. +PyObject * +PyCStatusBarCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CStatusBarCtrl *pPC = new CStatusBarCtrl(); + return ui_assoc_object::make( PyCStatusBarCtrl::type, pPC ); +} + +// @pymethod |PyCStatusBarCtrl|CreateWindow|Creates the actual control. +PyObject * +PyCStatusBarCtrl_create_window(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The style for the control. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the control. + &obParent, // @pyparm |parent||The parent window of the control. Usually a . + &id )) // @pyparm int|id||The control's ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CStatusBarCtrl *pPC = GetStatusBarCtrl(self); + if (!pPC) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pPC->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CStatusBarCtrl::Create"); + RETURN_NONE; +} + +// @pymethod (width, height, spacing)|PyCStatusBarCtrl|GetBorders|Retrieve the status bar control's current widths of the horizontal and vertical borders and of the space between rectangles. + +static PyObject * +PyCStatusBarCtrl_get_borders(PyObject *self, PyObject *args) +{ + int nHorz, nVert, nSpacing; + + CHECK_NO_ARGS(args); + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + if (!pSB) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pSB->GetBorders (nHorz, nVert, nSpacing); + GUI_END_SAVE; + + if (!ok) + RETURN_ERR ("CStatusBar::GetBorders"); + + return Py_BuildValue("(iii)", nHorz, nVert, nSpacing); +} + +// @pymethod (int)|PyCStatusBarCtrl|GetParts|Retrieve coordinates of the parts in a status bar control. + +static PyObject * +PyCStatusBarCtrl_get_parts(PyObject *self, PyObject *args) +{ + int i; + int nParts, nRequested = -1; + + // @comm This function, as designed in MFC, returns both the *number* of parts, and, + // through an OUT parameter, an array of ints giving the coordinates of the + // parts. There is also an IN parameter saying how many coordinates to give + // back. Here, we're explicitly changing the semantics a bit. + // + // GetParts() -> Tuple of all coordinates + // GetParts(n) -> Tuple of the first n coordinates (or all coordinates, if + // fewer than n) + // + // So, in Python, you can't simultaneously find out how many coordinates there + // are, and retrieve a subset of them. In a reasonable universe, there would + // have been GetParts() -> int, and GetCoords() -> List. This means that I + // need to call the MFC method twice; once to find out how many there are, and + // another time to get them. + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + if (!pSB) + return NULL; + + if (!PyArg_ParseTuple(args, + "|i:GetParts", // @pyparm int|nParts||The number of coordinates to retrieve + &nRequested)) + return NULL; + + if (nRequested == 0) + RETURN_NONE; + + GUI_BGN_SAVE; + nParts = pSB->GetParts (0, NULL); + + if ((nRequested == -1) || (nRequested > nParts)) + nRequested = nParts; + + int *pParts = new int[nParts]; + + (void) pSB->GetParts (nRequested, pParts); + GUI_END_SAVE; + + PyObject *parts = PyTuple_New(nParts); + for (i = 0; i < nParts; i++) { + PyTuple_SetItem(parts, i, Py_BuildValue ("i", parts[i])); + } + + delete pParts; + return parts; +} + +// @pymethod (left, top, right, bottom)|PyCStatusBarCtrl|GetRect|Retrieves the bounding rectangle of a part in a status bar control. + +static PyObject * +PyCStatusBarCtrl_get_rect(PyObject *self, PyObject *args) +{ + int nPane; + RECT rect; + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + if (!PyArg_ParseTuple(args, + "i:GetRect", // @pyparm int|nPane||Zero-based index of the part whose bounding rectangle is to be retrieved. + &nPane)) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pSB->GetRect (nPane, &rect); + GUI_END_SAVE; + + if (!ok) + RETURN_ERR ("CStatusBarCtrl::GetRect"); + + return Py_BuildValue ("(iiii)", rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod text|PyCStatusBarCtrl|GetText|Retrieve the text from the given part of a status bar control. + +PyObject * +PyCStatusBarCtrl_get_text (PyObject *self, PyObject *args) +{ + int attr; + int nPane; + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int|nPane||Zero-based index of the part whose text is to be retrieved. + + if (!PyArg_ParseTuple(args, + "i:GetText", + &nPane)) + return NULL; + + int len; + GUI_BGN_SAVE; + len = pSB->GetTextLength (nPane, &attr); + if (!len) { + RETURN_ERR ("CStatusBarCtrl::GetTextLength"); + } + + char *buf = new char[len]; + pSB->GetText (buf, nPane, &attr); + GUI_END_SAVE; + + return Py_BuildValue ("s", buf); +} + +// @pymethod int|PyCStatusBarCtrl|GetTextAttr|Retrieve the attributes of the text in the given part of a status bar control. + +static PyObject * +PyCStatusBarCtrl_get_text_attr(PyObject *self, PyObject *args) +{ + int attr; + int nPane; + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int|nPane||Zero-based index of the part whose text is to be retrieved. + + if (!PyArg_ParseTuple(args, + "i:GetTextAttr", + &nPane)) + return NULL; + + GUI_BGN_SAVE; + (void) pSB->GetTextLength (nPane, &attr); + GUI_END_SAVE; + + return Py_BuildValue ("i", attr); +} + +// @pymethod int|PyCStatusBarCtrl|GetTextLength|Retrieve the length the text in the given part of a status bar control. + +static PyObject * +PyCStatusBarCtrl_get_text_length(PyObject *self, PyObject *args) +{ + int attr; + int nPane; + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int|nPane||Zero-based index of the part whose text is to be retrieved. + + if (!PyArg_ParseTuple(args, + "i:GetTextLength", + &nPane)) + return NULL; + + int len; + GUI_BGN_SAVE; + len = pSB->GetTextLength (nPane, &attr); + GUI_END_SAVE; + + return Py_BuildValue ("i", len); +} + +// @pymethod |PyCStatusBarCtrl|SetMinHeight|Set the minimum height of a status bar control's drawing area. + +static PyObject * +PyCStatusBarCtrl_set_min_height(PyObject *self, PyObject *args) +{ + int nHeight; + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int|nHeight||Minimum height + + if (!PyArg_ParseTuple(args, + "i:SetMinHeight", + &nHeight)) + return NULL; + + GUI_BGN_SAVE; + pSB->SetMinHeight (nHeight); + GUI_END_SAVE; + + RETURN_NONE; +} + +// @pymethod |PyCStatusBarCtrl|SetParts|Sets the number of parts in a status bar control and the coordinate of the right edge of each part. + +static PyObject * +PyCStatusBarCtrl_set_parts (PyObject *self, PyObject *args) +{ + int nParts = 0; + int i; + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int...|coord||Coordinates of each part + + PyObject *obPart; + + nParts = PyTuple_Size (args); + int *pParts = new int[nParts]; + for (i = 0; i < nParts; i++) { + obPart = PyTuple_GetItem (args, i); + if (PyArg_Parse (obPart, "i", &pParts[i])) { + delete pParts; + RETURN_TYPE_ERR ("arguments must be integers"); + } + } + + GUI_BGN_SAVE; + pSB->SetParts (nParts, pParts); + GUI_END_SAVE; + + delete pParts; + + RETURN_NONE; +} + +// @pymethod |PyCStatusBarCtrl|SetSimple|Specify whether a status bar control displays simple text or displays all control parts set by a previous call to SetParts. + +static PyObject * +PyCStatusBarCtrl_set_simple (PyObject *self, PyObject *args) +{ + int bSimple; + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm int|bSimple||If non-zero, displays simple text. + + if (!PyArg_ParseTuple(args, + "i:SetSimple", + &bSimple)) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pSB->SetSimple (bSimple); + GUI_END_SAVE; + + if (!ok) { + RETURN_ERR ("CStatusBarCtrl::SetSimple"); + } + + RETURN_NONE; +} + +// @pymethod |PyCStatusBarCtrl|SetText|Set the text in the given part of a status bar control. + +PyObject * +PyCStatusBarCtrl_set_text(PyObject *self, PyObject *args) +{ + char *buf; + int nPane, nType; + + CStatusBarCtrl *pSB = GetStatusBarCtrl(self); + + if (!pSB) + return NULL; + + // @pyparm string|text||The text to display + // @pyparm int|nPane||Zero-based index of the part to set. + // @pyparm int|nType||Type of drawing operation. + // @comm The drawing type can be set to one of:~ + // * 0 - The text is drawn with a border to appear lower than + // the plane of the status bar.~ + // * win32con.SBT_NOBORDERS - The text is drawn without borders.~ + // * win32con.SBT_OWNERDRAW - The text is drawn by the parent window.~ + // * win32con.SBT_POPOUT - The text is drawn with a border to appear + // higher than the plane of the status bar. + + if (!PyArg_ParseTuple(args, + "sii:SetText", &buf, &nPane, &nType)) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pSB->SetText (buf, nPane, nType); + GUI_END_SAVE; + + if (!ok) { + RETURN_ERR ("CStatusBarCtrl::SetText"); + } + + RETURN_NONE; +} + +// @object PyCStatusBarCtrl|A windows progress bar control. Encapsulates an MFC class. Derived from . +static struct PyMethodDef PyCStatusBarCtrl_methods[] = { + {"CreateWindow", PyCStatusBarCtrl_create_window,1}, // @pymeth CreateWindow|Creates the window for a new progress bar object. + {"GetBorders", PyCStatusBarCtrl_get_borders, 1}, // @pymeth GetBorders|Retrieve the status bar control's current widths of the horizontal and vertical borders and of the space between rectangles. + {"GetParts", PyCStatusBarCtrl_get_borders, 1}, // @pymeth GetParts|Retrieve coordinates of the parts in a status bar control. + {"GetRect", PyCStatusBarCtrl_get_rect, 1}, // @pymeth GetRect|Retrieves the bounding rectangle of a part in a status bar control. + {"GetText", PyCStatusBarCtrl_get_text, 1}, // @pymeth GetText|Retrieves the text of a part in a status bar control. + {"GetTextAttr", PyCStatusBarCtrl_get_text_attr, 1}, // @pymeth GetTextAttr|Retrieves the text attributes of a part in a status bar control. + {"GetTextLength", PyCStatusBarCtrl_get_text_length, 1}, // @pymeth GetTextLength|Retrieves the length of the text in a part in a status bar control. + {"SetMinHeight", PyCStatusBarCtrl_set_min_height, 1}, // @pymeth SetMinHeight|Set the minimum height of a status bar control's drawing area. + {"SetParts", PyCStatusBarCtrl_set_parts, 1}, // @pymeth SetParts|Sets the number of parts in a status bar control and the coordinate of the right edge of each part. + {"SetText", PyCStatusBarCtrl_set_text, 1}, // @pymeth SetText|Set the text in the given part of a status bar control. + {NULL, NULL} +}; + +ui_type_CObject PyCStatusBarCtrl::type("PyCStatusBarCtrl", + &ui_control_object::type, + RUNTIME_CLASS(CStatusBarCtrl), + sizeof(PyCStatusBarCtrl), + PyCStatusBarCtrl_methods, + GET_PY_CTOR(PyCStatusBarCtrl)); + +// A spin control + +static CSpinButtonCtrl *GetSpinButtonCtrl(PyObject *self) +{ + return (CSpinButtonCtrl *)PyCWnd::GetPythonGenericWnd(self); +} + +// @pymethod int|PyCSpinButtonCtrl|GetPos|Obtains the current position for a spin button control. +PyObject * +PyCSpinButtonCtrl_GetPos(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "GetPos"); + CSpinButtonCtrl *pSB = GetSpinButtonCtrl(self); + + if (!pSB) + return NULL; + return PyInt_FromLong( pSB->GetPos() ); +} + +// @pymethod int|PyCSpinButtonCtrl|SetPos|Sets the current position for a spin button control. +PyObject * +PyCSpinButtonCtrl_SetPos(PyObject *self, PyObject *args) +{ + int pos; + // @pyparm int|pos||The new position. + if (!PyArg_ParseTuple(args, "i", &pos)) + return NULL; + CSpinButtonCtrl *pSB = GetSpinButtonCtrl(self); + + if (!pSB) + return NULL; + GUI_BGN_SAVE; + int oldPos = pSB->SetPos(pos); + GUI_END_SAVE; + // @rdesc The result is the previous position. + return PyInt_FromLong( oldPos ); +} + +// @pymethod int|PyCSpinButtonCtrl|SetRange|Sets the upper and lower limits (range) for a spin button control. +PyObject * +PyCSpinButtonCtrl_SetRange(PyObject *self, PyObject *args) +{ + int min, max; + if (!PyArg_ParseTuple(args, "ii", &min, &max)) + return NULL; + CSpinButtonCtrl *pSB = GetSpinButtonCtrl(self); + + if (!pSB) + return NULL; + GUI_BGN_SAVE; + pSB->SetRange(min, max); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCSpinButtonCtrl|SetRange32|Sets the 32 bit upper and lower limits (range) for a spin button control. +PyObject * +PyCSpinButtonCtrl_SetRange32(PyObject *self, PyObject *args) +{ + int min, max; + if (!PyArg_ParseTuple(args, "ii", &min, &max)) + return NULL; + CSpinButtonCtrl *pSB = GetSpinButtonCtrl(self); + + if (!pSB) + return NULL; + GUI_BGN_SAVE; + pSB->SetRange(min, max); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCSpinButtonCtrl|A windows spin button control. Encapsulates an MFC CSpinButtonCtrl object. +static struct PyMethodDef PyCSpinButtonCtrl_methods[] = { + { "GetPos", PyCSpinButtonCtrl_GetPos, 1 }, // @pymeth GetPos|Obtains the current position for a spin button control. + { "SetPos", PyCSpinButtonCtrl_SetPos, 1}, // @pymeth SetPos|Sets the current position for a spin button control. + { "SetRange", PyCSpinButtonCtrl_SetRange, 1}, // @pymeth SetRange|Sets the upper and lower limits (range) for a spin button control. + { "SetRange32", PyCSpinButtonCtrl_SetRange32, 1}, // @pymeth SetRange32|Sets the upper and lower limits (range) for a spin button control. + { NULL } +}; + +ui_type_CObject PyCSpinButtonCtrl::type("PyCSpinButtonCtrl", + &ui_control_object::type, + RUNTIME_CLASS(CSpinButtonCtrl), + sizeof(PyCSpinButtonCtrl), + PyCSpinButtonCtrl_methods, + GET_PY_CTOR(PyCSpinButtonCtrl)); diff --git a/Pythonwin/win32control.h b/Pythonwin/win32control.h new file mode 100644 index 0000000000..cfe2287a30 --- /dev/null +++ b/Pythonwin/win32control.h @@ -0,0 +1,194 @@ +// win32control.h : header file +// +// +///////////////////////////////////////////////////////// +// +// A derived type object. for the CCtrlView based objects. +// +class PYW_EXPORT PyCCtrlView_Type : public ui_type_CObject { +public: + PyCCtrlView_Type( const char *name, ui_type *pBaseType, ui_type_CObject *pControlType, CRuntimeClass *rtClass, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ); +public: + ui_type_CObject *control; +}; + +//////////////////////////////////////////////////////////////////////// +// View Classes +// +inline PyCCtrlView_Type::PyCCtrlView_Type( const char *name, ui_type *pBaseType, ui_type_CObject *pControlType, CRuntimeClass *pRT, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ) : + ui_type_CObject(name, pBaseType, pRT, typeSize, methodList, thector ) +{ + control = pControlType; +} + +class PYW_EXPORT PyCCtrlView : public PyCView { +public: + MAKE_PY_CTOR(PyCCtrlView) + static PyCCtrlView_Type type; + PyObject * getattr(char *name); + static PyObject *create(PyObject *self, PyObject *args); +protected: + PyCCtrlView() {return;} +}; + +class PYW_EXPORT PyCEditView : public PyCCtrlView { +public: + MAKE_PY_CTOR(PyCEditView) + static PyCCtrlView_Type type; + static PyObject *create(PyObject *self, PyObject *args); +protected: + PyCEditView() {return;} +}; + +class PYW_EXPORT PyCListView : public PyCCtrlView { +public: + MAKE_PY_CTOR(PyCListView) + static PyCCtrlView_Type type; + static PyObject *create(PyObject *self, PyObject *args); +protected: + PyCListView() {return;} +}; + +class PYW_EXPORT PyCTreeView : public PyCCtrlView { +public: + MAKE_PY_CTOR(PyCTreeView) + static PyCCtrlView_Type type; + static PyObject *create(PyObject *self, PyObject *args); +protected: + PyCTreeView() {return;} +}; + +/////////////////////////////////////////////////////////////////////// +// Control objects. +// +// ui_control_object +// +class ui_control_object : public PyCWnd { +public: + static ui_type_CObject type; +protected: + ui_control_object(); + virtual ~ui_control_object(); +}; + +///////////////////////////////////////////////////////// +// +// PyCButton +class PyCButton : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCButton) +protected: + PyCButton(); + virtual ~PyCButton(); +}; + + +///////////////////////////////////////////////////////// +// +// PyCRichEditCtrl +class PyCRichEditCtrl : public ui_control_object { +public: + static PyCCtrlView_Type type; + MAKE_PY_CTOR(PyCRichEditCtrl) +protected: + PyCRichEditCtrl(); + virtual ~PyCRichEditCtrl(); +}; + + +///////////////////////////////////////////////////////// +// +// PyCListBox +class PyCListBox : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCListBox) +protected: + PyCListBox(); + virtual ~PyCListBox(); +}; + +///////////////////////////////////////////////////////// +// +// PyCComboBox +class PyCComboBox : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCComboBox) +protected: + PyCComboBox(); + virtual ~PyCComboBox(); +}; + +///////////////////////////////////////////////////////// +// +// PyCEdit +class PyCEdit : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCEdit) +protected: + PyCEdit(); + virtual ~PyCEdit(); +}; + +///////////////////////////////////////////////////////// +// +// PyCProgressCtrl +class PyCProgressCtrl : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCProgressCtrl) +protected: + PyCProgressCtrl(); + virtual ~PyCProgressCtrl(); +}; + +///////////////////////////////////////////////////////// +// +// PyCStatusBarCtrl +class PyCStatusBarCtrl : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCStatusBarCtrl) +protected: + PyCStatusBarCtrl(); + virtual ~PyCStatusBarCtrl(); +}; + +///////////////////////////////////////////////////////// +// +// PyCSliderCtrl +class PyCSliderCtrl : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCSliderCtrl) +protected: + PyCSliderCtrl(); + virtual ~PyCSliderCtrl(); +}; + +///////////////////////////////////////////////////////// +// +// PyCSpinButtonCtrl +class PyCSpinButtonCtrl : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCSpinButtonCtrl) +protected: +}; + +///////////////////////////////////////////////////////// +// +// PyCToolTipCtrl +class PyCToolTipCtrl : public ui_control_object { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCToolTipCtrl) +protected: + PyCToolTipCtrl(); + virtual ~PyCToolTipCtrl(); +}; + +///////////////////////////////////////////////////////////////////////////// diff --git a/Pythonwin/win32ctledit.cpp b/Pythonwin/win32ctledit.cpp new file mode 100644 index 0000000000..65be713f28 --- /dev/null +++ b/Pythonwin/win32ctledit.cpp @@ -0,0 +1,423 @@ +/* win32ctledit : implementation file + + Edit control object. Note that these methods are shared by + the edit view object + + Created July 1995, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32control.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +extern CPythonEditView *GetEditViewPtr(PyObject *self); + +PyCEdit::PyCEdit() +{ +} +PyCEdit::~PyCEdit() +{ +} + +// @pymethod |win32ui|CreateEdit|Creates an Edit object. creates the actual control. +PyObject * +PyCEdit_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pBut = new CEdit(); + return ui_assoc_object::make( PyCEdit::type, pBut ); +} + +CEdit *GetEditCtrl(PyObject *self) +{ + if (ui_base_class::is_uiobject(self, &PyCEditView::type)) { + CPythonEditView *pView = GetEditViewPtr(self); + if (pView) + return &(pView->GetEditCtrl()); + else + return NULL; + } + if (ui_base_class::is_uiobject(self, &PyCEdit::type)) + return (CEdit *)PyCWnd::GetPythonGenericWnd(self); + RETURN_ERR("Python object can not be used as an edit control."); +} + +// @pymethod |PyCEdit|CreateWindow|Creates the window for a new Edit object. +static PyObject * +PyCEdit_create_window(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The style for the Edit. Use any of the win32con.BS_* constants. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the Edit. + &obParent, // @pyparm |parent||The parent window of the Edit. Usually a . + &id )) // @pyparm int|id||The Edits control ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pEdit->Create(style, rect, pParent, id ); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CEdit::Create"); + RETURN_NONE; +} + +///////////////////////////////////////////////////////////////////// +// +// Edit Control object +// +////////////////////////////////////////////////////////////////////// +// @pymethod int|PyCEdit|FmtLines|Sets the formatting options for the control. +static PyObject *PyCEdit_fmt_lines(PyObject *self, PyObject *args) +{ + // @ comm Sets the inclusion of soft line-break characters on or off within a multiple-line edit control. + // A soft line break consists of two carriage returns and a linefeed inserted at the end of a line that is + // broken because of word wrapping. + // A hard line break consists of one carriage return and a linefeed. + // Lines that end with a hard line break are not affected by FmtLines. ~ + // This function is inly effective on multi-line edit controls. + CEdit *pEdit = GetEditCtrl(self); + BOOL format; // @pyparm int|bAddEOL||Specifies whether soft line-break characters are to be inserted. + // A value of TRUE inserts the characters; a value of FALSE removes them. + if (!pEdit || !PyArg_ParseTuple(args, "i:FmtLines", &format)) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pEdit->FmtLines( format ); // @pyseemfc CEdit|FmtLines + GUI_END_SAVE; + return Py_BuildValue("i", rc ); + // @rdesc Nonzero if any formatting occurs; otherwise 0. +} +// @pymethod (start, end)|PyCEdit|GetSel|Returns the start and end of the current selection. +static PyObject *PyCEdit_get_sel(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + int start,end; + GUI_BGN_SAVE; + pEdit->GetSel(start,end); // @pyseemfc CEdit|GetSel + GUI_END_SAVE; + return Py_BuildValue("(ii)",start,end); + // @rdesc The return tuple is (the first character in the current selection, first nonselected character past the end of the current selection) +} +// @pymethod |PyCEdit|SetSel|Sets the selection in the edit control. +static PyObject *PyCEdit_set_sel(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + int start=0,end=0; + BOOL bNoScroll = FALSE; + if (!pEdit) + return NULL; + if (!PyArg_ParseTuple(args, "i|ii:SetSel", + &start, // @pyparm int|start||Specifies the starting position. + // If start is 0 and end is -1, all the text in the edit control is selected. + // If start is -1, any current selection is removed. + &end, // @pyparm int|end|start|Specifies the ending position. + &bNoScroll)) { // @pyparm int|bNoScroll|0|Indicates whether the caret should be scrolled into view. If 0, the caret is scrolled into view. If 1, the caret is not scrolled into view. + PyErr_Clear(); + bNoScroll = FALSE; + if (!PyArg_ParseTuple(args, "(ii)|i:SetSel", + &start, // @pyparmalt2 (int, int)|start,end)||As for normal start, end args. + &end, + &bNoScroll)) // @pyparmalt2 int|bNoScroll|0|Indicates whether the caret should be scrolled into view. If 0, the caret is scrolled into view. If 1, the caret is not scrolled into view. + return NULL; + } + if (start!=end && end==0) + end=start; + GUI_BGN_SAVE; + pEdit->SetSel(start,end,bNoScroll); // @pyseemfc CEdit|SetSel + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCEdit|Cut|Cuts the current selection to the clipboard. +static PyObject *PyCEdit_cut(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Cut(); // @pyseemfc CEdit|Cut + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCEdit|Copy|Copys the current selection to the clipboard. +static PyObject *PyCEdit_copy(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Copy(); // @pyseemfc CEdit|Copy + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCEdit|Paste|Pastes the contents of the clipboard into the control. +static PyObject *PyCEdit_paste(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Paste(); // @pyseemfc CEdit|Paste + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCEdit|ReplaceSel|Replaces the selection with the specified text. +static PyObject *PyCEdit_replace_sel(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + char *msg; + // @pyparm string|text||The text to replace the selection with. + if (!pEdit || !PyArg_ParseTuple(args, "s:ReplaceSel", &msg)) + return NULL; + GUI_BGN_SAVE; + pEdit->ReplaceSel(msg); // @pyseemfc CEdit|ReplaceSel + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCEdit|SetReadOnly|Sets or clears the read-only status of the listbox. +static PyObject * +PyCEdit_set_readonly(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + BOOL bState = TRUE; + // @pyparm int|bReadOnly|1|The read-only state to set. + if (!PyArg_ParseTuple(args, "|i:SetReadOnly", &bState)) + return NULL; + GUI_BGN_SAVE; + pEdit->SetReadOnly(bState); // @pyseemfc CEdit|SetReadOnly + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCEdit|GetLineCount|Gets the number of lines in an edit control. +static PyObject * +PyCEdit_get_line_count(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit ) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->GetLineCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CEdit|GetLineCount + // @rdesc The number of lines in the buffer. If the control is empty, the return value is 1. +} +// @pymethod int|PyCEdit|GetFirstVisibleLine|Returns zero-based index of the topmost visible line. +static PyObject * +PyCEdit_get_first_visible(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->GetFirstVisibleLine(); + GUI_END_SAVE; + + return Py_BuildValue("i",rc); // @pyseemfc CEdit|GetFirstVisibleLine + // @rdesc The zero-based index of the topmost visible line. For single-line edit controls, the return value is 0. +} +// @pymethod |PyCEdit|LimitText|Sets max length of text that user can enter +static PyObject *PyCEdit_limit_text(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + int nChars = 0; + // @pyparm int|nChars|0|Specifies the length (in bytes) of the text that the user can enter. If this parameter is 0, the text length is set to + // UINT_MAX bytes. This is the default behavior. + if (!PyArg_ParseTuple(args, "|i:LimitText", &nChars)) + return NULL; + GUI_BGN_SAVE; + pEdit->LimitText(nChars); // @pyseemfc CEdit|LimitText + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCEdit|LineIndex|Retrieves the character index of a line within a multiple-line edit control. +static PyObject * +PyCEdit_line_index(PyObject *self, PyObject *args) +{ + // @comm This method only works on multi-linr edit controls. + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|lineNo|-1|Contains the index value for the desired line in the text + // of the edit control, or contains -1. If -1, then it specifies the current line. + int lineNo = -1; + if (!PyArg_ParseTuple(args, "|i:LineIndex", &lineNo)) + return NULL; + GUI_BGN_SAVE; + long rc = pEdit->LineIndex(lineNo); + GUI_END_SAVE; + return Py_BuildValue("i",rc); // @pyseemfc CEdit|LineIndex + // @rdesc The character index of the line specified in lineNo, or -1 if + // the specified line number is greater then the number of lines in + // the edit control. +} +// @pymethod int|PyCEdit|LineScroll|Scroll the control vertically and horizontally +static PyObject * +PyCEdit_line_scroll(PyObject *self, PyObject *args) +{ + // @comm This method only works on multi-linr edit controls. + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|nLines||Specifies the number of lines to scroll vertically. + // @pyparm int|nChars|0|Specifies the number of character positions to scroll horizontally. This value is ignored if the edit control has either the + // ES_RIGHT or ES_CENTER style. + int nLines, nChars = 0; + if (!PyArg_ParseTuple(args, "i|i:LineScroll", &nLines, &nChars)) + return NULL; + GUI_BGN_SAVE; + pEdit->LineScroll(nLines, nChars); // @pyseemfc CEdit|LineScroll + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCEdit|LineFromChar|Returns the line number of the specified character. +static PyObject * +PyCEdit_line_from_char(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + int charNo=-1; + // @pyparm int|charNo|-1|Contains the zero-based index value for the desired character in the text of the edit + // control, or -1. If -1, then it specifies the current line. + if (!PyArg_ParseTuple(args, "|i:LineFromChar", &charNo)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->LineFromChar(charNo); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CEdit|LineFromChar + // @rdesc The zero-based line number of the line containing the character index specified by charNo. + // If charNo is -1, the number of the line that contains the first character of the selection is returned. + // If there is no selection, the current line number is returned. +} + +// @pymethod int|PyCEdit|GetLine|Returns the text in a specified line. +static PyObject * +PyCEdit_get_line(PyObject *self, PyObject *args) +{ + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + int lineNo = pEdit->LineFromChar(); + GUI_END_SAVE; + // @pyparm int|lineNo|current|Contains the zero-based index value for the desired line. + // @comm This function is not an MFC wrapper. + if (!PyArg_ParseTuple(args, "|i:GetLine", &lineNo)) + return NULL; + int size = 1024; // ahhhhh-this fails with 128, even when line len==4! + // god damn it - try and write a fairly efficient normal case, + // and handle worst case, and look what happens! + CString csBuffer; // use dynamic mem for buffer + char *buf; + int bytesCopied; + // this TRACE _always_ returns the length of the first line - hence the + // convaluted code below. +// TRACE("LineLength for line %d is %d\n", lineNo, pView->GetEditCtrl().LineLength(lineNo)); + // loop if buffer too small, increasing each time. + while (size<0x7FFF) // reasonable line size max? - maxuint on 16 bit. + { + buf = csBuffer.GetBufferSetLength(size); + if (buf==NULL) + RETURN_ERR("Out of memory getting Edit control line value"); + + GUI_BGN_SAVE; + bytesCopied = pEdit->GetLine(lineNo, buf, size); + GUI_END_SAVE; + if (bytesCopied!=size) // ok - get out. + break; + // buffer too small + size += size; // try doubling! + TRACE0("Doubling buffer for GetLine value\n"); + } + if (bytesCopied==size) // hit max. + --bytesCopied; // so NULL doesnt overshoot. + if (buf[bytesCopied-1]=='\r' || buf[bytesCopied-1]=='\n') // kill newlines. + --bytesCopied; + buf[bytesCopied] = '\0'; + return Py_BuildValue("s",buf); +} + +// @pymethod int|PyCEdit|Clear|Clears all text in an edit control. +static PyObject * +PyCEdit_clear(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CEdit *pEdit = GetEditCtrl(self); + if (!pEdit ) + return NULL; + GUI_BGN_SAVE; + pEdit->Clear(); // @pyseemfc CEdit|Clear + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCEdit|A windows edit control. Encapsulates an MFC class. Derived from a object. +static struct PyMethodDef PyCEdit_methods[] = { + {"CreateWindow", PyCEdit_create_window,1}, // @pymeth CreateWindow|Creates the window for a new edit object. + {"Clear", PyCEdit_clear, 1}, // @pymeth Clear|Clears all text from an edit control. + {"Copy", PyCEdit_copy, 1}, // @pymeth Copy|Copy the selection to the clipboard. + {"Cut", PyCEdit_cut, 1}, // @pymeth Cut|Cut the selection, and place it in the clipboard. + {"FmtLines", PyCEdit_fmt_lines, 1}, // @pymeth FmtLines|Change the formatting options for the edit control + {"GetFirstVisibleLine",PyCEdit_get_first_visible,1}, // @pymeth GetFirstVisibleLine|Returns zero-based index of the topmost visible line. + {"GetSel", PyCEdit_get_sel, 1}, // @pymeth GetSel|Returns the selection. + {"GetLine", PyCEdit_get_line, 1}, // @pymeth GetLine|Returns a specified line. + {"GetLineCount", PyCEdit_get_line_count, 1}, // @pymeth GetLineCount|Returns the number of lines in an edit control. + {"LimitText", PyCEdit_limit_text, 1}, // @pymeth LimitText|Sets max length of text that user can enter + {"LineFromChar", PyCEdit_line_from_char, 1}, // @pymeth LineFromChar|Returns the line number of a given character. + {"LineIndex", PyCEdit_line_index, 1}, // @pymeth LineIndex|Returns the line index + {"LineScroll", PyCEdit_line_scroll, 1}, // @pymeth LineScroll|Scroll the control vertically and horizontally + {"Paste", PyCEdit_paste, 1}, // @pymeth Paste|Pastes the contents of the clipboard into the edit control. + {"ReplaceSel", PyCEdit_replace_sel, 1}, // @pymeth ReplaceSel|Replace the selection with the specified text. + {"SetReadOnly", PyCEdit_set_readonly, 1}, // @pymeth SetReadOnly|Set the read only status of an edit control. + {"SetSel", PyCEdit_set_sel, 1}, // @pymeth SetSel|Changes the selection in an edit control. + {NULL, NULL} // sentinel +}; + +ui_type_CObject PyCEdit::type("PyCEdit", + &ui_control_object::type, + RUNTIME_CLASS(CEdit), + sizeof(PyCEdit), + PyCEdit_methods, + GET_PY_CTOR(PyCEdit)); diff --git a/Pythonwin/win32ctrlList.cpp b/Pythonwin/win32ctrlList.cpp new file mode 100644 index 0000000000..168f966dda --- /dev/null +++ b/Pythonwin/win32ctrlList.cpp @@ -0,0 +1,777 @@ +/* win32ctllist : implementation file + + List control object. + + Created May 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32dc.h" +#include "win32control.h" +#include "win32ctrlList.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +const char *szErrListRequiresWindow = "The list must have a window object for this operation"; + +PyCListCtrl::PyCListCtrl() +{ +} +PyCListCtrl::~PyCListCtrl() +{ +} +CListCtrl *GetListCtrl(PyObject *self, bool bNeedValidHwnd = true) +{ + extern CListView *GetListViewPtr(PyObject *self); + CListCtrl *rc; + + if (ui_base_class::is_uiobject(self, &PyCListView::type)) { + CListView *pView = GetListViewPtr(self); + if (pView) + rc = &(pView->GetListCtrl()); + else + rc = NULL; + } else + rc = (CListCtrl *)PyCWnd::GetPythonGenericWnd(self, &PyCListCtrl::type); + if (rc && bNeedValidHwnd && !::IsWindow(rc->m_hWnd)) + RETURN_ERR((char *)szErrListRequiresWindow); + return rc; +} + +// @pymethod |win32ui|CreateListCtrl|Creates a list control. +PyObject *PyCListCtrl_create(PyObject *self, PyObject *args) +{ + return ui_assoc_object::make( PyCListCtrl::type, new CListCtrl)->GetGoodRet(); +} + +// @pymethod |PyCListCtrl|CreateWindow|Creates the actual window for the object. +static PyObject * +PyCListCtrl_CreateWindow(PyObject *self, PyObject *args) +{ + extern CWnd *GetWndPtrFromParam(PyObject *ob, ui_type_CObject &type); + + CListCtrl *pT = GetListCtrl(self, false); + if (!pT) return NULL; + RECT rect; + PyObject *obParent; + long style; + long id; + if(!PyArg_ParseTuple(args, "l(iiii)Ol:Create", + &style, // @pyparm int|style||The window style + &rect.left, &rect.top, &rect.right,&rect.bottom, // @pyparm int, int, int, int|rect||The default rectangle + &obParent, // @pyparm parent|||The parent window + &id))// @pyparm int|id||The control ID + return NULL; + + CWnd *pParent = NULL; + if (obParent != Py_None) { + pParent = GetWndPtrFromParam(obParent, PyCWnd::type); + if (pParent==NULL) return NULL; + } + + GUI_BGN_SAVE; + // @pyseemfc CListCtrl|Create + BOOL ok = pT->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CListCtrl::Create failed"); + RETURN_NONE; +} + + +#define MAKE_GET_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CListCtrl *pList = GetListCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pList->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +#define MAKE_SETBOOL_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CListCtrl *pList = pList=GetListCtrl(self); \ + if (!pList) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + GUI_BGN_SAVE; \ + BOOL ok = pList->mfcName(val); \ + GUI_END_SAVE; \ + if (!ok) \ + RETURN_ERR(#mfcName "failed"); \ + RETURN_NONE; \ +} +#define MAKE_SETVOID_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CListCtrl *pList = pList=GetListCtrl(self); \ + if (!pList) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + GUI_BGN_SAVE; \ + pList->mfcName(val); \ + GUI_END_SAVE; \ + RETURN_NONE; \ +} + +// @pymethod |PyCListCtrl|DeleteItem|Deletes the specified item. +// @pyparm int|item||The item to delete. +MAKE_SETBOOL_INT_METH(PyCListCtrl_DeleteItem, DeleteItem ) + +// @pymethod int|PyCListCtrl|GetBkColor|Retrieves the background color of the control. +MAKE_GET_INT_METH(PyCListCtrl_GetBkColor, GetBkColor ) + +// @pymethod int|PyCListCtrl|GetTextColor|Retrieves the text color of a list view control. +MAKE_GET_INT_METH(PyCListCtrl_GetTextColor, GetTextColor ) +// @pymethod |PyCListCtrl|SetTextColor|Sets the text color of a list view control. +// @pyparm int|color||The new color. +MAKE_SETBOOL_INT_METH(PyCListCtrl_SetTextColor, SetTextColor ) + +// @pymethod |PyCListCtrl|Update|Forces the control to repaint a specified item. +// @pyparm int|item||The new color. +MAKE_SETBOOL_INT_METH(PyCListCtrl_Update, Update ) + +// @pymethod |PyCListCtrl|Arrange|Aligns items on a grid. +// @pyparm int|code||Specifies the alignment style for the items +MAKE_SETBOOL_INT_METH(PyCListCtrl_Arrange, Arrange ) + +// @pymethod int|PyCListCtrl|GetTextBkColor|Retrieves the text background color of a list view control. +MAKE_GET_INT_METH(PyCListCtrl_GetTextBkColor, GetTextBkColor ) +// @pymethod |PyCListCtrl|SetTextBkColor|Sets the text background color of a list view control. +// @pyparm int|color||The new background color. +MAKE_SETBOOL_INT_METH(PyCListCtrl_SetTextBkColor, SetTextBkColor ) + +// @pymethod int|PyCListCtrl|GetItemCount|Retrieves the number of items in a list view control. +MAKE_GET_INT_METH(PyCListCtrl_GetItemCount, GetItemCount ) +// @pymethod |PyCListCtrl|SetItemCount|Prepares a list view control for adding a large number of items. +// @pyparm int|count||Number of items that the control will ultimately contain. +// @comm By calling this function before adding a large number of items, +// you enable a list view control to reallocate its internal data structures +// only once rather than every time you add an item. +MAKE_SETVOID_INT_METH(PyCListCtrl_SetItemCount, SetItemCount ) + + +// @pymethod |PyCListCtrl|SetBkColor|Sets the background color of the control. +// @pyparm int|color||The new background color. +MAKE_SETBOOL_INT_METH(PyCListCtrl_SetBkColor, SetBkColor) + +// @pymethod int|PyCListCtrl|GetTopIndex|Retrieves the index of the topmost visible item. +MAKE_GET_INT_METH(PyCListCtrl_GetTopIndex, GetTopIndex ) + +// @pymethod int|PyCListCtrl|GetCountPerPage|Calculates the number of items that can fit vertically in a list view control. +MAKE_GET_INT_METH(PyCListCtrl_GetCountPerPage, GetCountPerPage ) + +// @pymethod int|PyCListCtrl|GetSelectedCount|Retrieves the number of selected items in the list view control. +MAKE_GET_INT_METH(PyCListCtrl_GetSelectedCount, GetSelectedCount ) + +// @pymethod |PyCListCtrl|DeleteAllItems|Deletes all items from the list. +PyObject *PyCListCtrl_DeleteAllItems( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + if (!(pList=GetListCtrl(self))) + return NULL; + CHECK_NO_ARGS2(args, "DeleteAllItems"); + GUI_BGN_SAVE; + BOOL ok = pList->DeleteAllItems(); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteAllItems failed"); + RETURN_NONE; +} + +// @pymethod |PyCListCtrl|GetImageList|Retrieves the current image list. +PyObject *PyCListCtrl_GetImageList( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + if (!(pList=GetListCtrl(self))) + return NULL; + int nList; + // @pyparm int|nImageList||Value specifying which image list to retrieve. It can be one of: + // - commctrl.LVSIL_NORMAL Image list with large icons. + // - commctrl.LVSIL_SMALL Image list with small icons. + // - commctrl.LVSIL_STATE Image list with state images. + if (!PyArg_ParseTuple(args, "i:GetImageList", &nList)) + return NULL; + GUI_BGN_SAVE; + CImageList *ret = pList->GetImageList(nList); + GUI_END_SAVE; + if (ret==NULL) + RETURN_ERR("There is no image list available"); + return ui_assoc_object::make( PyCImageList::type, ret)->GetGoodRet(); +} + +// @pymethod int|PyCListCtrl|InsertColumn|Inserts a column into a list control when in report view. +PyObject *PyCListCtrl_InsertColumn( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + int iColNo; + PyObject *obLVCol; + + if (!(pList=GetListCtrl(self))) + return NULL; + + if (!PyArg_ParseTuple(args, "iO:InsertColumn", + &iColNo, // @pyparm int|colNo||The new column number + &obLVCol)) // @pyparm |item||A tuple describing the new column. + return NULL; + LV_COLUMN lvCol; + if (!ParseLV_COLUMNTuple(obLVCol, &lvCol)) + return NULL; + GUI_BGN_SAVE; + int ret = pList->InsertColumn(iColNo, &lvCol); + GUI_END_SAVE; + if (ret==-1) + RETURN_ERR("InsertColumn failed"); + return Py_BuildValue("i",ret); +} + +// @pymethod int|PyCListCtrl|SetColumn|Changes column state in a list control when in report view. +PyObject *PyCListCtrl_SetColumn( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + int iColNo; + PyObject *obLVCol; + + if (!(pList=GetListCtrl(self))) + return NULL; + + if (!PyArg_ParseTuple(args, "iO:InsertColumn", + &iColNo, // @pyparm int|colNo||The to be modified column number + &obLVCol)) // @pyparm |item||A tuple describing the modified column. + return NULL; + LV_COLUMN lvCol; + if (!ParseLV_COLUMNTuple(obLVCol, &lvCol)) + return NULL; + GUI_BGN_SAVE; + int ret = pList->SetColumn(iColNo, &lvCol); + GUI_END_SAVE; + if (ret==-1) + RETURN_ERR("SetColumn failed"); + return Py_BuildValue("i",ret); +} + +// @pymethod int|PyCListCtrl|InsertItem|Inserts an item into the list. +PyObject *PyCListCtrl_InsertItem( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + int ret; + int item; + char *text; + int image; + PyObject *obLVItem; + if (!(pList=GetListCtrl(self))) + return NULL; + + if (PyArg_ParseTuple(args, "isi:InsertItem", + &item, // @pyparmalt1 int|item||The index of the item. + &text, // @pyparmalt1 string|text||The text of the item. + &image)) {// @pyparmalt1 int|image||The index of the image to use. + GUI_BGN_SAVE; + ret = pList->InsertItem(item, text, image ); + GUI_END_SAVE; + + } else { + PyErr_Clear(); + if (PyArg_ParseTuple(args, "is:InsertItem", + &item, // @pyparmalt2 int|item||The index of the item. + &text)) {// @pyparmalt2 string|text||The text of the item. + GUI_BGN_SAVE; + ret = pList->InsertItem(item, text ); + GUI_END_SAVE; + } else { + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O:InsertItem", + &obLVItem)) { // @pyparm |item||A tuple describing the new item. + LV_ITEM lvItem; + if (!ParseLV_ITEMTuple(obLVItem, &lvItem)) + return NULL; + GUI_BGN_SAVE; + ret = pList->InsertItem(&lvItem); + GUI_END_SAVE; + } else { + PyErr_Clear(); + RETURN_ERR("InsertItem requires (item, text, image), (item, text), or (itemObject)"); + } + } + } + if (ret==-1) + RETURN_ERR("InsertItem failed"); + return Py_BuildValue("i",ret); +} + +// @pymethod int|PyCListCtrl|SetItem|Sets some of all of an items attributes. +PyObject *PyCListCtrl_SetItem( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + PyObject *obLVItem; + if (!(pList=GetListCtrl(self))) + return NULL; + if (!PyArg_ParseTuple(args, "O:SetItem", + &obLVItem)) // @pyparm |item||A tuple describing the new item. + return NULL; + LV_ITEM lvItem; + if (!ParseLV_ITEMTuple(obLVItem, &lvItem)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItem(&lvItem); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItem failed"); + RETURN_NONE; +} + +// @pymethod int|PyCListCtrl|SetImageList|Assigns an image list to a list view control. +PyObject *PyCListCtrl_SetImageList( PyObject *self, PyObject *args ) +{ + CListCtrl *pList; + PyObject *obList; + int imageType; + if (!(pList=GetListCtrl(self))) + return NULL; + if (!PyArg_ParseTuple(args, "Oi:SetImageList", + &obList, // @pyparm |imageList||The Image List to use. + &imageType )) // @pyparm int|imageType||Type of image list. It can be one of (COMMCTRL.) LVSIL_NORMAL, LVSIL_SMALL or LVSIL_STATE + return NULL; + CImageList *pImageList = PyCImageList::GetImageList(obList); + if (pImageList==NULL) return NULL; + GUI_BGN_SAVE; + CImageList *pOldList = pList->SetImageList( pImageList, imageType ); + GUI_END_SAVE; + if (pOldList==NULL) + RETURN_NONE; + return ui_assoc_object::make( PyCImageList::type, pOldList )->GetGoodRet(); +} + +// @pymethod |PyCListCtrl|GetColumn|Retrieves the details of a column in the control. +PyObject *PyCListCtrl_GetColumn( PyObject *self, PyObject *args ) +{ + int col; + if (!PyArg_ParseTuple( args, "i:GetColumn", + &col)) // @pyparm int|column||The index of the column whose attributes are to be retrieved. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + char textBuf[256]; + LV_COLUMN lvItem; + lvItem.pszText = textBuf; + lvItem.cchTextMax = sizeof(textBuf); + lvItem.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; + GUI_BGN_SAVE; + BOOL ok = pList->GetColumn( col, &lvItem); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetColumn failed"); + return MakeLV_COLUMNTuple(&lvItem); +} + +// @pymethod int|PyCListCtrl|DeleteColumn|Deletes the specified column from the list control. +PyObject *PyCListCtrl_DeleteColumn( PyObject *self, PyObject *args ) +{ + int col; + if (!PyArg_ParseTuple( args, "i:DeleteColumn", + &col)) // @pyparm int|first||Index of the column to be removed. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->DeleteColumn(col); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteColumn failed"); + RETURN_NONE; +} + +// @pymethod int|PyCListCtrl|SetColumnWidth|Sets the width of the specified column in the list control. +PyObject *PyCListCtrl_SetColumnWidth( PyObject *self, PyObject *args ) +{ + int col, width; + if (!PyArg_ParseTuple( args, "ii:SetColumnWidth", + &col, // @pyparm int|first||Index of the column to be changed. + &width)) // @pyparm int|first||New width of the column. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetColumnWidth(col, width); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetColumnWidth failed"); + RETURN_NONE; +} + +// @pymethod int|PyCListCtrl|GetColumnWidth|Gets the width of the specified column in the list control. +PyObject *PyCListCtrl_GetColumnWidth( PyObject *self, PyObject *args ) +{ + int col; + if (!PyArg_ParseTuple( args, "i:GetColumnWidth", + &col)) // @pyparm int|first||Index of the column whose width is to be retrieved. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + int width = pList->GetColumnWidth(col); + GUI_END_SAVE; + return Py_BuildValue("i", width); +} + +// @pymethod int|PyCListCtrl|GetStringWidth|Gets the necessary column width to fully display this text in a column. +PyObject *PyCListCtrl_GetStringWidth( PyObject *self, PyObject *args ) +{ + char *text; + if (!PyArg_ParseTuple( args, "s:GetStringWidth", + &text)) // @pyparm int|first||String that contains the text whose width is to be determined. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + int width = pList->GetStringWidth(text); + GUI_END_SAVE; + return Py_BuildValue("i", width); + // @comm Doesn't take the size of an included Image in account, only the size of the text is determined. +} + +// @pymethod |PyCListCtrl|GetItem|Retrieves the details of an items attributes. +PyObject *PyCListCtrl_GetItem( PyObject *self, PyObject *args ) +{ + int item, sub = 0; + if (!PyArg_ParseTuple( args, "i|i:GetItem", + &item, // @pyparm int|item||The index of the item whose attributes are to be retrieved. + &sub)) // @pyparm int|sub||Specifies the subitem whose text is to be retrieved. + return NULL; + + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + char textBuf[256]; + LV_ITEM lvItem; + lvItem.iItem = item; + lvItem.iSubItem = sub; + lvItem.pszText = textBuf; + lvItem.cchTextMax = sizeof(textBuf); + lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + GUI_BGN_SAVE; + BOOL ok = pList->GetItem( &lvItem); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetItem failed"); + return MakeLV_ITEMTuple(&lvItem); +} + +// @pymethod int|PyCListCtrl|GetItemText|Retrieves the text of a list view item or subitem. +PyObject *PyCListCtrl_GetItemText( PyObject *self, PyObject *args ) +{ + int item, sub; + char buf[256]; + if (!PyArg_ParseTuple( args, "ii:GetItemText", + &item, // @pyparm int|item||The index of the item whose text is to be retrieved. + &sub)) // @pyparm int|sub||Specifies the subitem whose text is to be retrieved. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + int len = pList->GetItemText(item, sub, buf, sizeof(buf)); + GUI_END_SAVE; + if (len==0) + RETURN_ERR("GetItemText failed"); + return Py_BuildValue("s#",buf,len); +} + +// @pymethod int|PyCListCtrl|SetItemText|Changes the text of a list view item or subitem. +PyObject *PyCListCtrl_SetItemText( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item, sub; + char *text; + if (!PyArg_ParseTuple( args, "iis:SetItemText", + &item, // @pyparm int|item||Index of the item whose text is to be set. + &sub, // @pyparm int|sub||Index of the subitem, or zero to set the item label. + &text)) // @pyparm string|text||String that contains the new item text. + + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItemText(item, sub, text); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemText failed"); + RETURN_NONE; +} + +// @pymethod int|PyCListCtrl|GetItemState|Retrieves the state of a list view item. +PyObject *PyCListCtrl_GetItemState( PyObject *self, PyObject *args ) +{ + int item, mask; + if (!PyArg_ParseTuple( args, "ii:GetItemState", + &item, // @pyparm int|item||The index of the item whose position is to be retrieved. + &mask)) // @pyparm int|mask||Mask specifying which of the item's state flags to return. + + return NULL; + GUI_BGN_SAVE; + CListCtrl *pList = GetListCtrl(self); + GUI_END_SAVE; + if (!pList) return NULL; + return Py_BuildValue("i", pList->GetItemState(item, mask)); +} + +// @pymethod int|PyCListCtrl|SetItemState|Changes the state of an item in a list view control. +PyObject *PyCListCtrl_SetItemState( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item, state, mask; + if (!PyArg_ParseTuple( args, "iii:SetItemState", + &item, // @pyparm int|item||Index of the item whose state is to be set. + &state, // @pyparm int|state||New values for the state bits. + &mask)) // @pyparm int|mask||Mask specifying which state bits to change. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItemState(item, state, mask); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemState failed"); + RETURN_NONE; +} +// @pymethod object|PyCListCtrl|GetItemData|Retrieves the application-specific value associated with an item. +PyObject *PyCListCtrl_GetItemData( PyObject *self, PyObject *args ) +{ + int item; + if (!PyArg_ParseTuple( args, "i:GetItemData", + &item)) // @pyparm int|item||The index of the item whose data is to be retrieved. + + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + PyObject *ret = PyWin_GetPythonObjectFromLong(pList->GetItemData(item)); + GUI_END_SAVE; + // inc ref count for return value. + Py_XINCREF(ret); + return ret; +} + +// @pymethod int|PyCListCtrl|SetItemData|Sets the item's application-specific value. +PyObject *PyCListCtrl_SetItemData( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item; + PyObject *data; + if (!PyArg_ParseTuple( args, "iO:SetItemData", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &data)) // @pyparm object|Data||New value for the data. + return NULL; + if (data==Py_None) data = NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemData failed"); + // @comm Note that a reference count is not added to the object. This it is your + // responsibility to make sure the object remains alive while in the list. + RETURN_NONE; +} + +// @pymethod int|PyCListCtrl|GetNextItem|Searches for a list view item with specified properties and with specified relationship to a given item. +PyObject *PyCListCtrl_GetNextItem( PyObject *self, PyObject *args ) +{ + int item, flags; + if (!PyArg_ParseTuple( args, "ii:GetNextItem", + &item, // @pyparm int|item||Index of the item to begin the searching with, or -1 to find the first item that matches the specified flags. The specified item itself is excluded from the search. + &flags)) // @pyparm int|flags||Geometric relation of the requested item to the specified item, + // and the state of the requested item. The geometric relation can be one of these values: + // LVNI_ABOVELVNI_ALLLVNI_BELOWLVNI_TOLEFTLVNI_TORIGHT + // The state can be zero, or it can be one or more of these values: + // LVNI_DROPHILITEDLVNI_FOCUSEDLVNI_HIDDENLVNI_MARKEDLVNI_SELECTED + // If an item does not have all of the specified state flags set, the search continues with the next item. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + int rc = pList->GetNextItem(item, flags); + GUI_END_SAVE; + if (rc==(int)-1) + RETURN_ERR("GetNextItem failed"); + return Py_BuildValue("i",rc); + // @rdesc Returns an integer index, or raises a win32ui.error exception if not item can be found. +} + +// @pymethod int|PyCListCtrl|RedrawItems|Forces a listview to repaint a range of items. +PyObject *PyCListCtrl_RedrawItems( PyObject *self, PyObject *args ) +{ + int first, last; + if (!PyArg_ParseTuple( args, "ii:RedrawItems", + &first, // @pyparm int|first||Index of the first item to be repainted. + &last)) // @pyparm int|first||Index of the last item to be repainted. + return NULL; + CListCtrl *pList = GetListCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->RedrawItems(first, last); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("RedrawItems failed"); + RETURN_NONE; + // @comm The specified items are not actually repainted until the list view window receives a WM_PAINT message. + // To repaint immediately, call the Windows UpdateWindow function after using this function. +} + +// @pymethod (int, int, int, int)|PyCListCtrl|GetItemRect|Retrieves the bounding rectangle of a list view item. +PyObject *PyCListCtrl_GetItemRect( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item; + RECT rect; + BOOL bTextOnly; + if (!PyArg_ParseTuple( args, "ii:GetItemRect", + &item, // @pyparm int|item||Index of the item whose Data is to be set. + &bTextOnly)) // @pyparm int|bTextOnly||f this parameter is nonzero, the bounding rectangle includes only the text of the item. Otherwise it includes the entire line that the item occupies in the list view control. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->GetItemRect(item, &rect, bTextOnly); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetItemRect failed"); + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod |PyCListCtrl|GetEditControl|Retrieves the handle of the edit control used to edit the specified list view item. +PyObject *PyCListCtrl_GetEditControl( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + if (!PyArg_ParseTuple( args, ":GetEditControl")) + return NULL; + GUI_BGN_SAVE; + CEdit *pEdit = pList->GetEditControl(); + GUI_END_SAVE; + if (pEdit==NULL) + RETURN_ERR("GetEditControl failed"); + return ui_assoc_object::make(UITypeFromCObject(pEdit), pEdit)->GetGoodRet(); +} + +// @pymethod |PyCListCtrl|EditLabel|Edits a specified list view item in-place. +PyObject *PyCListCtrl_EditLabel( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item; + // @pyparm int|item||The index of item to edit. + if (!PyArg_ParseTuple( args, "i:EditLabel", &item)) + return NULL; + GUI_BGN_SAVE; + CEdit *pEdit = pList->EditLabel(item); + GUI_END_SAVE; + if (pEdit==NULL) + RETURN_ERR("EditLabel failed"); + return ui_assoc_object::make(UITypeFromCObject(pEdit), pEdit)->GetGoodRet(); +} + +// @pymethod int|PyCListCtrl|EnsureVisible|Ensures that a list view item is visible in its list view control. +PyObject *PyCListCtrl_EnsureVisible( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item; + BOOL bPartialOK; + // @pyparm int|item||The index of item to edit. + // @pyparm int|bPartialOK||Specifies whether partial visibility is acceptable. + if (!PyArg_ParseTuple( args, "ii:EnsureVisible", &item, &bPartialOK)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->EnsureVisible(item,bPartialOK); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("EnsureVisible failed"); + RETURN_NONE; +} + +// @pymethod ,(x,y)|PyCListCtrl|CreateDragImage|Creates a dragging bitmap for the specified list view item. +PyObject *PyCListCtrl_CreateDragImage( PyObject *self, PyObject *args ) +{ + CListCtrl *pList = pList=GetListCtrl(self); + if (!pList) return NULL; + int item; + // @pyparm int|item||The index of the item to edit. + if (!PyArg_ParseTuple( args, "i:CreateDragImage", &item)) + return NULL; + POINT pt; + CImageList *pIL = pList->CreateDragImage(item, &pt); + if (pIL==NULL) + RETURN_ERR("CreateDragImage failed"); + PyObject *newOb = ui_assoc_object::make(PyCImageList::type, pIL)->GetGoodRet(); + PyObject *ret = Py_BuildValue("O(ii)", newOb, pt.x, pt.y); + Py_DECREF(newOb); + return ret; +} + + +// @object PyCListCtrl|A class which encapsulates an MFC CListCtrl object. Derived from a object. +static struct PyMethodDef PyCListCtrl_methods[] = { + {"Arrange", PyCListCtrl_Arrange, 1}, // @pymeth Arrange|Aligns items on a grid. + {"CreateWindow", PyCListCtrl_CreateWindow, 1}, // @pymeth CreateWindow|Creates the actual window for the object. + {"DeleteAllItems", PyCListCtrl_DeleteAllItems, 1}, // @pymeth DeleteAllItems|Deletes all items from the list. + {"DeleteItem", PyCListCtrl_DeleteItem, 1}, // @pymeth DeleteItem|Deletes the specified item. + {"GetTextColor", PyCListCtrl_GetTextColor, 1}, // @pymeth GetTextColor|Retrieves the text color of a list view control. + {"SetTextColor", PyCListCtrl_SetTextColor, 1}, // @pymeth SetTextColor|Sets the text color of a list view control. + {"GetBkColor", PyCListCtrl_GetBkColor, 1}, // @pymeth GetBkColor|Retrieves the background color of the control. + {"SetBkColor", PyCListCtrl_SetBkColor, 1}, // @pymeth SetBkColor|Sets the background color of the control. + {"GetItem", PyCListCtrl_GetItem, 1}, // @pymeth GetItem|Retrieves the details of an items attributes. + {"GetItemCount", PyCListCtrl_GetItemCount, 1}, // @pymeth GetItemCount|Retrieves the number of items in a list view control. + {"GetItemRect", PyCListCtrl_GetItemRect, 1}, // @pymeth GetItemRect|Retrieves the bounding rectangle of a list view item. + {"GetEditControl", PyCListCtrl_GetEditControl, 1}, // @pymeth GetEditControl|Retrieves the handle of the edit control used to edit the specified list view item. + {"EditLabel", PyCListCtrl_EditLabel, 1}, // @pymeth EditLabel|Edits a specified list view item in-place. + {"EnsureVisible", PyCListCtrl_EnsureVisible, 1}, // @pymeth EnsureVisible|Ensures that a list view item is visible in its list view control. + {"CreateDragImage",PyCListCtrl_CreateDragImage, 1}, // @pymeth CreateDragImage|Creates a dragging bitmap for the specified list view item. + {"GetImageList", PyCListCtrl_GetImageList, 1}, // @pymeth GetImageList|Retrieves the current image list. + {"GetNextItem", PyCListCtrl_GetNextItem, 1}, // @pymeth GetNextItem|Searches for a list view item with specified properties and with specified relationship to a given item. + {"InsertColumn", PyCListCtrl_InsertColumn, 1}, // @pymeth InsertColumn|Inserts a column into a list control when in report view. + {"InsertItem", PyCListCtrl_InsertItem, 1}, // @pymeth InsertItem|Inserts an item into the list. + {"SetImageList", PyCListCtrl_SetImageList, 1}, // @pymeth SetImageList|Assigns an image list to a list view control. + {"GetColumn", PyCListCtrl_GetColumn, 1}, // @pymeth GetColumn|Retrieves the details of a column in the control. + {"GetTextBkColor", PyCListCtrl_GetTextBkColor, 1}, // @pymeth GetTextBkColor|Retrieves the text background color of a list view control. + {"SetTextBkColor", PyCListCtrl_SetTextBkColor, 1}, // @pymeth SetTextBkColor|Sets the text background color of a list view control. + {"GetTopIndex", PyCListCtrl_GetTopIndex, 1}, // @pymeth GetTopIndex|Retrieves the index of the topmost visible item. + {"GetCountPerPage",PyCListCtrl_GetCountPerPage, 1}, // @pymeth GetCountPerPage|Calculates the number of items that can fit vertically in a list view control. + {"GetSelectedCount",PyCListCtrl_GetSelectedCount, 1}, // @pymeth GetSelectedCount|Retrieves the number of selected items in the list view control. + {"SetItem", PyCListCtrl_SetItem, 1}, // @pymeth SetItem|Sets some of all of an items attributes. + {"SetItemState", PyCListCtrl_SetItemState, 1}, // @pymeth SetItemState|Changes the state of an item in a list view control. + {"GetItemState", PyCListCtrl_GetItemState, 1}, // @pymeth GetItemState|Retrieves the state of a list view item. + {"SetItemData", PyCListCtrl_SetItemData, 1}, // @pymeth SetItemData|Sets the item's application-specific value. + {"GetItemData", PyCListCtrl_GetItemData, 1}, // @pymeth GetItemData|Retrieves the application-specific value associated with an item. + {"SetItemCount", PyCListCtrl_SetItemCount, 1}, // @pymeth SetItemCount|Prepares a list view control for adding a large number of items. + {"GetItemCount", PyCListCtrl_GetItemCount, 1}, // @pymeth GetItemCount|Retrieves the number of items in a list view control. + {"SetItemText", PyCListCtrl_SetItemText, 1}, // @pymeth SetItemText|Changes the text of a list view item or subitem. + {"GetItemText", PyCListCtrl_GetItemText, 1}, // @pymeth GetItemText|Retrieves the text of a list view item or subitem. + {"RedrawItems", PyCListCtrl_RedrawItems, 1}, // @pymeth RedrawItems|Redraws a range of items + {"Update", PyCListCtrl_Update, 1}, // @pymeth Update|Forces the control to repaint a specified item. + + {"SetColumn", PyCListCtrl_SetColumn, 1}, // @pymeth SetColumn|Sets the state of a column in a list control when in report view. + {"DeleteColumn", PyCListCtrl_DeleteColumn, 1}, // @pymeth DeleteColumn|Deletes the specified column from the list control. + {"GetColumnWidth", PyCListCtrl_GetColumnWidth, 1}, // @pymeth GetColumnWidth|Gets the width of the specified column in the list control. + {"SetColumnWidth", PyCListCtrl_SetColumnWidth, 1}, // @pymeth SetColumnWidth|Sets the width of the specified column in the list control. + {"GetStringWidth", PyCListCtrl_GetStringWidth, 1}, // @pymeth GetStringWidth|Gets the necessary column width to fully display this text in a column. + {NULL, NULL} +}; + + +ui_type_CObject PyCListCtrl::type("PyCListCtrl", + &PyCWnd::type, + RUNTIME_CLASS(CListCtrl), + sizeof(PyCListCtrl), + PyCListCtrl_methods, + GET_PY_CTOR(PyCListCtrl)); diff --git a/Pythonwin/win32ctrlList.h b/Pythonwin/win32ctrlList.h new file mode 100644 index 0000000000..505a8c53da --- /dev/null +++ b/Pythonwin/win32ctrlList.h @@ -0,0 +1,53 @@ +/* win32ctrllist : header + + List control object. + + Created May 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 + +*/ +/////////////////////////////////////////////////////////////////////// +// Control objects. +// +// PyCListCtrl +// +class PyCListCtrl : public PyCWnd { + +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCListCtrl) + +protected: + PyCListCtrl(); + virtual ~PyCListCtrl(); +}; + +class PythonImageList : public CImageList +{ +public: + PythonImageList(); + ~PythonImageList(); +#ifdef _DEBUG + virtual void Dump( CDumpContext &dc ) const; +#endif +}; + +/////////////////////////////////////////////////////////////////////// +// ImageList +// + +class PYW_EXPORT PyCImageList : public ui_assoc_CObject{ +public: + MAKE_PY_CTOR(PyCImageList) + static CImageList *GetImageList(PyObject *self); + static ui_type_CObject type; +protected: + PyCImageList(); + virtual ~PyCImageList(); +}; diff --git a/Pythonwin/win32ctrlRichEdit.cpp b/Pythonwin/win32ctrlRichEdit.cpp new file mode 100644 index 0000000000..658c778565 --- /dev/null +++ b/Pythonwin/win32ctrlRichEdit.cpp @@ -0,0 +1,923 @@ +// Rich Edit Control +#include "stdafx.h" + +#include "win32win.h" +#include "win32control.h" +#include "win32doc.h" +#include "win32dc.h" +#include "win32RichEdit.h" + +// @doc + +extern CRichEditView *GetRichEditViewPtr(PyObject *self); + +CRichEditCtrl *GetRichEditCtrl(PyObject *self) +{ + if (ui_base_class::is_uiobject(self, &PyCRichEditView::type)) { + CRichEditView *pView = GetRichEditViewPtr(self); + if (pView) + return &(pView->GetRichEditCtrl()); + else + return NULL; + } + if (ui_base_class::is_uiobject(self, &PyCRichEditCtrl::type)) + return (CRichEditCtrl *)PyCWnd::GetPythonGenericWnd(self, &PyCRichEditCtrl::type); + RETURN_ERR("Python object can not be used as an edit control."); +} + +PyCRichEditCtrl::PyCRichEditCtrl() +{ +} +PyCRichEditCtrl::~PyCRichEditCtrl() +{ +} + +///////////////////////////////////////////////////////////////////// +// +// Rich Edit Control object +// +////////////////////////////////////////////////////////////////////// + +// @pymethod |win32ui|CreateRichEditCtrl|Creates a rich edit control. +PyObject *PyCRichEditCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + GUI_BGN_SAVE; + CRichEditCtrl *pCtrl = new CRichEditCtrl(); + GUI_END_SAVE; + return ui_assoc_object::make(PyCRichEditCtrl::type, pCtrl); + // @comm This method only creates the RichEdit object. To create the window, (ie, the control itself), call +} + +// @pymethod|PyCRichEditCtrl|CreateWindow|Creates a rich edit control window. +static PyObject *PyCRichEditCtrl_create_window(PyObject *self, PyObject *args) +{ + RECT rect; + int style, id; + PyObject *obParentWnd; + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The control style + // @pyparm int,int,int,int|rect||The position of the control + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm |parent||The parent window. Must not be None + &obParentWnd, + &id )) // @pyparm int|id||The control ID + return NULL; + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + if (pEdit->m_hWnd) + RETURN_ERR("The window already exists"); + CWnd *pParent = GetWndPtr(obParentWnd); + if (pParent==NULL) return NULL; + BOOL ok; + GUI_BGN_SAVE; + ok = pEdit->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("Create failed"); + RETURN_NONE; +} + +DWORD CALLBACK PyCRichEditCallbackIn(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + // documentation for ths function seems to be wrong WRT return values. + // I need to return 0 on all success, rather than bytes in buffer. + CEnterLeavePython _celp; + int retval = -1; + PyObject *method = (PyObject *)dwCookie; + PyObject *args = Py_BuildValue("(i)", cb); + PyObject *result = gui_call_object( method, args ); + Py_DECREF(args); + if (result==NULL) { +// gui_print_error(); // let it filter up. + } else if (result==Py_None) { + retval = 0; + *pcb = 0; + } else { + char *s = PyString_AsString(result); + if (s==NULL) { +// gui_print_error(); + } else { + strcpy((char *)pbBuff, s); + *pcb = strlen(s); + retval = 0; + } + } + Py_XDECREF(result); + return retval; +} + +DWORD CALLBACK PyCRichEditCallbackOut(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + CEnterLeavePython _celp; + int retval = 0; // default abort stream + PyObject *method = (PyObject *)dwCookie; + PyObject *args = Py_BuildValue("(s#)", pbBuff, cb); + PyObject *result = gui_call_object( method, args ); + Py_DECREF(args); + if (result==NULL) { +// gui_print_error(); + } else { + retval = PyInt_AsLong(result); + if (PyErr_Occurred()) { + gui_print_error(); +// retval = 0; + } + } + return retval; +} + + +// @pymethod |PyCRichEditCtrl|Copy|Copys the current selection to the clipboard. +static PyObject *PyCRichEditCtrl_copy(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Copy(); // @pyseemfc CRichEditCtrl|Copy + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCRichEditCtrl|Clear|Clears all text in an edit control. +static PyObject * +PyCRichEditCtrl_clear(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit ) + return NULL; + GUI_BGN_SAVE; + pEdit->Clear(); // @pyseemfc CRichEditCtrl|Clear + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|Cut|Cuts the current selection to the clipboard. +static PyObject *PyCRichEditCtrl_cut(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Cut(); // @pyseemfc CRichEditCtrl|Cut + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int, (start, end)|PyCRichEditCtrl|FindText|Finds text in the control +static PyObject * +PyCRichEditCtrl_find_text(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + int flags; + FINDTEXTEX ft = { {0,0}, NULL, {0,0} }; + // @pyparm int|charPos||The character position + if (!PyArg_ParseTuple(args, "l(ll)s:FindText", &flags, &ft.chrg.cpMin, &ft.chrg.cpMax, &ft.lpstrText)) + return NULL; + GUI_BGN_SAVE; + long rc = pEdit->FindText(flags, &ft); + GUI_END_SAVE; + return Py_BuildValue("l(ll)", rc, ft.chrgText.cpMin, ft.chrgText.cpMax); +} + +// @pymethod (tuple)|PyCRichEditCtrl|GetCharPos|Returns the location of the top-left corner of the character specified by charPos. +static PyObject * +PyCRichEditCtrl_get_char_pos(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + LONG lCharPos; + // @pyparm int|charPos||The character position + if (!PyArg_ParseTuple(args, "l:GetCharPos", &lCharPos)) + return NULL; + GUI_BGN_SAVE; + CPoint p = pEdit->GetCharPos(lCharPos); + GUI_END_SAVE; + return Py_BuildValue("ll", p.x, p.y); + // @rdesc The return value is a +} + +// @pymethod (tuple)|PyCRichEditCtrl|GetDefaultCharFormat|Returns the current default character formatting attributes in this object. +static PyObject * +PyCRichEditCtrl_get_default_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + if (!PyArg_ParseTuple(args, ":GetDefaultCharFormat")) + return NULL; + fmt.cbSize = sizeof(CHARFORMAT); + // Appears to be a documentation bug - dwMask always is + // coming back as zero! It appears you must set all dwMask + // bits, and the other bits do get reset. + fmt.dwMask = CFM_ALL; + GUI_BGN_SAVE; + /*fmt.dwMask = */ + pEdit->GetDefaultCharFormat(fmt); // @pyseemfc CRichEditCtrl|GetDefaultCharFormat + GUI_END_SAVE; + return MakeCharFormatTuple(&fmt); + // @rdesc The return value is a +} + +// @pymethod int|PyCRichEditCtrl|GetEventMask|Returns the current event mask. +static PyObject * +PyCRichEditCtrl_get_event_mask(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + if (!PyArg_ParseTuple(args, ":GetEventMask")) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->GetEventMask(); + GUI_END_SAVE; + return Py_BuildValue("i",rc); // @pyseemfc CRichEditCtrl|GetEventMask +} + +// @pymethod int|PyCRichEditCtrl|GetLineCount|Gets the number of lines in an edit control. +static PyObject * +PyCRichEditCtrl_get_line_count(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit ) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->GetLineCount(); + GUI_END_SAVE; + return Py_BuildValue("i",rc); // @pyseemfc CRichEditCtrl|GetLineCount + // @rdesc The number of lines in the buffer. If the control is empty, the return value is 1. +} +// @pymethod int|PyCRichEditCtrl|GetFirstVisibleLine|Returns zero-based index of the topmost visible line. +static PyObject * +PyCRichEditCtrl_get_first_visible(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->GetFirstVisibleLine(); + GUI_END_SAVE; + return Py_BuildValue("i",rc); // @pyseemfc CRichEditCtrl|GetFirstVisibleLine + // @rdesc The zero-based index of the topmost visible line. For single-line edit controls, the return value is 0. +} + +// @pymethod (tuple)|PyCRichEditCtrl|GetParaFormat|Returns the current paragraph formatting attributes. +static PyObject * +PyCRichEditCtrl_get_para_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + PARAFORMAT fmt; + if (!PyArg_ParseTuple(args, ":GetParaFormat")) + return NULL; + fmt.cbSize = sizeof(PARAFORMAT); + GUI_BGN_SAVE; + pEdit->GetParaFormat(fmt); // @pyseemfc CRichEditCtrl|GetParaFormat + GUI_END_SAVE; + return MakeParaFormatTuple(&fmt); + // @rdesc The return value is a +} + +// @pymethod (start, end)|PyCRichEditCtrl|GetSel|Returns the start and end of the current selection. +static PyObject *PyCRichEditCtrl_get_sel(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + long start,end; + GUI_BGN_SAVE; + pEdit->GetSel(start,end); // @pyseemfc CRichEditCtrl|GetSel + GUI_END_SAVE; + return Py_BuildValue("(ll)",start,end); + // @rdesc The return tuple is (the first character in the current selection, first nonselected character past the end of the current selection) +} + +// @pymethod (tuple)|PyCRichEditCtrl|GetSelectionCharFormat|Returns the character formatting of the selection. +static PyObject * +PyCRichEditCtrl_get_selection_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + if (!PyArg_ParseTuple(args, ":GetSelectionCharFormat")) + return NULL; + fmt.cbSize = sizeof(CHARFORMAT); + GUI_BGN_SAVE; + fmt.dwMask = pEdit->GetSelectionCharFormat(fmt); // @pyseemfc CRichEditCtrl|GetSelectionCharFormat + GUI_END_SAVE; + return MakeCharFormatTuple(&fmt); +} + + +// @pymethod |PyCRichEditCtrl|LimitText|Sets max length of text that user can enter +static PyObject *PyCRichEditCtrl_limit_text(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + int nChars = 0; + // @pyparm int|nChars|0|Specifies the length (in bytes) of the text that the user can enter. If this parameter is 0, the text length is set to + // UINT_MAX bytes. This is the default behavior. + if (!PyArg_ParseTuple(args, "|i:LimitText", &nChars)) + return NULL; + GUI_BGN_SAVE; + pEdit->LimitText(nChars); // @pyseemfc CRichEditCtrl|LimitText + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCRichEditCtrl|LineIndex|Retrieves the character index of a line within a multiple-line edit control. +static PyObject * +PyCRichEditCtrl_line_index(PyObject *self, PyObject *args) +{ + // @comm This method only works on multi-linr edit controls. + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|lineNo|-1|Contains the index value for the desired line in the text + // of the edit control, or contains -1. If -1, then it specifies the current line. + int lineNo = -1; + if (!PyArg_ParseTuple(args, "|i:LineIndex", &lineNo)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->LineIndex(lineNo); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CRichEditCtrl|LineIndex + // @rdesc The character index of the line specified in lineNo, or -1 if + // the specified line number is greater then the number of lines in + // the edit control. +} +// @pymethod int|PyCRichEditCtrl|LineScroll|Scroll the control vertically and horizontally +static PyObject * +PyCRichEditCtrl_line_scroll(PyObject *self, PyObject *args) +{ + // @comm This method only works on multi-linr edit controls. + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|nLines||Specifies the number of lines to scroll vertically. + // @pyparm int|nChars|0|Specifies the number of character positions to scroll horizontally. This value is ignored if the edit control has either the + // ES_RIGHT or ES_CENTER style. + int nLines, nChars = 0; + if (!PyArg_ParseTuple(args, "i|i:LineScroll", &nLines, &nChars)) + return NULL; + GUI_BGN_SAVE; + pEdit->LineScroll(nLines, nChars); // @pyseemfc CRichEditCtrl|LineScroll + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCRichEditCtrl|LineFromChar|Returns the line number of the specified character. +static PyObject * +PyCRichEditCtrl_line_from_char(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + int charNo=-1; + // @pyparm int|charNo|-1|Contains the zero-based index value for the desired character in the text of the edit + // control, or -1. If -1, then it specifies the current line. + if (!PyArg_ParseTuple(args, "|i:LineFromChar", &charNo)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->LineFromChar(charNo); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CRichEditCtrl|LineFromChar + // @rdesc The zero-based line number of the line containing the character index specified by charNo. + // If charNo is -1, the number of the line that contains the first character of the selection is returned. + // If there is no selection, the current line number is returned. +} + +// @pymethod int|PyCRichEditCtrl|GetLine|Returns the text in a specified line. +static PyObject * +PyCRichEditCtrl_get_line(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + int lineNo = pEdit->LineFromChar(-1); + GUI_END_SAVE; + // @pyparm int|lineNo|current|Contains the zero-based index value for the desired line. + // @comm This function is not an MFC wrapper. + if (!PyArg_ParseTuple(args, "|i:GetLine", &lineNo)) + return NULL; + int size = 1024; // ahhhhh-this fails with 128, even when line len==4! + // god damn it - try and write a fairly efficient normal case, + // and handle worst case, and look what happens! + CString csBuffer; // use dynamic mem for buffer + char *buf; + int bytesCopied; + // this TRACE _always_ returns the length of the first line - hence the + // convaluted code below. +// TRACE("LineLength for line %d is %d\n", lineNo, pView->GetRichEditCtrl().LineLength(lineNo)); + // loop if buffer too small, increasing each time. + while (size<0x7FFF) // reasonable line size max? - maxuint on 16 bit. + { + buf = csBuffer.GetBufferSetLength(size); + if (buf==NULL) + RETURN_ERR("Out of memory getting Edit control line value"); + + GUI_BGN_SAVE; + bytesCopied = pEdit->GetLine(lineNo, buf, size); + GUI_END_SAVE; + if (bytesCopied!=size) // ok - get out. + break; + // buffer too small + size += size; // try doubling! + TRACE0("Doubling buffer for GetLine value\n"); + } + if (bytesCopied==size) // hit max. + --bytesCopied; // so NULL doesnt overshoot. +// if (buf[bytesCopied-1]=='\r' || buf[bytesCopied-1]=='\n') // kill newlines. +// --bytesCopied; + buf[bytesCopied] = '\0'; + return Py_BuildValue("s",buf); +} + +// @pymethod |PyCRichEditCtrl|Paste|Pastes the contents of the clipboard into the control. +static PyObject *PyCRichEditCtrl_paste(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + GUI_BGN_SAVE; + pEdit->Paste(); // @pyseemfc CRichEditCtrl|Paste + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|ReplaceSel|Replaces the selection with the specified text. +static PyObject *PyCRichEditCtrl_replace_sel(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + char *msg; + // @pyparm string|text||The text to replace the selection with. + if (!pEdit || !PyArg_ParseTuple(args, "s:ReplaceSel", &msg)) + return NULL; + GUI_BGN_SAVE; + pEdit->ReplaceSel(msg); // @pyseemfc CRichEditCtrl|ReplaceSel + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCRichEditCtrl|SetBackgroundColor|Sets the background color for the control. +static PyObject * +PyCRichEditCtrl_set_background_color(PyObject *self, PyObject *args) +{ + BOOL bSysColor; + int cr = 0; + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|bSysColor||Indicates if the background color should be set to the system value. If this value is TRUE, cr is ignored. + // @pyparm int|cr|0|The requested background color. Used only if bSysColor is FALSE. + if (!PyArg_ParseTuple(args, "i|i:SetBackgroundColor", &bSysColor, &cr)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->SetBackgroundColor(bSysColor, (COLORREF)cr); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CRichEditCtrl|SetEventMask + // @rdesc The return value is the previous background color. +} + +// @pymethod int|PyCRichEditCtrl|SetEventMask|Sets the event motification mask. +static PyObject * +PyCRichEditCtrl_set_event_mask(PyObject *self, PyObject *args) +{ + int event; + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|eventMask||The new event mask. Must be one of the win32con.ENM_* flags. + if (!PyArg_ParseTuple(args, "i:SetEventMask", &event)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->SetEventMask(event); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CRichEditCtrl|SetEventMask + // @rdesc The return value is the previous event mask. +} + + +// @pymethod |PyCRichEditCtrl|SetDefaultCharFormat|Sets the current default character formatting attributes in this object. +static PyObject * +PyCRichEditCtrl_set_default_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.cbSize = sizeof(CHARFORMAT); + PyObject *fmtTuple; + // @pyparm tuple|charFormat||A charformat tuple. See for details. + if (!PyArg_ParseTuple(args, "O:SetDefaultCharFormat", &fmtTuple)) + return NULL; + if (!ParseCharFormatTuple(fmtTuple, &fmt)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pEdit->SetDefaultCharFormat(fmt); + GUI_END_SAVE; + if (!ok) // @pyseemfc CRichEditCtrl|SetDefaultCharFornmat + RETURN_ERR("SetDefaultCharFormat failed"); + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|SetWordCharFormat|Sets the currently selected word's character formatting attributes. +static PyObject * +PyCRichEditCtrl_set_word_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.cbSize = sizeof(CHARFORMAT); + PyObject *fmtTuple; + // @pyparm tuple|charFormat||A charformat tuple. See for details. + if (!PyArg_ParseTuple(args, "O:SetWordCharFormat", &fmtTuple)) + return NULL; + if (!ParseCharFormatTuple(fmtTuple, &fmt)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pEdit->SetWordCharFormat(fmt); + GUI_END_SAVE; + if (!ok) // @pyseemfc CRichEditCtrl|SetWordCharFormat + RETURN_ERR("SetWordCharFormat failed"); + RETURN_NONE; +} + + +// @pymethod int|PyCRichEditCtrl|SetParaFormat|Sets the paragraph formatting +static PyObject * +PyCRichEditCtrl_set_para_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + PARAFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + // @pyparm tuple|paraFormat||A charformat tuple. See for details. + fmt.cbSize = sizeof(PARAFORMAT); + PyObject *fmtTuple; + if (!PyArg_ParseTuple(args, "O:SetParaFormat", &fmtTuple)) + return NULL; + if (!ParseParaFormatTuple(fmtTuple, &fmt)) + return NULL; + GUI_BGN_SAVE; + int rc = pEdit->SetParaFormat(fmt); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CRichEditCtrl|SetParaFormat + // @rdesc This function seems to return occasionally return failure, but + // the formatting is applied. Therefore an exception is not raised on failure, + // but the BOOL return code is passed back. +} + +// @pymethod |PyCRichEditCtrl|SetSelectionCharFormat|Sets the current selections character formatting attributes. +static PyObject * +PyCRichEditCtrl_set_selection_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.cbSize = sizeof(CHARFORMAT); + fmt.dwMask = 0; + PyObject *fmtTuple; + // @pyparm tuple|charFormat||A charformat tuple. See for details. + if (!PyArg_ParseTuple(args, "O:SetSelectionCharFormat", &fmtTuple)) + return NULL; + if (!ParseCharFormatTuple(fmtTuple, &fmt)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pEdit->SetSelectionCharFormat(fmt); + GUI_END_SAVE; + if (!ok) // @pyseemfc CRichEditCtrl|SetSelectionCharFormat + RETURN_ERR("SetSelectionCharFormat failed"); + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|SetSelAndCharFormat|Sets the selection and char format. +// @comm Highly optimised for speed for color editors. +static PyObject * +PyCRichEditCtrl_set_sel_and_char_format(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.cbSize = sizeof(CHARFORMAT); + fmt.dwMask = 0; + // @pyparm tuple|charFormat||A charformat tuple. See for details. + if (!PyTuple_Check(args) || PyTuple_Size(args)!=3) + RETURN_TYPE_ERR("Expected exactly 3 arguments."); + +// PyErr_Clear(); + // Note - no reference added. + long start = PyInt_AsLong(PyTuple_GET_ITEM(args, 0)); + long end = PyInt_AsLong(PyTuple_GET_ITEM(args, 1)); + PyObject *fmtTuple = PyTuple_GET_ITEM(args, 2); + + if (PyErr_Occurred()) + return NULL; + + if (!ParseCharFormatTuple(fmtTuple, &fmt)) + return NULL; + + GUI_BGN_SAVE; + pEdit->SetSel(start,end); + BOOL ok = pEdit->SetSelectionCharFormat(fmt); + GUI_END_SAVE; + // @pyseemfc CRichEditCtrl|SetSelectionCharFormat + // @pyseemfc CRichEditCtrl|SetSel + if (!ok) + RETURN_ERR("SetSelectionCharFormat failed"); + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|SetOptions|Sets options for the control. +static PyObject * +PyCRichEditCtrl_set_options(PyObject *self, PyObject *args) +{ + int op, flags; + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + // @pyparm int|op||Indicates the operation. Must be one of the win32con.ECOOP_* flags. + // @pyparm int|flags||Indicates the options. Must be one a combination of win32con.ECO_* flags. + if (!PyArg_ParseTuple(args, "ii:SetOptions", &op, &flags)) + return NULL; + GUI_BGN_SAVE; + pEdit->SetOptions(op, flags); // @pyseemfc CRichEditCtrl|SetOptions + GUI_END_SAVE; + RETURN_NONE; +} + + +// @pymethod |PyCRichEditCtrl|SetReadOnly|Sets or clears the read-only status of the listbox. +static PyObject * +PyCRichEditCtrl_set_readonly(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + BOOL bState = TRUE; + // @pyparm int|bReadOnly|1|The read-only state to set. + if (!PyArg_ParseTuple(args, "|i:SetReadOnly", &bState)) + return NULL; + GUI_BGN_SAVE; + pEdit->SetReadOnly(bState); // @pyseemfc CRichEditCtrl|SetReadOnly + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|SetSel|Sets the selection in the edit control. +static PyObject *PyCRichEditCtrl_set_sel(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + int start=0,end=0; + if (!pEdit) + return NULL; + if (!PyArg_ParseTuple(args, "i|i:SetSel", + &start, // @pyparm int|start||Specifies the starting position. + // If start is 0 and end is -1, all the text in the edit control is selected. + // If start is -1, any current selection is removed. + &end)){ // @pyparm int|end|start|Specifies the ending position. + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "(ii):SetSel", + &start, // @pyparmalt2 (int, int)|start,end)||As for normal start, end args. + &end)) + return NULL; + } + if (start!=end && end==0) + end=start; + GUI_BGN_SAVE; + pEdit->SetSel(start,end); // @pyseemfc CRichEditCtrl|SetSel + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod (int,int)|PyCRichEditCtrl|StreamIn|Invokes a callback to stream data into the control. +static PyObject * +PyCRichEditCtrl_stream_in(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + int format; + PyObject *method; + // @pyparm int|format||The format. One of the win32con.SF_* flags (SF_TEXT,SF_RTF) + // @pyparm object|method||A callable object (eg, a method or function) + // This method is called with a single integer param, which is the maximum number of + // bytes to fetch. The method should return a zero length string, or None to + // finish the operation, and a string otherwise. + if (!PyArg_ParseTuple(args, "iO:StreamIn", &format, &method)) + return NULL; + if (!PyCallable_Check(method)) + RETURN_ERR("The method paramater is not callable"); + DOINCREF(method); + EDITSTREAM es; + es.dwCookie = (DWORD)method; + es.dwError = 0; + es.pfnCallback = PyCRichEditCallbackIn; + PyErr_Clear(); + GUI_BGN_SAVE; + long rc = pEdit->StreamIn(format, es); // @pyseemfc CRichEditCtrl|StreamIn + GUI_END_SAVE; + DODECREF(method); + return PyErr_Occurred() ? NULL : Py_BuildValue("li",rc, es.dwError); + // @rdesc The return value is a tuple of (no bytes written, error code) +} + +// @pymethod (int, int)|PyCRichEditCtrl|StreamOut|Invokes a callback to stream data into the control. +static PyObject * +PyCRichEditCtrl_stream_out(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + int format; + PyObject *method; + // @pyparm int|format||The format. One of the win32con.SF_* flags (SF_TEXT,SF_RTF) and may also combine SFF_SELECTION. + // @pyparm object|method||A callable object (eg, a method or function) + // This method is called with a string paramater. It should return an integer, zero to abort, non zero otherwise. + if (!PyArg_ParseTuple(args, "iO:StreamOut", &format, &method)) + return NULL; + if (!PyCallable_Check(method)) + RETURN_ERR("The method paramater is not callable"); + EDITSTREAM es; + DOINCREF(method); + es.dwCookie = (DWORD)method; + es.dwError = 0; + es.pfnCallback = PyCRichEditCallbackOut; + PyErr_Clear(); + GUI_BGN_SAVE; + long rc = pEdit->StreamOut(format, es); // @pyseemfc CRichEditCtrl|StreamOut + GUI_END_SAVE; + DODECREF(method); + return PyErr_Occurred() ? NULL : Py_BuildValue("li",rc, es.dwError); + // @rdesc The return value is a tuple of (no bytes written, error code) +} + +// @pymethod int|PyCRichEditCtrl|GetTextLength|Returns the length of the text in the control. +static PyObject * +PyCRichEditCtrl_get_text_length(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHECK_NO_ARGS2(args,GetTextLength); + GUI_BGN_SAVE; + long rc = pEdit->GetTextLength(); // @pyseemfc CRichEditCtrl|GetTextLength + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCRichEditCtrl|GetModify|Nonzero if the text in this control has been modified; otherwise 0. +static PyObject * +PyCRichEditCtrl_get_modify(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHECK_NO_ARGS2(args,GetModify); + GUI_BGN_SAVE; + BOOL rc = pEdit->GetModify(); // @pyseemfc CRichEditCtrl|GetModify + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod |PyCRichEditCtrl|SetModify|Sets the modified flag for this control +static PyObject * +PyCRichEditCtrl_set_modify(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + BOOL modified = TRUE; + // @pyparm int|modified|1|Indicates the new value for the modified flag. + if (!PyArg_ParseTuple(args, "i:SetModify", &modified)) + return NULL; + GUI_BGN_SAVE; + pEdit->SetModify(modified); // @pyseemfc CRichEditCtrl|SetModify + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCRichEditCtrl|SetTargetDevice|Sets the target device for the control +static PyObject * +PyCRichEditCtrl_set_target_device(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + PyObject *obDC; + int lineWidth; + // @pyparm |dc||The new DC - may be None + // @pyparm int|lineWidth||Line width to use for formatting. + if (!PyArg_ParseTuple(args, "Oi:SetTargetDevice", &obDC, &lineWidth)) + return NULL; + + CDC *pDC = NULL; + if (obDC != Py_None) { + pDC = ui_dc_object::GetDC(obDC); + if (pDC==NULL) + return NULL; + } + GUI_BGN_SAVE; + BOOL ok = pEdit->SetTargetDevice(*pDC, lineWidth); + GUI_END_SAVE; + if (!ok) // @pyseemfc CRichEditCtrl|SetTargetDevice + RETURN_ERR("SetTargetDevice failed"); + RETURN_NONE; +} + +// @pymethod string|PyCRichEditCtrl|GetSelText|Returns the currently selected text +static PyObject * +PyCRichEditCtrl_get_sel_text(PyObject *self, PyObject *args) +{ + CRichEditCtrl *pEdit = GetRichEditCtrl(self); + if (!pEdit) + return NULL; + CHECK_NO_ARGS2(args,GetSelText); + long start=0, end=0; + GUI_BGN_SAVE; + pEdit->GetSel(start, end); + GUI_END_SAVE; + PyObject *ret; + if (end-start) { + ret = PyString_FromStringAndSize(NULL, end-start); + char *buf = PyString_AsString(ret); + GUI_BGN_SAVE; + pEdit->GetSelText(buf); + GUI_END_SAVE; + } + else + ret = PyString_FromStringAndSize("", 0); + return ret; +} + +// @object PyCRichEditCtrl|A windows Rich Text edit control. Encapsulates an MFC class. Derived from a object. +static struct PyMethodDef PyCRichEditCtrl_methods[] = { + {"Clear", PyCRichEditCtrl_clear, 1}, // @pymeth Clear|Clears all text from an edit control. + {"Copy", PyCRichEditCtrl_copy, 1}, // @pymeth Copy|Copy the selection to the clipboard. + {"CreateWindow", PyCRichEditCtrl_create_window, 1}, // @pymeth CreateWindow|Creates a rich edit control. + {"Cut", PyCRichEditCtrl_cut, 1}, // @pymeth Cut|Cut the selection, and place it in the clipboard. + {"FindText", PyCRichEditCtrl_find_text, 1}, // @pymeth FindText|Finds text in the control + {"GetCharPos", PyCRichEditCtrl_get_char_pos, 1}, // @pymeth GetCharPos|Returns te location of the top-left corner of the specified character. + {"GetDefaultCharFormat", PyCRichEditCtrl_get_default_char_format, 1}, // @pymeth GetDefaultCharFormat|Returns the current default character formatting attributes in this object. + {"GetEventMask", PyCRichEditCtrl_get_event_mask, 1}, // @pymeth GetEventMask|Returns the current event mask. + {"GetSelectionCharFormat", PyCRichEditCtrl_get_selection_char_format, 1}, // @pymeth GetSelectionCharFormat|Returns the character formatting attributes of the current selection in this object. + {"GetFirstVisibleLine",PyCRichEditCtrl_get_first_visible,1}, // @pymeth GetFirstVisibleLine|Returns zero-based index of the topmost visible line. + {"GetParaFormat", PyCRichEditCtrl_get_para_format, 1}, // @pymeth GetParaFormat|Returns the formatting of the current paragraph. + {"GetSel", PyCRichEditCtrl_get_sel, 1}, // @pymeth GetSel|Returns the selection. + {"GetSelText", PyCRichEditCtrl_get_sel_text, 1}, // @pymeth GetSelText|Returns the currently selected text + {"GetTextLength", PyCRichEditCtrl_get_text_length, 1}, // @pymeth GetTextLength|Returns the length of the text in the control. + {"GetLine", PyCRichEditCtrl_get_line, 1}, // @pymeth GetLine|Returns a specified line. + {"GetModify", PyCRichEditCtrl_get_modify, 1}, // @pymeth GetModify|Determines if the control has been modified. + {"GetLineCount", PyCRichEditCtrl_get_line_count, 1}, // @pymeth GetLineCount|Returns the number of lines in an edit control. + {"LimitText", PyCRichEditCtrl_limit_text, 1}, // @pymeth LimitText|Sets max length of text that user can enter + {"LineFromChar", PyCRichEditCtrl_line_from_char, 1}, // @pymeth LineFromChar|Returns the line number of a given character. + {"LineIndex", PyCRichEditCtrl_line_index, 1}, // @pymeth LineIndex|Returns the line index + {"LineScroll", PyCRichEditCtrl_line_scroll, 1}, // @pymeth LineScroll|Scroll the control vertically and horizontally + {"Paste", PyCRichEditCtrl_paste, 1}, // @pymeth Paste|Pastes the contents of the clipboard into the edit control. + {"ReplaceSel", PyCRichEditCtrl_replace_sel, 1}, // @pymeth ReplaceSel|Replace the selection with the specified text. + {"SetBackgroundColor", PyCRichEditCtrl_set_background_color, 1}, // @pymeth SetBackgroundColor|Sets the background color for the control. + {"SetDefaultCharFormat", PyCRichEditCtrl_set_default_char_format, 1}, // @pymeth SetDefaultCharFormat|Sets the current default character formatting attributes in this PyCRichEditCtrl object. + {"SetEventMask", PyCRichEditCtrl_set_event_mask, 1}, // @pymeth SetEventMask|Sets the event motification mask. + {"SetSelectionCharFormat", PyCRichEditCtrl_set_selection_char_format, 1}, // @pymeth SetSelectionCharFormat|Sets the character formatting attributes for the selection in this PyCRichEditCtrl object. + {"SetModify", PyCRichEditCtrl_set_modify, 1}, // @pymeth SetModify|Sets the modified flag. + {"SetOptions", PyCRichEditCtrl_set_options, 1}, // @pymeth SetOptions|Sets options for the control. + {"SetParaFormat", PyCRichEditCtrl_set_para_format, 1}, // @pymeth SetParaFormat|Sets the paragraph formatting. + {"SetReadOnly", PyCRichEditCtrl_set_readonly, 1}, // @pymeth SetReadOnly|Set the read only status of an edit control. + {"SetSel", PyCRichEditCtrl_set_sel, 1}, // @pymeth SetSel|Changes the selection in an edit control. + {"SetSelAndCharFormat", PyCRichEditCtrl_set_sel_and_char_format, 1}, // @pymeth SetSelAndCharFormat|Sets the selection and the char format. + {"SetTargetDevice", PyCRichEditCtrl_set_target_device, 1}, // @pymeth SetTargetDevice|Sets the target device for the control + {"StreamIn", PyCRichEditCtrl_stream_in, 1}, // @pymeth StreamIn|Invokes a callback to stream data into the control. + {"StreamOut", PyCRichEditCtrl_stream_out, 1}, // @pymeth StreamOut|Invokes a callback to stream data out of the control. + {NULL, NULL} +}; + +PyCCtrlView_Type PyCRichEditCtrl::type("PyCRichEditCtrl",&ui_control_object::type, &PyCRichEditCtrl::type, RUNTIME_CLASS(CRichEditCtrl), sizeof(PyCRichEditCtrl), PyCRichEditCtrl_methods, GET_PY_CTOR(PyCRichEditCtrl)); + diff --git a/Pythonwin/win32ctrlTree.cpp b/Pythonwin/win32ctrlTree.cpp new file mode 100644 index 0000000000..ed3bd50221 --- /dev/null +++ b/Pythonwin/win32ctrlTree.cpp @@ -0,0 +1,777 @@ +/* win32ctllist : implementation file + + List control object. + + Created Feb 1997, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32dc.h" +#include "win32control.h" +#include "win32ctrltree.h" +#include "win32ImageList.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +const char *szErrTreeRequiresWindow = "The tree must have a window object for this operation"; + +PyCTreeCtrl::PyCTreeCtrl() +{ +} +PyCTreeCtrl::~PyCTreeCtrl() +{ +} +CTreeCtrl *GetTreeCtrl(PyObject *self, bool bNeedValidHwnd = true) +{ + extern CTreeView *GetTreeViewPtr(PyObject *self); + CTreeCtrl *rc; + + if (ui_base_class::is_uiobject(self, &PyCTreeView::type)) { + CTreeView *pView = GetTreeViewPtr(self); + if (pView) + rc = &(pView->GetTreeCtrl()); + else + rc = NULL; + } else + rc = (CTreeCtrl *)PyCWnd::GetPythonGenericWnd(self, &PyCTreeCtrl::type); + if (rc && bNeedValidHwnd && !::IsWindow(rc->m_hWnd)) + RETURN_ERR((char *)szErrTreeRequiresWindow); + return rc; +} + +// @pymethod |win32ui|CreateTreeCtrl|Creates a tree control. +PyObject *PyCTreeCtrl_create(PyObject *self, PyObject *args) +{ + return ui_assoc_object::make( PyCTreeCtrl::type, new CTreeCtrl)->GetGoodRet(); +} + +// @pymethod |PyCTreeCtrl|CreateWindow|Creates the actual window for the object. +static PyObject * +PyCTreeCtrl_CreateWindow(PyObject *self, PyObject *args) +{ + extern CWnd *GetWndPtrFromParam(PyObject *ob, ui_type_CObject &type); + + CTreeCtrl *pT = GetTreeCtrl(self, false); + if (!pT) return NULL; + RECT rect; + PyObject *obParent; + long style; + long id; + if(!PyArg_ParseTuple(args, "l(iiii)Ol:Create", + &style, // @pyparm int|style||The window style + &rect.left, &rect.top, &rect.right,&rect.bottom, // @pyparm int, int, int, int|rect||The default rectangle + &obParent, // @pyparm parent|||The parent window + &id))// @pyparm int|id||The control ID + return NULL; + + CWnd *pParent = NULL; + if (obParent != Py_None) { + pParent = GetWndPtrFromParam(obParent, PyCWnd::type); + if (pParent==NULL) return NULL; + } + + GUI_BGN_SAVE; + // @pyseemfc CTreeCtrl|Create + BOOL ok = pT->Create(style, rect, pParent, id); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CTreeCtrl::Create failed"); + RETURN_NONE; +} + +#define MAKE_GET_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pList->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +#define MAKE_GET_ITEM_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM htree; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &htree)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pList->mfcName(htree); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +#define MAKE_GET_ITEM_ITEM_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM htree; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &htree)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + HTREEITEM item = pList->mfcName(htree); \ + GUI_END_SAVE; \ + if (item==NULL) \ + RETURN_ERR(#mfcName " failed"); \ + return Py_BuildValue("i",item); \ +} + +#define MAKE_GET_ITEM_ITEM_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM htree; int code;\ + if (!PyArg_ParseTuple( args, "ii:" #mfcName, &htree, &code)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + HTREEITEM item = pList->mfcName(htree, code); \ + GUI_END_SAVE; \ + if (item==NULL) \ + RETURN_ERR(#mfcName " failed"); \ + return Py_BuildValue("i",item); \ +} + +#define MAKE_GET_ITEM_VOID_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + if (!PyArg_ParseTuple( args, ":" #mfcName)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + HTREEITEM item = pList->mfcName(); \ + GUI_END_SAVE; \ + if (item==NULL) \ + RETURN_ERR(#mfcName " failed"); \ + return Py_BuildValue("i",item); \ +} + +#define MAKE_SET_ITEMS_INTS_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM item; int i1, i2;\ + if (!PyArg_ParseTuple( args, "iii:" #mfcName, &item, &i1, &i2)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + BOOL ok = pList->mfcName(item, i1, i2); \ + GUI_END_SAVE; \ + if (!ok) \ + RETURN_ERR(#mfcName " failed"); \ + RETURN_NONE; \ +} + +#define MAKE_BOOL_ITEM_ACTION(fnname, mfcName) \ + PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM item; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &item)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + BOOL ok = pList->mfcName(item); \ + GUI_END_SAVE; \ + if (!ok) \ + RETURN_ERR(#mfcName " failed"); \ + RETURN_NONE; \ +} + +#define MAKE_BOOL_ITEM_INT_ACTION(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + HTREEITEM item; int code;\ + if (!PyArg_ParseTuple( args, "ii:" #mfcName, &item, &code)) \ + return NULL; \ + CTreeCtrl *pList = GetTreeCtrl(self); \ + if (!pList) return NULL; \ + GUI_BGN_SAVE; \ + BOOL ok = pList->mfcName(item, code); \ + GUI_END_SAVE; \ + if (!ok) \ + RETURN_ERR(#mfcName " failed"); \ + RETURN_NONE; \ +} + +#define MAKE_SETBOOL_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CTreeCtrl *pList = pList=GetTreeCtrl(self); \ + if (!pList) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + GUI_BGN_SAVE; \ + BOOL ok = pList->mfcName(val); \ + GUI_END_SAVE; \ + if (!ok) \ + RETURN_ERR(#mfcName "failed"); \ + RETURN_NONE; \ +} +#define MAKE_SETVOID_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CTreeCtrl *pList = pList=GetTreeCtrl(self); \ + if (!pList) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + GUI_BGN_SAVE; \ + pList->mfcName(val); \ + GUI_END_SAVE; \ + RETURN_NONE; \ +} + +// @pymethod int|PyCTreeCtrl|GetCount|Retrieves the number of tree items associated with a tree view control. +MAKE_GET_INT_METH(PyCTreeCtrl_GetCount, GetCount ) + +// @pymethod int|PyCTreeCtrl|GetVisibleCount|Retrieves the number of visible tree items associated with a tree view control. +MAKE_GET_INT_METH(PyCTreeCtrl_GetVisibleCount, GetVisibleCount ) + +// @pymethod int|PyCTreeCtrl|GetIndent|Retrieves the offset (in pixels) of a tree view item from its parent. +MAKE_GET_INT_METH(PyCTreeCtrl_GetIndent, GetIndent ) + +// @pymethod |PyCTreeCtrl|SetIndent|Sets the offset (in pixels) of a tree view item from its parent. +// @pyparm int|indent||The new indent. +MAKE_SETVOID_INT_METH(PyCTreeCtrl_SetIndent, SetIndent) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetNextItem|Retrieves the next item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_INT_METH(PyCTreeCtrl_GetNextItem, GetNextItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetChildItem|Retrieves the first child item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetChildItem, GetChildItem ) + +// @pymethod |PyCTreeCtrl|SetItemImage|Sets the index of an items images. +// @pyparm HTREEITEM|item||The specified item +// @pyparm int|iImage||The offset of the image. +// @pyparm int|iSelectedImage||The offset of the selected image. +MAKE_SET_ITEMS_INTS_METH(PyCTreeCtrl_SetItemImage, SetItemImage ) + +// @pymethod |PyCTreeCtrl|SetItemState|Sets the state of item. +// @pyparm HTREEITEM|item||The specified item +// @pyparm int|state||The new state +// @pyparm int|stateMask||The mask for the new state +MAKE_SET_ITEMS_INTS_METH(PyCTreeCtrl_SetItemState, SetItemState ) + +// @pymethod int|PyCTreeCtrl|ItemHasChildren|Returns nonzero if the specified item has child items. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_INT_METH(PyCTreeCtrl_ItemHasChildren, ItemHasChildren ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetNextSiblingItem|Retrieves the next sibling of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetNextSiblingItem, GetNextSiblingItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetPrevSiblingItem|Retrieves the previous sibling of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetPrevSiblingItem, GetPrevSiblingItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetParentItem|Retrieves the parent item of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetParentItem, GetParentItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetFirstVisibleItem|Retrieves the first visible item of the tree view control. +MAKE_GET_ITEM_VOID_METH(PyCTreeCtrl_GetFirstVisibleItem, GetFirstVisibleItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetNextVisibleItem|Retrieves the next visible item of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetNextVisibleItem, GetNextVisibleItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetPrevVisibleItem|Retrieves the previous visible item of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_GET_ITEM_ITEM_METH(PyCTreeCtrl_GetPrevVisibleItem, GetPrevVisibleItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetSelectedItem|Retrieves the currently selected tree view item. +MAKE_GET_ITEM_VOID_METH(PyCTreeCtrl_GetSelectedItem, GetSelectedItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetDropHilightItem|Retrieves the target of a drag-and-drop operation. +MAKE_GET_ITEM_VOID_METH(PyCTreeCtrl_GetDropHilightItem, GetDropHilightItem ) + +// @pymethod HTREEITEM|PyCTreeCtrl|GetRootItem|Retrieves the root of the specified tree view item. +MAKE_GET_ITEM_VOID_METH(PyCTreeCtrl_GetRootItem, GetRootItem ) + + +// @pymethod |PyCTreeCtrl|DeleteItem|Deletes the specified item. +// @pyparm HTREEITEM|item||The specified item +MAKE_BOOL_ITEM_ACTION(PyCTreeCtrl_DeleteItem, DeleteItem ) + +// @pymethod |PyCTreeCtrl|SelectItem|Selects a specified tree view item. +// @pyparm HTREEITEM|item||The specified item +MAKE_BOOL_ITEM_ACTION(PyCTreeCtrl_SelectItem, SelectItem ) + +// @pymethod |PyCTreeCtrl|SelectDropTarget|Redraws the tree item as the target of a drag-and-drop operation. +// @pyparm HTREEITEM|item||The specified item +MAKE_BOOL_ITEM_ACTION(PyCTreeCtrl_SelectDropTarget, SelectDropTarget ) + +// @pymethod |PyCTreeCtrl|SelectSetFirstVisible|Selects a specified tree view item as the first visible item. +// @pyparm HTREEITEM|item||The specified item +MAKE_BOOL_ITEM_ACTION(PyCTreeCtrl_SelectSetFirstVisible, SelectSetFirstVisible ) + +// @pymethod |PyCTreeCtrl|SortChildren|Sorts the children of a given parent item. +// @pyparm HTREEITEM|item||The specified parent item +MAKE_BOOL_ITEM_ACTION(PyCTreeCtrl_SortChildren, SortChildren ) + +// @pymethod |PyCTreeCtrl|Expand|Expands, or collapses, the child items of the specified tree view item. +// @pyparm HTREEITEM|item||The specified item +// @pyparm int|code||The action to take +MAKE_BOOL_ITEM_INT_ACTION(PyCTreeCtrl_Expand, Expand ) + +// @pymethod |PyCTreeCtrl|Select|Selects, scrolls into view, or redraws a specified tree view item. +// @pyparm HTREEITEM|item||The specified item +// @pyparm int|code||The action to take +MAKE_BOOL_ITEM_INT_ACTION(PyCTreeCtrl_Select, Select ) + +// @pymethod (int,int)|PyCTreeCtrl|GetItemImage|Retrieves the index of an items images. +// @pyparm HTREEITEM|item||The specified item +PyObject *PyCTreeCtrl_GetItemImage( PyObject *self, PyObject *args ) +{ + HTREEITEM item; + if (!PyArg_ParseTuple( args, "i:GetItemImage", &item)) + return NULL; + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + int res1, res2; + GUI_BGN_SAVE; + BOOL ok = pList->GetItemImage(item, res1, res2); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetItemImage failed"); + return Py_BuildValue("ii",res1, res2); +} + +// @pymethod (int,int)|PyCTreeCtrl|GetItemState|Retrieves the state and mask of an item. +// @pyparm HTREEITEM|item||The specified item +// @pyparm int|stateMask||The mask for the result. +PyObject *PyCTreeCtrl_GetItemState( PyObject *self, PyObject *args ) +{ + HTREEITEM item; UINT stateMask; + if (!PyArg_ParseTuple( args, "ii:GetItemState", &item, &stateMask)) + return NULL; + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + long state = pList->GetItemState(item, stateMask); + GUI_END_SAVE; + return PyInt_FromLong(state); +} + +// @pymethod |PyCTreeCtrl|GetImageList|Retrieves the current image list. +PyObject *PyCTreeCtrl_GetImageList( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList; + if (!(pList=GetTreeCtrl(self))) + return NULL; + int nList; + // @pyparm int|nImageList||Value specifying which image list to retrieve. It can be one of: + // - commctrl.LVSIL_NORMAL Image list with large icons. + // - commctrl.LVSIL_SMALL Image list with small icons. + // - commctrl.LVSIL_STATE Image list with state images. + if (!PyArg_ParseTuple(args, "i:GetImageList", &nList)) + return NULL; + GUI_BGN_SAVE; + CImageList *ret = pList->GetImageList(nList); + GUI_END_SAVE; + if (ret==NULL) + RETURN_ERR("There is no image list available"); + return ui_assoc_object::make( PyCImageList::type, ret)->GetGoodRet(); +} + + +// @pymethod int|PyCTreeCtrl|InsertItem|Inserts an item into the list. +PyObject *PyCTreeCtrl_InsertItem( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList; + HTREEITEM ret = NULL; + UINT mask; + int image, selImage, state, stateMask; + LPARAM lParam; + HTREEITEM hParent, hInsertAfter; + char *text; + if (!(pList=GetTreeCtrl(self))) + return NULL; + + if (PyArg_ParseTuple(args, "iziiiiOii:InsertItem", + &mask, // @pyparmalt1 int|mask||Integer specifying which attributes to set + &text, // @pyparmalt1 string|text||The text of the item. + &image, // @pyparmalt1 int|image||The index of the image to use. + &selImage, // @pyparmalt1 int|selectedImage||The index of the items selected image. + &state, // @pyparmalt1 int|state||The initial state of the item. + &stateMask, // @pyparmalt1 int|stateMask||Specifies which bits of the state are valid. + &lParam, // @pyparmalt1 object|lParam||A user defined object for the item. + &hParent, // @pyparmalt1 HTREEITEM|parent||The parent of the item. + &hInsertAfter)) { // @pyparmalt1 HTREEITEM|parent||The parent of the item. + GUI_BGN_SAVE; + ret = pList->InsertItem(mask, text, image, selImage, state, stateMask, lParam, hParent, hInsertAfter); + GUI_END_SAVE; + } else { + PyErr_Clear(); + hParent = TVI_ROOT; + hInsertAfter = TVI_LAST; + if (PyArg_ParseTuple(args, "sii|ii:InsertItem", + &text, // @pyparmalt2 string|text||The text for the item. + &image, // @pyparmalt2 int|image||The index of the image to use. + &selImage, // @pyparmalt2 int|selectedImage||The index of the items selected image. + &hParent, // @pyparmalt2 HTREEITEM|parent|commctrl.TVI_ROOT|The parent of the item. + &hInsertAfter)) { // @pyparmalt2 HTREEITEM|insertAfter|commctrl.TVI_LAST|The item to insert the new item after, or TVI_FIRST, TVI_LAST or TVI_SORT + GUI_BGN_SAVE; + ret = pList->InsertItem(text, image, selImage, hParent, hInsertAfter); + GUI_END_SAVE; + } else { + PyErr_Clear(); + hParent = TVI_ROOT; + hInsertAfter = TVI_LAST; + if (PyArg_ParseTuple(args, "s|ii:InsertItem", + &text, // @pyparmalt3 string|text||The text for the item. + &hParent, // @pyparmalt3 HTREEITEM|parent|commctrl.TVI_ROOT|The parent of the item. + &hInsertAfter)) {// @pyparmalt3 HTREEITEM|parent|commctrl.TVI_LAST|The parent of the item. + GUI_BGN_SAVE; + ret = pList->InsertItem(text, hParent, hInsertAfter); + GUI_END_SAVE; + } else { + PyObject *obTVItem; + TV_INSERTSTRUCT tvItem; + PyErr_Clear(); + if (PyArg_ParseTuple(args, "iiO:InsertItem", + &tvItem.hParent, // @pyparm HTREEITEM|hParent||The parent item. If commctrl.TVI_ROOT or 0, it is added to the root. + &tvItem.hInsertAfter, // @pyparm HTREEITEM|hInsertAfter||The item to insert after. Can be an item or TVI_FIRST, TVI_LAST or TVI_SORT + &obTVItem)) { // @pyparm |item||A tuple describing the new item. + + if (!ParseTV_ITEMTuple(obTVItem, &tvItem.item)) + return NULL; + GUI_BGN_SAVE; + ret = pList->InsertItem(&tvItem); + GUI_END_SAVE; + } else { + PyErr_Clear(); + RETURN_ERR("InsertItem could not parse the params."); + } + } + } + } + if (ret==NULL) + RETURN_ERR("InsertItem failed"); + return Py_BuildValue("i",ret); +} + +// @pymethod int|PyCTreeCtrl|SetItem|Sets some of all of an items attributes. +PyObject *PyCTreeCtrl_SetItem( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList; + PyObject *obTVItem; + if (!(pList=GetTreeCtrl(self))) + return NULL; + if (!PyArg_ParseTuple(args, "O:SetItem", + &obTVItem)) // @pyparm |item||A tuple describing the new item. + return NULL; + TV_ITEM tvItem; + if (!ParseTV_ITEMTuple(obTVItem, &tvItem)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItem(&tvItem); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItem failed"); + RETURN_NONE; +} + +// @pymethod int|PyCTreeCtrl|SetImageList|Assigns an image list to a list view control. +PyObject *PyCTreeCtrl_SetImageList( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList; + PyObject *obList; + int imageType; + if (!(pList=GetTreeCtrl(self))) + return NULL; + if (!PyArg_ParseTuple(args, "Oi:SetImageList", + &obList, // @pyparm |imageList||The Image List to use. + &imageType )) // @pyparm int|imageType||Type of image list. It can be one of (COMMCTRL.) LVSIL_NORMAL, LVSIL_SMALL or LVSIL_STATE + return NULL; + CImageList *pImageList = PyCImageList::GetImageList(obList); + if (pImageList==NULL) return NULL; + GUI_BGN_SAVE; + CImageList *pOldList = pList->SetImageList( pImageList, imageType ); + GUI_END_SAVE; + if (pOldList==NULL) + RETURN_NONE; + return ui_assoc_object::make( PyCImageList::type, pOldList )->GetGoodRet(); +} + +// @pymethod |PyCTreeCtrl|GetItem|Retrieves the details of an items attributes. +PyObject *PyCTreeCtrl_GetItem( PyObject *self, PyObject *args ) +{ + HTREEITEM item; + UINT mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; + + if (!PyArg_ParseTuple( args, "i|i:GetItem", + &item, // @pyparm HTREEITEM|item||The item whose attributes are to be retrieved. + &mask)) // @pyparm int|mask|(all flags set)|The requested attributes. + return NULL; + + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + char textBuf[256]; + TV_ITEM tvItem; + tvItem.hItem = item; + tvItem.pszText = textBuf; + tvItem.cchTextMax = sizeof(textBuf); + tvItem.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; + GUI_BGN_SAVE; + BOOL ok = pList->GetItem( &tvItem); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetItem failed"); + return MakeTV_ITEMTuple(&tvItem); +} + +// @pymethod int|PyCTreeCtrl|GetItemText|Retrieves the text of a list view item or subitem. +PyObject *PyCTreeCtrl_GetItemText( PyObject *self, PyObject *args ) +{ + HTREEITEM item; + if (!PyArg_ParseTuple( args, "i:GetItemText", + &item)) // @pyparm HTREEITEM|item||The item whose text is to be retrieved. + return NULL; + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + CString csText = pList->GetItemText(item); + GUI_END_SAVE; + return PyString_FromString((char *)(const char *)csText); +} + +// @pymethod int|PyCTreeCtrl|SetItemText|Changes the text of a list view item or subitem. +PyObject *PyCTreeCtrl_SetItemText( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + char *text; + if (!PyArg_ParseTuple( args, "is:SetItemText", + &item, // @pyparm HTREEITEM|item||The item whose text is to be retrieved. + &text)) // @pyparm string|text||String that contains the new item text. + + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItemText(item, text); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemText failed"); + RETURN_NONE; +} + +// @pymethod object|PyCTreeCtrl|GetItemData|Retrieves the application-specific value associated with an item. +PyObject *PyCTreeCtrl_GetItemData( PyObject *self, PyObject *args ) +{ + HTREEITEM item; + if (!PyArg_ParseTuple( args, "i:GetItemData", + &item)) // @pyparm HTREEITEM|item||The index of the item whose data is to be retrieved. + + return NULL; + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + long rc = pList->GetItemData(item); + GUI_END_SAVE; + PyObject *ret = PyWin_GetPythonObjectFromLong(rc); + // inc ref count for return value. + Py_XINCREF(ret); + return ret; +} + +// @pymethod int|PyCTreeCtrl|SetItemData|Sets the item's application-specific value. +PyObject *PyCTreeCtrl_SetItemData( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + PyObject *data; + if (!PyArg_ParseTuple( args, "iO:SetItemData", + &item, // @pyparm HTREEITEM|item||The item whose Data is to be set. + &data)) // @pyparm object|Data||New value for the data. + return NULL; + if (data==Py_None) data = NULL; + GUI_BGN_SAVE; + BOOL ok = pList->SetItemData(item, (DWORD)data); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemData failed"); + // @comm Note that a reference count is not added to the object. This it is your + // responsibility to make sure the object remains alive while in the list. + RETURN_NONE; +} + +// @pymethod object|PyCTreeCtrl|DeleteAllItems|Deletes all items in the control +PyObject *PyCTreeCtrl_DeleteAllItems( PyObject *self, PyObject *args ) +{ + if (!PyArg_ParseTuple( args, ":DeleteAllItems")) + return NULL; + CTreeCtrl *pList = GetTreeCtrl(self); + if (!pList) return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->DeleteAllItems(); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteAllItems failed"); + RETURN_NONE; +} + +// @pymethod (int, int, int, int)|PyCTreeCtrl|GetItemRect|Retrieves the bounding rectangle of a tree view item. +PyObject *PyCTreeCtrl_GetItemRect( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + RECT rect; + BOOL bTextOnly; + if (!PyArg_ParseTuple( args, "ii:GetItemRect", + &item, // @pyparm HTREEITEM|item||The item whose Data is to be set. + &bTextOnly)) // @pyparm int|bTextOnly||f this parameter is nonzero, the bounding rectangle includes only the text of the item. Otherwise it includes the entire line that the item occupies in the tree view control. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->GetItemRect(item, &rect, bTextOnly); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetItemRect failed"); + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod |PyCTreeCtrl|GetEditControl|Retrieves the handle of the edit control used to edit the specified tree view item. +PyObject *PyCTreeCtrl_GetEditControl( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + if (!PyArg_ParseTuple( args, ":GetEditControl")) + return NULL; + GUI_BGN_SAVE; + CEdit *pEdit = pList->GetEditControl(); + GUI_END_SAVE; + if (pEdit==NULL) + RETURN_ERR("GetEditControl failed"); + return ui_assoc_object::make(UITypeFromCObject(pEdit), pEdit)->GetGoodRet(); +} + +// @pymethod |PyCTreeCtrl|EditLabel|Edits a specified tree view item in-place. +PyObject *PyCTreeCtrl_EditLabel( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + // @pyparm HTREEITEM|item||The item to edit. + if (!PyArg_ParseTuple( args, "i:EditLabel", &item)) + return NULL; + GUI_BGN_SAVE; + CEdit *pEdit = pList->EditLabel(item); + GUI_END_SAVE; + if (pEdit==NULL) + RETURN_ERR("EditLabel failed"); + return ui_assoc_object::make(UITypeFromCObject(pEdit), pEdit)->GetGoodRet(); +} + +// @pymethod int|PyCTreeCtrl|EnsureVisible|Ensures that a tree view item is visible in its tree view control. +PyObject *PyCTreeCtrl_EnsureVisible( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + // @pyparm HTREEITEM|item||The item to edit. + if (!PyArg_ParseTuple( args, "i:EnsureVisible", &item)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pList->EnsureVisible(item); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("EnsureVisible failed"); + RETURN_NONE; +} + +// @pymethod |PyCTreeCtrl|CreateDragImage|Creates a dragging bitmap for the specified tree view item. +PyObject *PyCTreeCtrl_CreateDragImage( PyObject *self, PyObject *args ) +{ + CTreeCtrl *pList = pList=GetTreeCtrl(self); + if (!pList) return NULL; + HTREEITEM item; + // @pyparm HTREEITEM|item||The item to edit. + if (!PyArg_ParseTuple( args, "i:CreateDragImage", &item)) + return NULL; + GUI_BGN_SAVE; + CImageList *pIL = pList->CreateDragImage(item); + GUI_END_SAVE; + if (pIL==NULL) + RETURN_ERR("CreateDragImage failed"); + return ui_assoc_object::make(PyCImageList::type, pIL)->GetGoodRet(); +} + + +// @object PyCTreeCtrl|A class which encapsulates an MFC CTreeCtrl object. Derived from a object. +static struct PyMethodDef PyCTreeCtrl_methods[] = { + // Same order as MFC doco. + {"CreateWindow", PyCTreeCtrl_CreateWindow, 1}, // @pymeth CreateWindow|Creates the actual window for the object. + {"GetCount", PyCTreeCtrl_GetCount, 1}, // @pymeth GetCount|Retrieves the number of tree items associated with a tree view control. + {"GetIndent", PyCTreeCtrl_GetIndent, 1}, // @pymeth GetIndent|Retrieves the offset (in pixels) of a tree view item from its parent. + {"SetIndent", PyCTreeCtrl_SetIndent, 1}, // @pymeth SetIndent|Sets the offset (in pixels) of a tree view item from its parent. + {"GetImageList", PyCTreeCtrl_GetImageList, 1}, // @pymeth GetImageList|Retrieves the current image list. + {"SetImageList", PyCTreeCtrl_SetImageList, 1}, // @pymeth SetImageList|Assigns an image list to a list view control. + {"GetNextItem", PyCTreeCtrl_GetNextItem, 1}, // @pymeth GetNextItem|Retrieves the next item. + {"ItemHasChildren",PyCTreeCtrl_ItemHasChildren, 1}, // @pymeth ItemHasChildren|Returns nonzero if the specified item has child items. + {"GetChildItem", PyCTreeCtrl_GetChildItem, 1}, // @pymeth GetChildItem|Retrieves the child item of the specified tree view item. + {"GetNextSiblingItem",PyCTreeCtrl_GetNextSiblingItem, 1}, // @pymeth GetNextSiblingItem|Retrieves the next sibling of the specified tree view item. + {"GetPrevSiblingItem",PyCTreeCtrl_GetPrevSiblingItem, 1}, // @pymeth GetPrevSiblingItem|Retrieves the previous sibling of the specified tree view item. + {"GetParentItem",PyCTreeCtrl_GetParentItem, 1}, // @pymeth GetParentItem|Retrieves the parent item of the specified tree view item. + {"GetFirstVisibleItem",PyCTreeCtrl_GetFirstVisibleItem, 1}, // @pymeth GetFirstVisibleItem|Retrieves the first visible item of the specified tree view item. + {"GetNextVisibleItem",PyCTreeCtrl_GetNextVisibleItem, 1}, // @pymeth GetNextVisibleItem|Retrieves the next visible item of the specified tree view item. + {"GetPrevVisibleItem",PyCTreeCtrl_GetPrevVisibleItem, 1}, // @pymeth GetNextVisibleItem|Retrieves the previous visible item of the specified tree view item. + {"GetSelectedItem",PyCTreeCtrl_GetSelectedItem, 1}, // @pymeth GetSelectedItem|Retrieves the currently selected tree view item. + {"GetDropHilightItem",PyCTreeCtrl_GetDropHilightItem, 1}, // @pymeth GetDropHilightItem|Retrieves the target of a drag-and-drop operation. + {"GetRootItem", PyCTreeCtrl_GetRootItem, 1}, // @pymeth GetRootItem|Retrieves the root of the specified tree view item. + {"GetItem", PyCTreeCtrl_GetItem, 1}, // @pymeth GetItem|Retrieves the details of an items attributes. + {"SetItem", PyCTreeCtrl_SetItem, 1}, // @pymeth SetItem|Sets some of all of an items attributes. + {"GetItemState", PyCTreeCtrl_GetItemState, 1}, // @pymeth GetItemState|Retrieves the state of an item. + {"SetItemState", PyCTreeCtrl_SetItemState, 1}, // @pymeth SetItemState|Sets the state of an item. + {"GetItemImage", PyCTreeCtrl_GetItemImage, 1}, // @pymeth GetItemState|Retrieves the index of an items image. + {"SetItemImage", PyCTreeCtrl_SetItemImage, 1}, // @pymeth SetItemState|Sets the state of an item. + {"SetItemText", PyCTreeCtrl_SetItemText, 1}, // @pymeth SetItemText|Changes the text of a list view item or subitem. + {"GetItemText", PyCTreeCtrl_GetItemText, 1}, // @pymeth GetItemText|Retrieves the text of a list view item or subitem. + {"GetItemData", PyCTreeCtrl_GetItemData, 1}, // @pymeth GetItemState|Retrieves the object associated with an item. + {"SetItemData", PyCTreeCtrl_SetItemData, 1}, // @pymeth SetItemState|Sets the object associated with an item. + {"GetItemRect", PyCTreeCtrl_GetItemRect, 1}, // @pymeth GetItemRect|Retrieves the bounding rectangle of a tree view item. + {"GetEditControl", PyCTreeCtrl_GetEditControl, 1}, // @pymeth GetEditControl|Retrieves the handle of the edit control used to edit the specified tree view item. + {"GetVisibleCount", PyCTreeCtrl_GetVisibleCount, 1}, // @pymeth GetVisibleCount|Retrieves the number of visible tree items associated with a tree view control. + + {"InsertItem", PyCTreeCtrl_InsertItem, 1}, // @pymeth InsertItem|Inserts an item into the list. + {"DeleteItem", PyCTreeCtrl_DeleteItem, 1}, // @pymeth DeleteItem|Deletes an item from the list. + {"DeleteAllItems", PyCTreeCtrl_DeleteAllItems, 1}, // @pymeth DeleteAllItems|Deletes all items from the list. + {"Expand", PyCTreeCtrl_Expand, 1}, // @pymeth Expand|Expands, or collapses, the child items of the specified tree view item. + {"Select", PyCTreeCtrl_Select, 1}, // @pymeth Select|Selects, scrolls into view, or redraws a specified tree view item. + {"SelectItem", PyCTreeCtrl_SelectItem, 1}, // @pymeth SelectItem|Selects a specified tree view item. + {"SelectDropTarget",PyCTreeCtrl_SelectDropTarget, 1}, // @pymeth SelectDropTarget|Redraws the tree item as the target of a drag-and-drop operation. + {"SelectSetFirstVisible",PyCTreeCtrl_SelectSetFirstVisible, 1}, // @pymeth SelectSetFirstVisible|Selects a specified tree view item as the first visible item. + {"EditLabel", PyCTreeCtrl_EditLabel, 1}, // @pymeth EditLabel|Edits a specified tree view item in-place. + {"CreateDragImage",PyCTreeCtrl_CreateDragImage, 1}, // @pymeth CreateDragImage|Creates a dragging bitmap for the specified tree view item. + {"SortChildren", PyCTreeCtrl_SortChildren, 1}, // @pymeth SortChildren|Sorts the children of a given parent item. + {"EnsureVisible", PyCTreeCtrl_EnsureVisible, 1}, // @pymeth EnsureVisible|Ensures that a tree view item is visible in its tree view control. + {NULL, NULL} +}; +// @comm Sam Rushing has found the following tidbits: +// You can implement dynamic collapsing and expanding of events for large +// collections yourself - see KB Q130697 +// The MFC docs tell you to use TVE_COLLAPSERESET in order to +// throw away the child items when collapsing a node. They neglect to +// tell you a very important tidbit: that you need to combine the flag +// with TVE_COLLAPSE. This is pointed out in the docs for +// TreeView_Expand(), but not in those for CTreeCtrl::Expand. + +ui_type_CObject PyCTreeCtrl::type("PyCTreeCtrl", + &PyCWnd::type, + RUNTIME_CLASS(CTreeCtrl), + sizeof(PyCTreeCtrl), + PyCTreeCtrl_methods, + GET_PY_CTOR(PyCTreeCtrl)); diff --git a/Pythonwin/win32ctrlTree.h b/Pythonwin/win32ctrlTree.h new file mode 100644 index 0000000000..05ee8fb4af --- /dev/null +++ b/Pythonwin/win32ctrlTree.h @@ -0,0 +1,29 @@ +/* win32ctrltree : header + + Tree control object. + + Created May 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 + +*/ +/////////////////////////////////////////////////////////////////////// +// Control objects. +// +// PyCTreeCtrl +// +class PyCTreeCtrl : public PyCWnd { + +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCTreeCtrl) + +protected: + PyCTreeCtrl(); + virtual ~PyCTreeCtrl(); +}; diff --git a/Pythonwin/win32dbg.py b/Pythonwin/win32dbg.py new file mode 100644 index 0000000000..6b3976bdf5 --- /dev/null +++ b/Pythonwin/win32dbg.py @@ -0,0 +1,3 @@ +print "The win32dbg package has been moved to pywin.debugger" +print "Please move to the new name. The debugger is loading - please wait..." +from pywin.debugger import * diff --git a/Pythonwin/win32dc.cpp b/Pythonwin/win32dc.cpp new file mode 100644 index 0000000000..64f3914c98 --- /dev/null +++ b/Pythonwin/win32dc.cpp @@ -0,0 +1,2360 @@ +/* + + device context data types + + Created July 1994, Mark Hammond (MHammond@cmutual.com.au) + + These are implemented using CDC's, and hDC's in the map + +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 + +#include "win32dc.h" +#include "win32gdi.h" +#include "win32brush.h" +#include "win32font.h" +#include "win32pen.h" +#include "win32bitmap.h" +#include "win32rgn.h" + +// LOGPALETTE support. +BOOL PyObject_AsLOGPALETTE(PyObject *obLogPal, LOGPALETTE **ppLogPal) +{ + BOOL ok = FALSE; + if (!PySequence_Check(obLogPal)) { + PyErr_SetString(PyExc_TypeError, "LOGPALETTE must be a sequence"); + return FALSE; + } + int n = PySequence_Length(obLogPal); + *ppLogPal = (LOGPALETTE *)malloc(sizeof(LOGPALETTE) + n * sizeof(PALETTEENTRY)); + LOGPALETTE *pPal = *ppLogPal; + if (pPal==NULL) { + PyErr_SetString(PyExc_MemoryError, "Allocating LOGPALETTE"); + return FALSE; + } + + pPal->palVersion = 0x300; + pPal->palNumEntries = n; + + for (int i=0;ipalPalEntry[i].peRed, &pPal->palPalEntry[i].peGreen, &pPal->palPalEntry[i].peBlue, &pPal->palPalEntry[i].peFlags)) { + Py_XDECREF(subOb); + goto done; + } + Py_XDECREF(subOb); + } + ok = TRUE; +done: + if (!ok) { + free(pPal); + } + return ok; +} + +void PyObject_FreeLOGPALETTE(LOGPALETTE *pLogPal) +{ + if (pLogPal) free(pLogPal); +} + +PyObject *PyObject_FromLOGPALETTE( LOGPALETTE *pLP) +{ + PyObject *entries = PyTuple_New(pLP->palNumEntries); + for (int i=0;ipalNumEntries;i++) { + PyTuple_SET_ITEM(entries, i, Py_BuildValue("bbbb", + pLP->palPalEntry[i].peRed, + pLP->palPalEntry[i].peGreen, + pLP->palPalEntry[i].peBlue, + pLP->palPalEntry[i].peFlags)); + } + PyObject *rc = Py_BuildValue("lO", pLP->palVersion, entries); + Py_DECREF(entries); + return rc; +} + +// @pymethod int|win32ui|CreatePalette|Creates a HPALETTE +PyObject *win32uiCreatePalette(PyObject *self, PyObject *args) +{ + // @pyparm |lp||The entries for the palette. + PyObject *obLP; + if (!PyArg_ParseTuple(args, "O", &obLP)) + return NULL; + LOGPALETTE *pLP; + if (!PyObject_AsLOGPALETTE(obLP, &pLP)) + return NULL; + HPALETTE hp = CreatePalette(pLP); + PyObject_FreeLOGPALETTE(pLP); + if (hp==NULL) + RETURN_API_ERR("CreatePalette"); + return PyInt_FromLong((long)hp); +} + +// this returns a pointer that should not be stored. +CDC *ui_dc_object::GetDC(PyObject *self) +{ + return (CDC *)GetGoodCppObject( self, &type); +} + +void ui_dc_object::SetAssocInvalid() +{ + return; // do nothing. Dont call base as dont want my handle wiped. +} +void ui_dc_object::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + if (m_deleteDC) { + CDC *pDC = GetDC(this); + if (pDC) + ::DeleteDC (pDC->m_hDC); + } + ui_assoc_object::DoKillAssoc(bDestructing); +} + +ui_dc_object::~ui_dc_object() +{ + DoKillAssoc(TRUE); +} + +// @pymethod |win32ui|CreateDC|Creates an uninitialised device context. +PyObject *ui_dc_object::create_dc( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, CreateDC); + // create Python device context + CDC *pDC = new CDC; + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC)->GetGoodRet(); + return dc; +} + +// @pymethod |win32ui|CreateDCFromHandle|Creates a DC object from an integer handle. +PyObject *ui_create_dc_from_handle (PyObject *self, PyObject *args) +{ + HDC hDC; + if (!PyArg_ParseTuple(args,"i",&hDC)) + return NULL; + + CDC* pDC = CDC::FromHandle(hDC); + if (pDC==NULL) + RETURN_ERR ("Could not create DC."); + + // create Python device context + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC)->GetGoodRet(); + return dc; +} + + +// @pymethod |PyCDC|BitBlt|Copies a bitmap from the source device context to this device context. +static PyObject * +ui_dc_bitblt (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y, width, height, xsrc, ysrc; + DWORD rop; + PyObject *dc_ob; + if (!PyArg_ParseTuple (args, "(ii)(ii)O(ii)i", + &x, &y, // @pyparm (x,y)-ints|destPos||The logical x,y coordinates of the upper-left corner of the destination rectangle. + &width, &height, // @pyparm (width, height)-ints|size||Specifies the width and height (in logical units) of the destination rectangle and source bitmap. + &dc_ob, // @pyparm |dc||Specifies the PyCDC object from which the bitmap will be copied. It must be None if rop specifies a raster operation that does not include a source. + &xsrc, &ysrc, // @pyparm (xSrc, ySrc)-ints|srcPos||Specifies the logical x,y coordinates of the upper-left corner of the source bitmap. + &rop)) // @pyparm int|rop||Specifies the raster operation to be performed. See the win32 api documentation for details. + return NULL; + if (!ui_base_class::is_uiobject (dc_ob, &ui_dc_object::type)) + RETURN_TYPE_ERR("The 'O' param must be a PyCDC object"); + CDC *pSrcDC = NULL; + if (dc_ob!=Py_None) { + pSrcDC = ui_dc_object::GetDC(dc_ob); + if (!pSrcDC) + RETURN_ERR("The source DC is invalid"); + } + GUI_BGN_SAVE; + BOOL ok = pDC->BitBlt(x, y, width, height, pSrcDC, xsrc, ysrc, rop); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|BitBlt + RETURN_ERR("BitBlt failed"); + RETURN_NONE; +} + +// @pymethod |PyCDC|PatBlt|Creates a bit pattern on the device. +static PyObject * +ui_dc_patblt (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y, width, height; + DWORD rop; + if (!PyArg_ParseTuple (args, "(ii)(ii)i", + &x, &y, // @pyparm (x,y)-ints|destPos||The logical x,y coordinates of the upper-left corner of the destination rectangle. + &width, &height, // @pyparm (width, height)-ints|size||Specifies the width and height (in logical units) of the destination rectangle and source bitmap. + &rop)) // @pyparm int|rop||Specifies the raster operation to be performed. See the win32 api documentation for details. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pDC->PatBlt(x, y, width, height, rop); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|BitBlt + RETURN_ERR("PatBlt failed"); + RETURN_NONE; +} + + +// @pymethod |PyCDC|SetPixel|Sets a pixel in a device context +static PyObject * +ui_dc_setpixel (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y; + long color, rcolor; + + if (!PyArg_ParseTuple (args, "iil", + &x, // @pyparm int|x||Horizontal coordinate. + &y, // @pyparm int|y||Vertical coordinate. + &color)) { // @pyparm int|color||The brush color. + return NULL; + } + GUI_BGN_SAVE; + rcolor = pDC->SetPixel (x, y, color); + GUI_END_SAVE; + if (rcolor < 0) + RETURN_ERR ("SetPixel failed"); + return Py_BuildValue ("l", rcolor); +} + +// @pymethod int|PyCDC|GetSafeHdc|Returns the HDC of this DC object. +static PyObject * +ui_dc_get_safe_hdc(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + // @pyseemfc CDC|GetSafeHdc + HDC hdc = pDC->GetSafeHdc(); + return Py_BuildValue("l", (long)hdc); +} + +// @pymethod |PyCDC|GetPixel|Gets a pixel at a local in a device context +static PyObject * +ui_dc_get_pixel (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y; + long rcolor; + + if (!PyArg_ParseTuple (args, "ii", + &x, // @pyparm int|x||Horizontal coordinate. + &y)) { // @pyparm int|y||Vertical coordinate. + return NULL; + } + + GUI_BGN_SAVE; + rcolor = pDC->GetPixel (x, y); + GUI_END_SAVE; + if (rcolor < 0) + RETURN_ERR ("GetPixel failed"); + return Py_BuildValue ("l", rcolor); +} + + +// @pymethod (x, y)|PyCDC|GetCurrentPosition|Retrieves the current position (in logical coordinates). +static PyObject * +ui_dc_get_current_position (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CHECK_NO_ARGS(args); + CPoint pt; + + pt = pDC->GetCurrentPosition (); + + return Py_BuildValue ("(ii)", pt.x, pt.y); +} + +// @pymethod |PyCDC|Pie|Draws a pie slice in a device context +static PyObject * +ui_dc_pie (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x1, y1, x2, y2, x3, y3, x4, y4; + + if (!PyArg_ParseTuple (args, "iiiiiiii", + &x1, // @pyparm int|x1||X coordinate of upper left corner + &y1, // @pyparm int|y1||Y coordinate of upper left corner + &x2, // @pyparm int|x2||X coordinate of lower right corner + &y2, // @pyparm int|y2||Y coordinate of lower right corner + &x3, // @pyparm int|x3||X coordinate of starting point of arc + &y3, // @pyparm int|y3||Y coordinate of starting point of arc + &x4, // @pyparm int|x4||X coordinate of ending point of arc + &y4)) { // @pyparm int|y4||Y coordinate of ending point of arc + return NULL; + } + GUI_BGN_SAVE; + int rc = pDC->Pie (x1, y1, x2, y2, x3, y3, x4, y4); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); +} + +// @pymethod |PyCDC|CreateCompatibleDC|Creates a memory device context that is compatible with this DC. +PyObject *ui_dc_object::create_compatible_dc( PyObject *self, PyObject *args ) +{ + // @comm Note that unlike the MFC version, this function + // calls the global CreateCompatibleDC function and returns + // a new object. + PyObject *obDCFrom = Py_None; + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + if (!PyArg_ParseTuple(args, "|O:CreateCompatibleDC", &obDCFrom )) + return NULL; // @pyparm |dcFrom|None|The source DC, or None to make a screen compatible DC. + CDC *dcFrom = NULL; + if (obDCFrom!=Py_None) + dcFrom = GetDC(obDCFrom); + HDC hDC = NULL; + if (dcFrom) { + hDC = dcFrom->GetSafeHdc(); + } + GUI_BGN_SAVE; + HDC hcDC = ::CreateCompatibleDC(hDC); + GUI_END_SAVE; + if (!hcDC) // @pyseemfc CDC|CreateCompatibleDC + RETURN_ERR("CreateCompatibleDC failed"); + // create Python device context + CDC *pcDC = pDC->FromHandle(hcDC); + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pcDC)->GetGoodRet(); + return dc; +} + +// @pymethod |PyCDC|CreatePrinterDC|Creates a device context for a specific printer +PyObject *ui_dc_object::create_printer_dc( PyObject *self, PyObject *args ) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + char *printerName = NULL; + if (!PyArg_ParseTuple(args, "|z:CreatePrinterDC", &printerName )) + return NULL; // @pyparm string|printerName|None|The printer name, or None for the default printer + + BOOL result; + if (printerName == NULL) + { + // Attempt to open the default printer + CPrintInfo info; + if (!AfxGetApp()->GetPrinterDeviceDefaults(&(info.m_pPD->m_pd))) + { + RETURN_ERR("No default printer found"); + return NULL; + } + + if (info.m_pPD->m_pd.hDC == NULL && !info.m_pPD->CreatePrinterDC()) + { + result = FALSE; + } + else + { + result = pDC->Attach(info.m_pPD->m_pd.hDC); + info.m_pPD->m_pd.hDC = NULL; // Prevent this DC from being deleted + } + } + else + { + // Attempt to open a specific printer + HANDLE hPrinter; + if (!::OpenPrinter(printerName, &hPrinter, NULL)) + { + RETURN_ERR("Unable to open printer"); + return NULL; + } + + DWORD len; + unsigned char buf; + ::GetPrinter(hPrinter, 2, &buf, 1, &len); + unsigned char *buffer = new unsigned char[len]; + result = ::GetPrinter(hPrinter, 2, buffer, len, &len); + ::ClosePrinter(hPrinter); + if (!result) + { + RETURN_ERR("Unable to get printer info"); + delete [] buffer; + return NULL; + } + + PRINTER_INFO_2 *pinfo = (PRINTER_INFO_2*)buffer; + GUI_BGN_SAVE; + result = pDC->CreateDC(pinfo->pDriverName, pinfo->pPrinterName, NULL, NULL); // @pyseemfc CDC|CreateDC + GUI_END_SAVE; + delete [] buffer; + } + + if (!result) + RETURN_ERR("CreateDC failed"); + RETURN_NONE; +} + +// @pymethod |PyCDC|DeleteDC|Deletes all resources associated with a device context. +static PyObject * +ui_dc_delete_dc (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CHECK_NO_ARGS2(args, DeleteDC); + GUI_BGN_SAVE; + BOOL ok = pDC->DeleteDC(); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteDC failed"); + RETURN_NONE; + // @comm In general, do not call this function; the destructor will do it for you. + // An application should not call DeleteDC if objects have been selected into the device context. Objects must first be selected out of the device context before it it is deleted. + // An application must not delete a device context whose handle was obtained by calling CWnd::GetDC. Instead, it must call CWnd::ReleaseDC to free the device context. + // The DeleteDC function is generally used to delete device contexts created with CreateDC, CreateIC, or CreateCompatibleDC. +} + +// @pymethod |PyCDC|DrawIcon|Draws an icon on the DC. +static PyObject * +ui_dc_draw_icon (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + // @pyparm (x,y)|point||The point coordinate to draw to. + // @pyparm int|hIcon||The handle of the icon to draw. + int x, y; + HICON hIcon; + if (!PyArg_ParseTuple (args, "(ii)i", &x, &y, &hIcon)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pDC->DrawIcon (x,y, hIcon); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|DrawIcon + RETURN_ERR ("DrawIcon failed"); + else + RETURN_NONE; +} +// @pymethod |PyCDC|DrawFocusRect|Draws a rectangle in the style used to +// indicate the rectangle has focus +static PyObject * +ui_dc_draw_focus_rect (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + CRect rect; + if (!pDC) { + return NULL; + // @pyparm (left, top, right, bottom)|rect||The coordinates of the + // rectangle + } else if (!PyArg_ParseTuple (args, + "(iiii)", + &rect.left, &rect.top, + &rect.right, &rect.bottom)) { + return NULL; + } else { + // it's a void function + GUI_BGN_SAVE; + pDC->DrawFocusRect (rect); // @pyseemfc CDC|DrawFocusRect + GUI_END_SAVE; + RETURN_NONE; + } +} + +//@pymethod |PyCDC|ExtTextOut|Writes text to the DC. +static PyObject *ui_dc_ext_text_out (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + char *text; + int strLen, x, y; + UINT options; + PyObject *rectObject, *widthObject = NULL; + RECT rect, *rectPtr; + int *widths = NULL; + if (!PyArg_ParseTuple (args, "iiiOs#|O", + &x, // @pyparm x|int||The x coordinate to write the text to. + &y, // @pyparm y|int||The y coordinate to write the text to. + &options, // @pyparm nOptions|int||Specifies the rectangle type. This parameter can be one, both, or neither of ETO_CLIPPED and ETO_OPAQUE + &rectObject,// @pyparm (left, top, right, bottom)|rect||Specifies the text's bounding rectangle. (Can be None.) + &text, // @pyparm text|string||The text to write. + &strLen, + &widthObject)) // @pyparm (width1, width2, ...)|tuple||Optional array of values that indicate distance between origins of character cells. + { + return NULL; + } + + // Parse out rectangle object + if (rectObject != Py_None) { + if (!PyArg_ParseTuple(rectObject, "iiii", &rect.left, + &rect.top, &rect.right, &rect.bottom)) + return NULL; + rectPtr = ▭ + } + else + rectPtr = NULL; + + // Parse out widths + if (widthObject) { + BOOL error = !PyTuple_Check(widthObject); + if (!error) { + int len = PyTuple_Size(widthObject); + if (len == (strLen - 1)) { + widths = new int[len + 1]; + for (int i = 0; i < len; i++) { + PyObject *item = PyTuple_GetItem(widthObject, i); + if (!PyInt_Check(item)) + error = TRUE; + else + widths[i] = PyInt_AsLong(item); + } + } + } + if (error) { + delete [] widths; + RETURN_TYPE_ERR("The width param must be a tuple of integers with a length one less than that of the string"); + } + } + + GUI_BGN_SAVE; + BOOL ret = pDC->ExtTextOut(x, y, options, rectPtr, text, strLen, widths); // @pyseemfc CDC|ExtTextOut + GUI_END_SAVE; + delete [] widths; + if (!ret) { + RETURN_API_ERR("CDC::TextOut"); + } + RETURN_NONE; + // @rdesc Always none. If the function fails, an exception is raised. +} + +// @pymethod int|PyCDC|RectVisible|Determines whether any part of the given rectangle lies within the clipping region of the display context. +static PyObject *ui_dc_rect_visible( PyObject *self, PyObject *args ) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CRect rect; + // @pyparm (left, top, right, bottom)|rect||The coordinates of the reactangle to be checked. + if (!PyArg_ParseTuple(args,"(iiii)", &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + GUI_BGN_SAVE; + int rc = pDC->RectVisible(&rect); + GUI_END_SAVE; + return Py_BuildValue("i",rc); // @pyseemfc CDC|RectVisible + // @rdesc Non zero if any part of the rectangle lies within the clipping region, else zero. +} + +// @pymethod |PyCDC|Arc|Draws an eliptical arc. +static PyObject *ui_dc_arc (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CRect rect; + POINT pts, pte; + if (!PyArg_ParseTuple(args,"(iiii)(ii)(ii):Arc", + // @pyparm (left, top, right, bottom)|rect||Specifies the ellipse's bounding rectangle + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (x,y)|pointStart||Specifies the x- and y-coordinates + // of the point that defines the arc's starting point (in logical units). + // This point does not have to lie exactly on the arc. + &pts.x, &pts.y, + // @pyparm (x,y)|pointEnd||Specifies the x- and y-coordinates + // of the point that defines the arc's ending point (in logical units). + // This point does not have to lie exactly on the arc. + &pte.x, &pte.y )) + return NULL; + GUI_BGN_SAVE; + BOOL ret = pDC->Arc(&rect, pts, pte); // @pyseemfc CDC|Arc + GUI_END_SAVE; + if (!ret) + RETURN_API_ERR("CDC::Arc"); + RETURN_NONE; + // @rdesc Always none. If the function fails, an exception is raised. + // @comm The arc drawn by using the function is a segment of the ellipse defined by the specified bounding rectangle. + // The actual starting point of the arc is the point at which a ray drawn + // from the center of the bounding rectangle through the specified starting + // point intersects the ellipse. The actual ending point of the arc is the + // point at which a ray drawn from the center of the bounding rectangle through + // the specified ending point intersects the ellipse. The arc is drawn in a + // counterclockwise direction. Since an arc is not a closed figure, it is + // not filled. Both the width and height of the rectangle must be greater + // than 2 units and less than 32,767 units. +} + +// @pymethod |PyCDC|Chord|Draws a chord. +static PyObject *ui_dc_chord (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CRect rect; + POINT pts, pte; + if (!PyArg_ParseTuple(args,"(iiii)(ii)(ii):Chord", + // @pyparm (left, top, right, bottom)|rect||Specifies the ellipse's bounding rectangle + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (x,y)|pointStart||Specifies the x- and y-coordinates + // of the point that defines the arc's starting point (in logical units). + // This point does not have to lie exactly on the arc. + &pts.x, &pts.y, + // @pyparm (x,y)|pointEnd||Specifies the x- and y-coordinates + // of the point that defines the arc's ending point (in logical units). + // This point does not have to lie exactly on the arc. + &pte.x, &pte.y )) + return NULL; + GUI_BGN_SAVE; + BOOL ret = pDC->Chord(&rect,pts,pte); // @pyseemfc CDC|Chord + GUI_END_SAVE; + if (!ret) + RETURN_API_ERR("CDC::Chord"); + RETURN_NONE; + // @rdesc Always none. If the function fails, an exception is raised. + // @comm Draws a chord (a closed figure bounded by the intersection + // of an ellipse and a line segment). The rect parameter specify the + // upper-left and lower-right corners, respectively, of a rectangle + // bounding the ellipse that is part of the chord. + // The pointStart and pointEnd parameters specify + // the endpoints of a line that intersects the ellipse. + // The chord is drawn by using the selected pen and filled + // by using the selected brush. +} + +// @pymethod |PyCDC|Ellipse|Draws an Ellipse. +static PyObject *ui_dc_ellipse (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CRect rect; + // @pyparm (left, top, right, bottom)|rect||Specifies the ellipse's bounding rectangle + if (!PyArg_ParseTuple(args,"(iiii):Ellipse", &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + GUI_BGN_SAVE; + BOOL ret = pDC->Ellipse(rect); // @pyseemfc CDC|Ellipse + GUI_END_SAVE; + if (!ret) + RETURN_API_ERR("CDC::Ellipse"); + RETURN_NONE; + // @rdesc Always none. If the function fails, an exception is raised. + // @comm The center of the ellipse is the center of the bounding rectangle + // specified by rect. The ellipse is drawn with the current pen, and its + // interior is filled with the current brush. +} + + + +// @pymethod |PyCDC|Polygon|Draws an Polygon. +static PyObject *ui_dc_polygon (PyObject *self, PyObject *args) +{ + PyObject * point_list; + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + if (!PyArg_ParseTuple(args, + "O:Polygon", + &point_list)) { + return NULL; + } else if (!PyList_Check(point_list)) { + return NULL; + } else { + // Convert the list of point tuples into an array of POINT structs + int num = PyList_Size (point_list); + POINT * point_array = new POINT[num]; + for (int i=0; i < num; i++) { + PyObject * point_tuple = PyList_GetItem (point_list, i); + if (!PyTuple_Check (point_tuple) || PyTuple_Size (point_tuple) != 2) { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } else { + long x, y; + PyObject *px, *py; + px = PyTuple_GetItem (point_tuple, 0); + py = PyTuple_GetItem (point_tuple, 1); + if ((!PyInt_Check(px)) || (!PyInt_Check(py))) { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } else { + x = PyInt_AsLong (px); + y = PyInt_AsLong (py); + point_array[i].x = x; + point_array[i].y = y; + } + } + } + // we have an array of POINT structs, now we + // can finally draw the polygon. + GUI_BGN_SAVE; + BOOL ret = pDC->Polygon (point_array, num); + GUI_END_SAVE; + delete[] point_array; + if (!ret) { + RETURN_API_ERR("CDC::Polygon"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|PolyBezier|Draws one or more Bezier splines. +static PyObject *ui_dc_poly_bezier (PyObject *self, PyObject *args) +{ + PyObject * triple_list; + int do_to=0; + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + if (!PyArg_ParseTuple(args, + "O|i:PolyBezier[To]", + &triple_list, + &do_to)) { + return NULL; + } else if (!PyList_Check(triple_list)) { + return NULL; + } else { + int index = 0; + int num = PyList_Size (triple_list); + +#define HURL \ + do { \ + PyErr_SetString (PyExc_ValueError, \ + "arg must be a list of 3-tuples of (x,y) tuples"); \ + delete[] point_array; \ + return NULL; \ + } \ + while (0) + + POINT * point_array = new POINT[num*3]; + for (int i=0; i < num; i++) { + PyObject * triplet = PyList_GetItem (triple_list, i); + if (!PyTuple_Check (triplet) || PyTuple_Size (triplet) != 3) { + HURL; + } else { + for (int j=0; j < 3; j++) { + PyObject *point = PyTuple_GetItem (triplet, j); + if (!PyTuple_Check (point) || PyTuple_Size (point) != 2) { + HURL; + } else { + PyObject *px, *py; + px = PyTuple_GetItem (point, 0); + py = PyTuple_GetItem (point, 1); + if (!PyInt_Check(px) || !PyInt_Check(py)) { + HURL; + } else { + point_array[index].x = PyInt_AsLong (px); + point_array[index].y = PyInt_AsLong (py); + index++; + } + } + } + } + } + // we have an array of POINT structs, now we + // can finally draw the splines.. + BOOL result; + if (do_to) { + result = pDC->PolyBezierTo (point_array, index); + } else { + result = pDC->PolyBezier (point_array, index); + } + delete[] point_array; + if (!result) { + RETURN_API_ERR("CDC::PolyBezier[To]"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|FillRect|Fills a given rectangle with the specified brush +static PyObject * +ui_dc_fillrect (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + RECT rect; + PyObject *obBrush; + if (!PyArg_ParseTuple (args, "(iiii)O:FillRect", + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (left, top, right, bottom|rect||Specifies the bounding rectangle, in logical units. + &obBrush)) // @pyparm |brush||Specifies the brush to use. + return NULL; + if (!ui_base_class::is_uiobject (obBrush, &PyCBrush::type)) + RETURN_TYPE_ERR("The 'O' param must be a PyCBrush object"); + CBrush *pBrush = PyCBrush::GetBrush(obBrush); + if (!pBrush) + return NULL; + GUI_BGN_SAVE; + pDC->FillRect( &rect, pBrush ); + GUI_END_SAVE; + // @pyseemfc CDC|FillRect + RETURN_NONE; +} + +// @pymethod |PyCDC|FillSolidRect|Fills the given rectangle with the specified solid color. +static PyObject * +ui_dc_fillsolidrect (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + RECT rect; + int col; + if (!PyArg_ParseTuple (args, "(iiii)i:FillSolidRect", + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (left, top, right, bottom|rect||Specifies the bounding rectangle, in logical units. + &col)) // @pyparm int|color||Specifies the color to use. + return NULL; + GUI_BGN_SAVE; + pDC->FillSolidRect( &rect, (COLORREF)col ); + GUI_END_SAVE; + // @pyseemfc CDC|FillSolidRect + RETURN_NONE; +} + +// @pymethod |PyCDC|FrameRect|Draws a border around the rectangle specified by rect +static PyObject * +ui_dc_framerect (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + RECT rect; + PyObject *obBrush; + if (!PyArg_ParseTuple (args, "(iiii)O:FrameRect", + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (left, top, right, bottom|rect||Specifies the bounding rectangle, in logical units. + &obBrush)) // @pyparm |brush||Specifies the brush to use. + return NULL; + if (!ui_base_class::is_uiobject (obBrush, &PyCBrush::type)) + RETURN_TYPE_ERR("The 'O' param must be a PyCBrush object"); + CBrush *pBrush = PyCBrush::GetBrush(obBrush); + if (!pBrush) + RETURN_ERR("The PyCBrush parameter is invalid."); + GUI_BGN_SAVE; + pDC->FrameRect( &rect, pBrush ); + GUI_END_SAVE; + // @pyseemfc CDC|FrameRect + RETURN_NONE; +} + +// @pymethod |PyCDC|Draw3dRect|Draws a three-dimensional rectangle. +static PyObject * +ui_dc_draw3drect(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + RECT rect; + int ctl, cbr; + if (!PyArg_ParseTuple (args, "(iiii)ii:Draw3dRect", + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (left, top, right, bottom|rect||Specifies the bounding rectangle, in logical units. + &ctl, // @pyparm int|colorTopLeft||Specifies the color of the top and left sides of the three-dimensional rectangle. + &cbr)) // @pyparm int|colorBotRight||Specifies the color of the bottom and right sides of the three-dimensional rectangle. + return NULL; + GUI_BGN_SAVE; + pDC->Draw3dRect( &rect, ctl, cbr); + GUI_END_SAVE; + // @pyseemfc CDC|Draw3dRect + RETURN_NONE; +} + +// @pymethod int|PyCDC|GetNearestColor|Returns the closest color a device can map. +static PyObject *ui_dc_get_nearest_color (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int col; + // @pyparm int|color||Specifies the color to be matched. + if (!PyArg_ParseTuple (args, "i:GetNearestColor", &col)) + return NULL; + GUI_BGN_SAVE; + int rc = pDC->GetNearestColor(col); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); +} + +// @pymethod (x,y)|PyCDC|GetTextExtentPoint|An alias for . +// GetTextExtentPoint is the preferred win32api name, but GetTextExtent is the MFC name. +// Calculates the width and height of a line of text using the current font to determine the dimensions. +// @pyparm string|text||The text to calculate for. +// @rdesc A tuple of integers with the size of the string, in logical units. + +// @pymethod (x,y)|PyCDC|GetTextExtent|Calculates the width and height of a line of text using the current font to determine the dimensions. +static PyObject *ui_dc_get_text_extent (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + char *text; + int strLen; + // @pyparm string|text||The text to calculate for. + if (!PyArg_ParseTuple (args, "s#", &text, &strLen)) + return NULL; + GUI_BGN_SAVE; + CSize sz = pDC->GetTextExtent(text, strLen); // @pyseemfc CFC|GetTextExtent + GUI_END_SAVE; + return Py_BuildValue ("(ii)", sz.cx, sz.cy); + // @rdesc A tuple of integers with the size of the string, in logical units. +} + +// @pymethod int|PyCDC|SetTextColor|Sets the text color to the specified color. +static PyObject * +ui_dc_set_text_color (PyObject *self, PyObject *args) +{ + // @comm This text color is used when writing text to this device context and also when converting bitmaps between color and monochrome device contexts. + // If the device cannot represent the specified color, the system sets the text color to the nearest physical color. + // The background color for a character is specified by the SetBkColor and SetBkMode member functions. + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + int new_color; + // @pyparm int|color||A windows color specification. See the win32api documentation for details. + if (!PyArg_ParseTuple (args, "i", &new_color)) + return NULL; + + GUI_BGN_SAVE; + int old_color = pDC->SetTextColor (new_color); // @pyseemfc CDC|SetTextColor + GUI_END_SAVE; + return Py_BuildValue ("i", old_color); + // @rdesc The return value is the previous text color. +} + +// @pymethod int|PyCDC|SetBkColor|Sets the current background color to the specified color. +static PyObject * +ui_dc_set_bk_color (PyObject *self, PyObject *args) +{ + // @comm If the background mode is OPAQUE, the system uses the background color + // to fill the gaps in styled lines, the gaps between hatched lines in brushes, and + // the background in character cells. + // The system also uses the background color when converting bitmaps between color and + // monochrome device contexts. + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int new_color; + if (!PyArg_ParseTuple (args, "i", &new_color)) // @pyparm int|color||A windows color specification. See the win32api documentation for details. + return NULL; + GUI_BGN_SAVE; + int old_color = pDC->SetBkColor (new_color); // @pyseemfc CDC|SetBkColor + GUI_END_SAVE; + return Py_BuildValue ("i", old_color); + // @rdesc The return value is the previous background color. +} + +// @pymethod int|PyCDC|SetBkMode|Sets the current background mode to the specified mode. +static PyObject * +ui_dc_set_bk_mode (PyObject *self, PyObject *args) +{ + // @comm Specifies the mode to be set. This parameter can be either OPAQUE or TRANSPARENT + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int new_mode; + if (!PyArg_ParseTuple (args, "i", &new_mode)) // @pyparm int|mode||A background mode. May be either TRANSPARENT or OPAQUE. + return NULL; + GUI_BGN_SAVE; + int old_mode = pDC->SetBkMode(new_mode); // @pyseemfc CDC|SetBkMode + GUI_END_SAVE; + return Py_BuildValue ("i", old_mode); + // @rdesc The return value is the previous background mode. +} + +// @pymethod (int, int)|PyCDC|SetBrushOrg|Specifies the origin that GDI will assign to the next brush that the application selects into the device context. +static PyObject * +ui_dc_set_brush_org (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y; + // @pyparm (x,y)|point||The new origin in device units. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + GUI_BGN_SAVE; + CPoint pt = pDC->SetBrushOrg( x, y ); // @pyseemfc CDC|SetBrushOrg + GUI_END_SAVE; + return Py_BuildValue ("(ii)", pt.x, pt.y); + // @rdesc The previous origin in device units. +} + +// @pymethod (int,int)|PyCDC|GetBrushOrg|Retrieves the origin (in device units) of the brush currently selected for the device context. +static PyObject * +ui_dc_get_brush_org(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int item; + if (!PyArg_ParseTuple (args, ":GetBrushOrg", &item)) + return NULL; + GUI_BGN_SAVE; + CPoint pt = pDC->GetBrushOrg();// @pyseemfc CDC|GetBrushOrg + GUI_END_SAVE; + return Py_BuildValue ("ii", pt.x, pt.y); +} + + +// @pymethod int|PyCDC|GetDeviceCaps|Retrieves a capability of the device context. +static PyObject * +ui_dc_get_device_caps (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int item; + if (!PyArg_ParseTuple (args, "i", &item)) // @pyparm int|index||The information requested. See the win32api documentation for details. + return NULL; + GUI_BGN_SAVE; + int value = pDC->GetDeviceCaps (item); // @pyseemfc CDC|GetDeviceCaps + GUI_END_SAVE; + return Py_BuildValue ("i", value); + // @rdesc The value of the requested capability +} + +// @pymethod int|PyCDC|SetMapMode|Sets the mapping mode for the device context. +static PyObject * +ui_dc_set_map_mode (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int new_mode; + if (!PyArg_ParseTuple (args, "i", &new_mode)) + // @pyparm int|newMode||The new mode. Can be one of + // MM_ANISOTROPIC, MM_HIENGLISH, MM_HIMETRIC, MM_ISOTROPIC, MM_LOENGLISH, MM_LOMETRIC, MM_TEXT, MM_TWIPS + return NULL; + GUI_BGN_SAVE; + int old_mode = pDC->SetMapMode (new_mode); // @pyseemfc CDC|SetMapMode + GUI_END_SAVE; + if (old_mode == 0) + RETURN_ERR ("SetMapMode failed"); + else + return Py_BuildValue ("i", old_mode); + // @rdesc The previous mapping mode. +} + +// @pymethod int|PyCDC|GetMapMode|Gets the mapping mode for the device context. +static PyObject * +ui_dc_get_map_mode (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + if (!PyArg_ParseTuple (args, "")) + return NULL; + GUI_BGN_SAVE; + int mode = pDC->GetMapMode (); // @pyseemfc CDC|GetMapMode + GUI_END_SAVE; + if (mode == 0) + RETURN_ERR ("GetMapMode failed"); + return PyInt_FromLong(mode); +} + +// @pymethod x, y|PyCDC|SetWindowOrg|Sets the window origin of the device context +static PyObject * +ui_dc_set_window_org (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + int x, y; + // @pyparm int, int|x,y||The new origin. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize old_size = pDC->SetWindowOrg (x, y); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", old_size.cx, old_size.cy); + } + } +} + +// @pymethod x, y|PyCDC|GetWindowOrg|Retrieves the x- and y-coordinates of the origin of the window associated with the device context. +static PyObject * +ui_dc_get_window_org (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize org = pDC->GetWindowOrg (); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", org.cx, org.cy); + } + } +} + +// @pymethod x, y|PyCDC|SetViewportOrg|Sets the viewport origin of the device context +static PyObject * +ui_dc_set_viewport_org (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + int x, y; + // @pyparm int, int|x,y||The new origin. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize old_size = pDC->SetViewportOrg (x, y); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", old_size.cx, old_size.cy); + } + } +} + +// @pymethod x, y|PyCDC|GetViewportOrg|Gets the viewport origin of the device context +static PyObject * +ui_dc_get_viewport_org (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize org = pDC->GetViewportOrg (); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", org.cx, org.cy); + } + } +} + +// @pymethod x, y|PyCDC|GetViewportExt|Gets the viewport extent of the device context +static PyObject * +ui_dc_get_viewport_ext (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize ext = pDC->GetViewportExt (); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", ext.cx, ext.cy); + } + } +} + +// @pymethod x, y|PyCDC|GetWindowExt|Gets the window extent of the device context +static PyObject * +ui_dc_get_window_ext (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize ext = pDC->GetWindowExt (); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", ext.cx, ext.cy); + } + } +} + +// @pymethod int|PyCDC|SetGraphicsMode|Sets the graphics mode for the specified device context +static PyObject * +ui_dc_set_graphics_mode (PyObject * self, PyObject * args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + int mode; + // @pyparm int|mode||The new mode. + if (!PyArg_ParseTuple (args, "i", &mode)) { + return NULL; + } else { + return Py_BuildValue ("i", SetGraphicsMode (pDC->GetSafeHdc(), mode)); + } + } +} + +// @pymethod int|PyCDC|SetWorldTransform|sets a two-dimensional linear transformation between world space and page space for the specified device context. This transformation can be used to scale, rotate, shear, or translate graphics output. +static PyObject * +ui_dc_set_world_transform (PyObject * self, PyObject * args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + XFORM xf; + if (!PyArg_ParseTuple (args, + "ffffff", + &xf.eM11, + &xf.eM12, + &xf.eM21, + &xf.eM22, + &xf.eDx, + &xf.eDy + )) { + return NULL; + } else { + return Py_BuildValue ("i", SetWorldTransform (pDC->GetSafeHdc(), &xf)); + } + } +} + +// @pymethod (x,y)|PyCDC|SetWindowExt|Sets the x,y extents of the window associated with the device context. +static PyObject * +ui_dc_set_window_ext (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y; + // @pyparm (x,y)|size||The new size. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + GUI_BGN_SAVE; + CSize old_size = pDC->SetWindowExt (x, y); // @pyseemfc CDC|SetWindowExt + GUI_END_SAVE; + if (old_size.cx == 0 && old_size.cy == 0) + RETURN_ERR ("SetWindowExt failed"); + else + return Py_BuildValue ("(ii)", old_size.cx, old_size.cy); + // @rdesc The previous extents of the window (in logical units). +} + +// @pymethod (x,y)|PyCDC|SetViewportExt|Sets the x,y extents of the viewport of the device context. +static PyObject * +ui_dc_set_viewport_ext (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y; + // @pyparm (x,y)|size||The new size. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + GUI_BGN_SAVE; + CSize old_size = pDC->SetViewportExt (x, y); // @pyseemfc CDC|SetViewportExt + GUI_END_SAVE; + if (old_size.cx == 0 && old_size.cy == 0) + RETURN_ERR ("SetViewportExt failed"); + else + return Py_BuildValue ("(ii)", old_size.cx, old_size.cy); + // @rdesc The previous extents of the viewport (in logical units). +} + +// @pymethod int|PyCDC|SetTextAlign|Sets the text-alignment flags. +static PyObject * +ui_dc_set_text_align (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int new_flags; + // @pyparm int|newFlags||The new alignment flags. Can be a combination of (TA_CENTER, TA_LEFT, TA_RIGHT), (TA_BASELINE, TA_BOTTOM, TA_TOP) and (TA_NOUPDATECP, TA_UPDATECP) + // The default is TA_LEFT\|TA_TOP\|TA_NOUPDATECP + if (!PyArg_ParseTuple (args, "i", &new_flags)) + return NULL; + GUI_BGN_SAVE; + int old_flags = pDC->SetTextAlign (new_flags); // @pyseemfc CDC|SetTextAlign + GUI_END_SAVE; + return Py_BuildValue ("i", old_flags); + // @rdesc The old alignment flags. +} + +// @pymethod object|PyCDC|SelectObject|Selects an object into the device context. +// Currently, only , , and objects are supported. +static PyObject * +ui_dc_select_object (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + PyObject *v; + // @pyparm object|ob||The object to select. + if (!PyArg_ParseTuple (args, "O", &v)) + return NULL; + + if (ui_base_class::is_uiobject (v, &PyCFont::type)) { + PyCFont *new_font = (PyCFont *) v; + GUI_BGN_SAVE; + CFont *cFont = pDC->SelectObject (new_font->GetFont()); + GUI_END_SAVE; + if (cFont == NULL) + RETURN_ERR ("Select font object failed"); + else { + PyCFont *ret = (PyCFont *)ui_assoc_object::make (PyCFont::type, cFont ); + if (ret && ret->ob_refcnt == 1) // only set m_delete if new object + ret->m_deleteObject = FALSE; + return ret; + } + } else if (ui_base_class::is_uiobject (v, &ui_bitmap::type)) { + ui_bitmap *new_bitmap = (ui_bitmap *) v; + GUI_BGN_SAVE; + CBitmap *pbm = pDC->SelectObject (new_bitmap->GetBitmap()); + GUI_END_SAVE; + if (pbm == NULL) + RETURN_ERR ("Select bitmap object failed"); + else { + ui_bitmap *ret = (ui_bitmap *)ui_assoc_object::make (ui_bitmap::type, pbm ); + if (ret && ret->ob_refcnt == 1) // only set m_delete if new object + ret->m_deleteObject = FALSE; + return ret; + } + } else if (ui_base_class::is_uiobject (v, &PyCBrush::type)) { + PyCBrush *new_brush = (PyCBrush *) v; + GUI_BGN_SAVE; + CBrush *pbm = pDC->SelectObject (PyCBrush::GetBrush(new_brush)); + GUI_END_SAVE; + if (pbm == NULL) + RETURN_ERR ("Select brush object failed"); + else { + PyCBrush *ret = (PyCBrush *)ui_assoc_object::make (PyCBrush::type, pbm ); + if (ret && ret->ob_refcnt == 1) // only set m_delete if new object + ret->m_deleteObject = FALSE; + return ret; + } + } else if (ui_base_class::is_uiobject (v, &ui_pen_object::type)) { + ui_pen_object * new_pen = (ui_pen_object *) v; + GUI_BGN_SAVE; + CPen * cPen = pDC->SelectObject (new_pen->GetPen()); + GUI_END_SAVE; + if (cPen == NULL) { + RETURN_ERR ("Select pen object failed"); + } else { + ui_pen_object *ret = (ui_pen_object *)ui_assoc_object::make (ui_pen_object::type, cPen ); + if (ret) { + ret->m_deleteObject = FALSE; + } + return ret; + } + } + RETURN_ERR ("Attempt to select unsupported object type."); + // @pyseemfc CDC|SelectObject + // @rdesc The previously selected object. This will be the same type as the object parameter. +} + +// @pymethod int|PyCDC|SelectPalette|Sets the logical palette. +static PyObject * +ui_dc_select_palette (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + HPALETTE hPal; + BOOL bForceBG = FALSE; + // @pyparm int|hPalette||The handle to the palette + // @pyparm int|forceBackground||Specifies whether the logical palette is forced to be a background palette. + if (!PyArg_ParseTuple (args, "i|i:SelectPalette", &hPal, &bForceBG)) + return NULL; + GUI_BGN_SAVE; + HPALETTE ret = ::SelectPalette(pDC->GetSafeHdc(), hPal, bForceBG); // @pyseemfc CDC|SelectePalette + GUI_END_SAVE; + return Py_BuildValue ("i", (int)ret); + // @rdesc The previous palette handle. +} + +// @pymethod int|PyCDC|RealizePalette|Maps palette entries in the current logical palette to the system palette. +static PyObject * +ui_dc_realize_palette (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + CHECK_NO_ARGS2(args, RealizePalette); + GUI_BGN_SAVE; + UINT ret = pDC->RealizePalette(); + GUI_END_SAVE; + return Py_BuildValue ("i", ret); + // @rdesc Indicates how many entries in the logical palette were mapped to different entries + // in the system palette. This represents the number of entries that this function + // remapped to accommodate changes in the system palette since the logical palette + // was last realized. +} + + +// @pymethod dict|PyCDC|SetROP2|Sets the current drawing mode. +static PyObject * +ui_dc_set_rop2 (PyObject * self, PyObject * args) +{ + int mode, old_mode; + CDC *pDC = ui_dc_object::GetDC (self); + if (!pDC) { + return NULL; + } + // @pyparm int|mode||The new drawing mode. + if (!PyArg_ParseTuple (args, "i", &mode)) { + return NULL; + } + GUI_BGN_SAVE; + old_mode = pDC->SetROP2 (mode);// @pyseemfc CDC|SetROP2 + GUI_END_SAVE; + return Py_BuildValue ("i", old_mode); +} + + +// @pymethod |PyCDC|TextOut|Outputs text to the display context, using the currently selected font. +static PyObject *ui_dc_text_out (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + char *text; + int strLen, x, y; + if (!PyArg_ParseTuple (args, "iis#", + &x, // @pyparm x|int||The x coordinate to write the text to. + &y, // @pyparm y|int||The y coordinate to write the text to. + &text, // @pyparm text|string||The text to write. + &strLen)) + return NULL; + GUI_BGN_SAVE; + BOOL ret = pDC->TextOut (x, y, text, strLen); // @pyseemfc CDC|TextOut + GUI_END_SAVE; + if (!ret) + RETURN_API_ERR("CDC::TextOut"); + RETURN_NONE; + // @rdesc Always none. If the function fails, an exception is raised. +} + +/* struct to dict macro (alpha version) + move to win32ui_int eventually */ +#define DICTADD(D,ST,M,TYPE) PyDict_SetItemString (D, #M, Py_BuildValue (TYPE, ST.M)) + +// @pymethod dict|PyCDC|GetTextMetrics|Retrieves the metrics for the current font in this device context. +static PyObject * +ui_dc_get_text_metrics (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + CHECK_NO_ARGS(args); + + TEXTMETRIC tm; + + if (!pDC->GetTextMetrics (&tm)) // @pyseemfc CDC|GetTextMetrics + RETURN_ERR ("GetTextMetrics failed"); + + PyObject *d = PyDict_New(); + + // @rdesc A dictionary of integers, keyed by the following strings: + DICTADD (d, tm, tmHeight, "i"); // tmHeight + DICTADD (d, tm, tmAscent, "i"); // tmAscent + DICTADD (d, tm, tmDescent, "i"); // tmDescent + DICTADD (d, tm, tmInternalLeading, "i"); // tmInternalLeading + DICTADD (d, tm, tmExternalLeading, "i"); // tmExternalLeading + DICTADD (d, tm, tmAveCharWidth, "i"); // tmAveCharWidth + DICTADD (d, tm, tmMaxCharWidth, "i"); // tmMaxCharWidth + DICTADD (d, tm, tmWeight, "i"); // tmWeight + DICTADD (d, tm, tmItalic, "i"); // tmItalic + DICTADD (d, tm, tmUnderlined, "i"); // tmUnderlined + DICTADD (d, tm, tmStruckOut, "i"); // tmStruckOut + DICTADD (d, tm, tmFirstChar, "i"); // tmFirstChar + DICTADD (d, tm, tmLastChar, "i"); // tmLastChar + DICTADD (d, tm, tmDefaultChar, "i"); // tmDefaultChar + DICTADD (d, tm, tmBreakChar, "i"); // tmBreakChar + DICTADD (d, tm, tmPitchAndFamily, "i"); // tmPitchAndFamily + DICTADD (d, tm, tmCharSet, "i"); // tmCharSet + DICTADD (d, tm, tmOverhang, "i"); // tmOverhang + DICTADD (d, tm, tmDigitizedAspectX, "i"); // tmDigitizedAspectX + DICTADD (d, tm, tmDigitizedAspectY, "i"); // tmDigitizedAspectY + + return d; +} + +// @pymethod string|PyCDC|GetTextFace|Returns typeface name of the current font. +static PyObject * +ui_dc_get_text_face (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + char buf[LF_FACESIZE]; + + GUI_BGN_SAVE; + int ret = pDC->GetTextFace (LF_FACESIZE, buf); // @pyseemfc CDC|GetTextFace + GUI_END_SAVE; + if (ret == 0) + buf[0] = '\0'; + + return Py_BuildValue ("s", buf); +} + + +// @pymethod int|PyCDC|SaveDC|Saves the current state of the device context. Windows manages a stack of state information. +// The saved device context can later be restored by using +static PyObject * +ui_dc_save_dc (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + GUI_BGN_SAVE; + int ret = pDC->SaveDC(); // @pyseemfc CDC|SaveDC + GUI_END_SAVE; + if (ret == 0) + RETURN_ERR ("SaveDC failed"); + else + return Py_BuildValue ("i", ret); + // @rdesc An integer identifying the context, which can be used by . + // An exception is raised if this function fails. +} + +// @pymethod |PyCDC|RestoreDC|Restores the state of the device context. +static PyObject * +ui_dc_restore_dc (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + int saved; + // @pyparm int|saved||The id of a previously saved device context. See + if (!PyArg_ParseTuple (args, "i", &saved)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pDC->RestoreDC (saved); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|RestoreDC + RETURN_ERR ("RestoreDC failed"); + else + RETURN_NONE; +} + +// @pymethod (x,y)|PyCDC|MoveTo|Moves the current position to a specified point. +static PyObject * +ui_dc_move_to (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + // @pyparm (x,y)|point||The point coordinate to move to. + // @pyparmalt1 int|x||The x coordinate to move to. + // @pyparmalt1 int|y||The y coordinate to move to. + int x, y; + CPoint prev; + if (!PyArg_ParseTuple (args, "ii", &x, &y)) { + PyErr_Clear(); + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + } + + GUI_BGN_SAVE; + prev = pDC->MoveTo (x,y); // @pyseemfc CDC|MoveTo + GUI_END_SAVE; + + return Py_BuildValue ("(ii)", prev.x, prev.y); + // @rdesc The previous position. +} + +// @pymethod |PyCDC|LineTo|Draws a line to a specified point, using the currently selected pen. +static PyObject * +ui_dc_line_to (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + // @pyparm (x,y)|point||The point coordinate to draw to. + // @pyparmalt1 int|x||The x coordinate to draw to. + // @pyparmalt1 int|y||The y coordinate to draw to. + int x, y; + CPoint prev; + if (!PyArg_ParseTuple (args, "ii", &x, &y)) { + PyErr_Clear(); + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + } + + GUI_BGN_SAVE; + BOOL ok = pDC->LineTo (x,y); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|LineTo + RETURN_ERR ("LineTo failed"); + else + RETURN_NONE; +} + +// @pymethod (x,y)|PyCDC|DPtoLP|Converts device units into logical units. +static PyObject * +ui_dc_dp_to_lp (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + CPoint pt; + // @todo Should really handle list of (x,y) points + // @pyparm (x,y)|point||The point to convert + // @pyparmalt1 int|x||The x coordinate to convert. + // @pyparmalt1 int|y||The y coordinate to convert. + if (!PyArg_ParseTuple (args, "ii", &pt.x, &pt.y)) { + PyErr_Clear(); + if (!PyArg_ParseTuple (args, "(ii)", &pt.x, &pt.y)) + return NULL; + } + + GUI_BGN_SAVE; + pDC->DPtoLP (&pt, 1); // @pyseemfc CDC|DPtoLP + GUI_END_SAVE; + return (Py_BuildValue ("(ii)", pt.x, pt.y)); + // @rdesc The converted coordinates. +} + +// @pymethod (x,y)|PyCDC|LPtoDP|Converts logical units into device units. +static PyObject * +ui_dc_lp_to_dp (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + CPoint pt; + // should really handle array of (x,y) points + // @pyparm (x,y)|point||The point coordinate to convert. + // @pyparmalt1 int|x||The x coordinate to convert. + // @pyparmalt1 int|y||The y coordinate to convert. + if (!PyArg_ParseTuple (args, "ii", &pt.x, &pt.y)) { + PyErr_Clear(); + if (!PyArg_ParseTuple (args, "(ii)", &pt.x, &pt.y)) + return NULL; + } + + GUI_BGN_SAVE; + pDC->LPtoDP (&pt, 1); // @pyseemfc CDC|LPtoDP + GUI_END_SAVE; + return (Py_BuildValue ("(ii)", pt.x, pt.y)); + // @rdesc The converted coordinates. + +} + +// @pymethod (left, top, right, bottom)|PyCDC|GetClipBox|Retrieves the dimensions of the smallest bounding rectangle around the current clipping boundary. +static PyObject * +ui_dc_get_clip_box (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + CRect rect; + GUI_BGN_SAVE; + int ret = pDC->GetClipBox (&rect); // @pyseemfc CDC|GetClipBox + GUI_END_SAVE; + if (ret == ERROR) + RETURN_ERR ("GetClipBox failed"); + else + return Py_BuildValue ("(iiii)", rect.left, rect.top, rect.right, rect.bottom); + // @rdesc A tuple of integers specifying the rectangle. +} + +// @pymethod int|PyCDC|GetHandleAttrib|Retrieves the handle of the attribute device context. +static PyObject * +ui_dc_get_handle_attrib (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + if (!PyArg_ParseTuple (args, ":GetHandleAttrib")) + return NULL; + + return Py_BuildValue ("i", pDC->m_hAttribDC); +} +// @pymethod int|PyCDC|GetHandleOutput|Retrieves the handle of the output device context. +static PyObject * +ui_dc_get_handle_output (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + if (!PyArg_ParseTuple (args, ":GetHandleOutput")) + return NULL; + + return Py_BuildValue ("i", pDC->m_hDC); +} + +// Path methods: +// BeginPath +// EndPath +// StrokePath +// FillPath +// StrokeAndFillPath + +// @pymethod |PyCDC|BeginPath|Opens a path bracket in the device context +static PyObject * +ui_dc_begin_path (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + BOOL ok = pDC->BeginPath(); + GUI_END_SAVE; + if (!ok) { + RETURN_API_ERR ("CDC::BeginPath"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|EndPath|Closes a path bracket and selects the path defined by the bracket into the specified device context +static PyObject * +ui_dc_end_path (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + BOOL ok = pDC->EndPath(); + GUI_END_SAVE; + if (!ok) { + RETURN_API_ERR ("CDC::EndPath"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|FillPath|Closes any open figures in the current path and fills the path's interior by using the current brush and polygon-filling mode. After its interior is filled, the path is discarded from the device context. +static PyObject * +ui_dc_fill_path (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + BOOL ok = pDC->FillPath(); + GUI_END_SAVE; + if (!ok) { + RETURN_API_ERR ("CDC::FillPath"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|StrokePath|Renders the specified path by using the current pen. +static PyObject * +ui_dc_stroke_path (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + BOOL ok = pDC->StrokePath(); + GUI_END_SAVE; + if (!ok) { + RETURN_API_ERR ("CDC::StrokePath"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod |PyCDC|StrokeAndFillPath|Closes any open figures in a path, strokes the outline of the path by using the current pen, and fills its interior by using the current brush. The device context must contain a closed path. +static PyObject * +ui_dc_stroke_and_fill_path (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else if (!PyArg_ParseTuple (args, "")) { + return NULL; + } else { + GUI_BGN_SAVE; + BOOL ok = pDC->StrokeAndFillPath(); + GUI_END_SAVE; + if (!ok) { + RETURN_API_ERR ("CDC::StrokeAndFillPath"); + } else { + RETURN_NONE; + } + } +} + +// @pymethod int|PyCDC|IsPrinting|Returns 1 if the DC is currently printing, else 0 +static PyObject * +ui_dc_is_printing (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) { + return NULL; + } else { + GUI_BGN_SAVE; + int rc = pDC->IsPrinting(); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); + } +} + + +// @pymethod x, y|PyCDC|ScaleWindowExt|Modifies the window extents relative to the current values. +static PyObject * +ui_dc_scale_window_ext (PyObject * self, PyObject * args) +{ + CDC *pDC = ui_dc_object::GetDC (self); + if (!pDC) { + return NULL; + } else { + int xn,xd,yn,yd; + if (!PyArg_ParseTuple (args, "iiii", &xn, &xd, &yn, &yd)) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize r = pDC->ScaleWindowExt (xn, xd, yn, yd); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", r.cx, r.cy); + } + } +} + + +// @pymethod x, y|PyCDC|ScaleViewportExt|Modifies the viewport extents relative to the current values. +static PyObject * +ui_dc_scale_viewport_ext (PyObject * self, PyObject * args) +{ + CDC *pDC = ui_dc_object::GetDC (self); + if (!pDC) { + return NULL; + } else { + int xn,xd,yn,yd; + if (!PyArg_ParseTuple (args, "iiii", &xn, &xd, &yn, &yd)) { + return NULL; + } else { + GUI_BGN_SAVE; + CSize r = pDC->ScaleViewportExt (xn, xd, yn, yd); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", r.cx, r.cy); + } + } +} + +// Printing functions + +// @pymethod |PyCDC|AbortDoc|Aborts a print job +static PyObject *ui_dc_abort_doc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, AbortDoc); + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + GUI_BGN_SAVE; + pDC->AbortDoc(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDC|EndDoc|Finishes spooling the document and starts printing it +static PyObject *ui_dc_end_doc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, EndDoc); + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + GUI_BGN_SAVE; + int err = pDC->EndDoc(); + GUI_END_SAVE; + if (err < 0) + { + char msg[64]; + sprintf(msg, "EndDoc failed (error code %d)", err); + PyErr_SetString(ui_module_error, msg); + return NULL; + } + RETURN_NONE; +} + +// @pymethod |PyCDC|EndPage|Finishes a page on a printer DC +static PyObject *ui_dc_end_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, EndPage); + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + GUI_BGN_SAVE; + int err = pDC->EndPage(); + GUI_END_SAVE; + if (err < 0) + { + char msg[64]; + sprintf(msg, "EndDoc failed (error code %d)", err); + PyErr_SetString(ui_module_error, msg); + return NULL; + } + RETURN_NONE; +} + +// @pymethod |PyCDC|StartDoc|Starts spooling a document to a printer DC +static PyObject *ui_dc_start_doc(PyObject * self, PyObject * args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + + // @pyparm string|docName||The document name + // @pyparm string|outputFile||The output file name. Use this to spool to a file. Omit to send to the printer. + char *docName, *outputFile = NULL; + if (!PyArg_ParseTuple(args, "s|z:StartDoc", &docName, &outputFile)) + return NULL; + + DOCINFO info; + info.cbSize = sizeof(DOCINFO); + memset(&info, 0, sizeof(DOCINFO)); + info.lpszDocName = docName; + info.lpszOutput = outputFile; + + GUI_BGN_SAVE; + int rc = pDC->StartDoc(&info); + GUI_END_SAVE; + if ( rc < 0) + { + RETURN_ERR("StartDoc failed"); + } + + RETURN_NONE; +} + +// @pymethod |PyCDC|StartPage|Starts a new page on a printer DC +static PyObject *ui_dc_start_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, StartPage); + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + GUI_BGN_SAVE; + int rc = pDC->StartPage(); + GUI_END_SAVE; + if ( rc <= 0) + RETURN_ERR("StartPage failed"); + RETURN_NONE; +} + +///////////////////////////////////////////////////////////////////// +// +// DC methods contributed by: Kleanthis Kleanthous (kk@epsilon.com.gr) + +// @pymethod |PyCDC|IntersectClipRect|Creates a new clipping region by forming the intersection of the current region and the rectangle specified +// @rdesc region type as integer +static PyObject * +ui_dc_intersect_clip_rect(PyObject *self, PyObject *args) + { + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) return NULL; + + RECT rect; + if (!PyArg_ParseTuple (args, "(iiii):IntersectClipRect", + &rect.left, &rect.top, &rect.right, &rect.bottom + // @pyparm (left, top, right, bottom)|rect||Specifies the bounding rectangle, in logical units. + )) + return NULL; + GUI_BGN_SAVE; + int type=pDC->IntersectClipRect(&rect); + GUI_END_SAVE; + // @pyseemfc CDC|IntersectClipRect + return Py_BuildValue("i",type); + } + + +// @pymethod (int)|PyCDC|SetPolyFillMode|Sets the polygon-filling mode. +// @rdesc The previous PolyFillMode as integer +static PyObject * +ui_dc_set_poly_fill_mode(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int nPolyFillMode; + // @pyparm (x,y)|point||The new origin in device units. + if (!PyArg_ParseTuple (args, "i",&nPolyFillMode)) + return NULL; + GUI_BGN_SAVE; + int pr = pDC->SetPolyFillMode(nPolyFillMode); // @pyseemfc CDC|SetPolyFillMode + GUI_END_SAVE; + return Py_BuildValue ("i", pr); + // @rdesc The previous PolyFillMode. +} + +// @pymethod |PyCDC|Polyline|Draws a Polyline. +static PyObject *ui_dc_polyline (PyObject *self, PyObject *args) +{ + PyObject * point_list; + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + if (!PyArg_ParseTuple(args, + "O:Polyline", + &point_list)) { + return NULL; + } else if (!PyList_Check(point_list)) { + return NULL; + } else { + // Convert the list of point tuples into an array of POINT structs + int num = PyList_Size (point_list); + POINT * point_array = new POINT[num]; + for (int i=0; i < num; i++) { + PyObject * point_tuple = PyList_GetItem (point_list, i); + if (!PyTuple_Check (point_tuple) || PyTuple_Size (point_tuple) != 2) { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } else { + long x, y; + PyObject *px, *py; + px = PyTuple_GetItem (point_tuple, 0); + py = PyTuple_GetItem (point_tuple, 1); + if ((!PyInt_Check(px)) || (!PyInt_Check(py))) { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } else { + x = PyInt_AsLong (px); + y = PyInt_AsLong (py); + point_array[i].x = x; + point_array[i].y = y; + } + } + } + // we have an array of POINT structs, now we + // can finally draw the polyline. + GUI_BGN_SAVE; + BOOL ret = pDC->Polyline(point_array, num); + GUI_END_SAVE; + delete[] point_array; + if (!ret) { + RETURN_API_ERR("CDC::Polyline"); + } else { + RETURN_NONE; + } + } +} + + +// @pymethod x, y|PyCDC|OffsetWindowOrg|Modifies the coordinates of the window origin relative to the coordinates of the current window origin. +// @rdesc The previous origin as a tuple (x,y) +static PyObject * +ui_dc_offset_window_org(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC)return NULL; + + int x,y; + // @pyparm int, int|x,y||The new origin offset. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + + GUI_BGN_SAVE; + CPoint old_org = pDC->OffsetWindowOrg(x, y); + GUI_END_SAVE; + return Py_BuildValue("(ii)", old_org.x, old_org.y); +} + +// @pymethod x, y|PyCDC|OffsetViewportOrg|Modifies the coordinates of the viewport origin relative to the coordinates of the current viewport origin +// @rdesc The previous viewport origin as a tuple (x,y) +static PyObject * +ui_dc_offset_viewport_org(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC)return NULL; + + int x,y; + // @pyparm int, int|x,y||The new origin offset. + if (!PyArg_ParseTuple (args, "(ii)", &x, &y)) + return NULL; + + GUI_BGN_SAVE; + CPoint old_org = pDC->OffsetViewportOrg(x, y); + GUI_END_SAVE; + return Py_BuildValue("(ii)", old_org.x, old_org.y); +} + + +// @pymethod obRgn|PyCDC|SelectClipRgn|Selects the given region as the current clipping region for the device context +// @rdesc The return value specifies the region's complexity (integer) +static PyObject * +ui_dc_select_clip_rgn(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC)return NULL; + + PyObject *objRgn = Py_None; + if (!PyArg_ParseTuple(args,"O:SelectClipRgn",&objRgn)) + return NULL; + + CRgn *pRgn = PyCRgn::GetRgn(objRgn); + if (!pRgn) return NULL; + + + GUI_BGN_SAVE; + int r=pDC->SelectClipRgn(pRgn); + GUI_END_SAVE; + + return Py_BuildValue("i",r); +} + +// @pymethod rc|PyCDC|Rectangle|Draws a rectangle using the current pen. The interior of the rectangle is filled using the current brush. +static PyObject * +ui_dc_rectangle(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC)return NULL; + RECT rect; + if (!PyArg_ParseTuple(args, + "(iiii)", + &rect.left,&rect.top, + &rect.right,&rect.bottom)) + return NULL; + + BOOL b=pDC->Rectangle(rect.left,rect.top,rect.right,rect.bottom); + + if(!b)RETURN_API_ERR("CDC::Rectangle"); + + RETURN_NONE; +} + +// @pymethod s,rc,forat|PyCDC|DrawText|Formats text in the given rectangle +// @rdesc Height of text in pixels +static PyObject * +ui_dc_draw_text(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC)return NULL; + + char *psz; + RECT rect; + UINT nFormat=DT_SINGLELINE|DT_CENTER|DT_VCENTER; + if (!PyArg_ParseTuple(args, + "s(iiii)|i",&psz, + &rect.left,&rect.top, + &rect.right,&rect.bottom,&nFormat)) + return NULL; + + CString str(psz); + int height=pDC->DrawText(str,&rect,nFormat); + + return Py_BuildValue("i",height); +} + +// @pymethod |PyCDC|StretchBlt|Copies a bitmap from the source device context to this device context. +static PyObject * +ui_dc_stretch_blt (PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) + return NULL; + int x, y, width, height, xsrc, ysrc,widthsrc, heightsrc; + DWORD rop; + PyObject *dc_ob; + if (!PyArg_ParseTuple (args, "(ii)(ii)O(ii)(ii)i", + &x, &y, // @pyparm (x,y)-ints|destPos||The logical x,y coordinates of the upper-left corner of the destination rectangle. + &width, &height, // @pyparm (width, height)-ints|size||Specifies the width and height (in logical units) of the destination rectangle and source bitmap. + &dc_ob, // @pyparm |dc||Specifies the PyCDC object from which the bitmap will be copied. It must be None if rop specifies a raster operation that does not include a source. + &xsrc, &ysrc, // @pyparm (xSrc, ySrc)-ints|srcPos||Specifies the logical x,y coordinates of the upper-left corner of the source bitmap. + &widthsrc, &heightsrc, // @pyparm (widthsrc, heightsrc)-ints|size||Specifies the width and height (in logical units) of the destination rectangle and source bitmap. + &rop)) // @pyparm int|rop||Specifies the raster operation to be performed. See the win32 api documentation for details. + return NULL; + if (!ui_base_class::is_uiobject (dc_ob, &ui_dc_object::type)) + RETURN_TYPE_ERR("The 'O' param must be a PyCDC object"); + CDC *pSrcDC = NULL; + if (dc_ob!=Py_None) { + pSrcDC = ui_dc_object::GetDC(dc_ob); + if (!pSrcDC) + RETURN_ERR("The source DC is invalid"); + } + GUI_BGN_SAVE; + int prevMode=pDC->SetStretchBltMode(COLORONCOLOR); + BOOL ok = pDC->StretchBlt(x, y, width, height, pSrcDC, xsrc, ysrc,widthsrc, heightsrc, rop); + pDC->SetStretchBltMode(prevMode); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDC|StretchBlt + RETURN_ERR("StretchBlt failed"); + RETURN_NONE; +} +// End of kk contributed methods! +// @pymethod |PyCDC|DrawFrameControl|Draws a frame control of the specified type and style. +static PyObject * +ui_dc_draw_frame_control(PyObject *self, PyObject *args) +{ + CDC *pDC = ui_dc_object::GetDC(self); + if (!pDC) return NULL; + + RECT rect; + int typ, state; + if (!PyArg_ParseTuple (args, "(iiii)ii:DrawFrameControl", + &rect.left, &rect.top, &rect.right, &rect.bottom, + // @pyparm (left, top, right, bottom)|rect||Specifies the bounding rectangle, in logical units. + &typ, // @pyparm int|typ|| + &state // @pyparm int|state|| + )) + return NULL; + GUI_BGN_SAVE; + BOOL ok=pDC->DrawFrameControl(&rect, typ, state); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DrawFrameControl failed"); + // @pyseemfc CDC|DrawFrameControl + RETURN_NONE; +} + +///////////////////////////////////////////////////////////////////// +// DC Methods +// @object PyCDC|A Device Context. Encapsulates an MFC class. +static struct PyMethodDef ui_dc_methods[] = { + {"AbortDoc", ui_dc_abort_doc, 1}, // @pymeth AbortDoc|Aborts a print job + {"Arc", ui_dc_arc, 1}, // @pymeth Arc|Draws an arc. + {"BeginPath", ui_dc_begin_path, 1}, // @pymeth BeginPath|Opens a path bracket in the device context + {"BitBlt", ui_dc_bitblt, 1}, // @pymeth BitBlt|Copies a bitmap + {"Chord", ui_dc_chord, 1}, // @pymeth Chord|Draws a chord. + {"CreateCompatibleDC", ui_dc_object::create_compatible_dc, 1}, // @pymeth CreateCompatibleDC|Creates a memory DC compatible with this DC. + {"CreatePrinterDC", ui_dc_object::create_printer_dc, 1}, // @pymeth CreatePrinterDC|Creates a device context for a specific printer + {"DeleteDC", ui_dc_delete_dc, 1}, // @pymeth DeleteDC|Deletes all resources associated with a device context. + {"DPtoLP", ui_dc_dp_to_lp, 1}, // @pymeth DPtoLP|Convert from device points to logical points. + {"Draw3dRect", ui_dc_draw3drect, 1}, // @pymeth Draw3dRect|Draws a three-dimensional rectangle. + {"DrawFocusRect", ui_dc_draw_focus_rect, 1}, // @pymeth DrawFocusRect|Draws a rectangle in the style used to indicate the rectangle has focus + {"DrawFrameControl", ui_dc_draw_frame_control,1}, // @pymeth DrawFrameControl|Draws a frame control of the specified type and style. + {"DrawIcon", ui_dc_draw_icon, 1}, // @pymeth DrawIcon|Draws an icon on the DC. + {"DrawText", ui_dc_draw_text,1}, // @pymeth DrawText|Formats text in the given rectangle + {"Ellipse", ui_dc_ellipse, 1}, // @pymeth Ellipse|Draws an Ellipse. + {"EndDoc", ui_dc_end_doc, 1}, // @pymeth EndDoc|Finishes spooling the document and starts printing it + {"EndPage", ui_dc_end_page, 1}, // @pymeth EndPage|Finishes a page on a printer DC + {"EndPath", ui_dc_end_path, 1}, // @pymeth EndPath|Closes a path bracket and selects the path defined by the bracket into the specified device context + {"ExtTextOut", ui_dc_ext_text_out, 1}, //@pymeth ExtTextOut|Writes text to the DC. + {"FillPath", ui_dc_fill_path, 1}, // @pymeth FillPath|Closes any open figures in the current path and fills the path's interior by using the current brush and polygon-filling mode. + {"FillRect", ui_dc_fillrect, 1}, // @pymeth FillRect|Fills a given rectangle with the specified brush + {"FillSolidRect", ui_dc_fillsolidrect, 1}, // @pymeth FillSolidRect|Fills the given rectangle with the specified solid color. + {"FrameRect", ui_dc_framerect, 1}, // @pymeth FrameRect|Draws a border around the rectangle specified by rect + {"GetBrushOrg", ui_dc_get_brush_org, 1}, // @pymeth GetBrushOrg|Retrieves the origin (in device units) of the brush currently selected for the device context. + {"GetClipBox", ui_dc_get_clip_box, 1}, // @pymeth GetClipBox|Retrives the current clipping region. + {"GetCurrentPosition", ui_dc_get_current_position, 1}, // @pymeth GetCurrentPosition|Retrieves the current position (in logical coordinates). + {"GetDeviceCaps", ui_dc_get_device_caps, 1}, // @pymeth GetDeviceCaps|Retrieves current device capabilities. + {"GetHandleAttrib", ui_dc_get_handle_attrib, 1}, // @pymeth GetHandleAttrib|Retrieves the handle of the attribute device context. + {"GetHandleOutput", ui_dc_get_handle_output, 1}, // @pymeth GetHandleOutput|Retrieves the handle of the output device context. + {"GetMapMode", ui_dc_get_map_mode, 1}, //@pymeth GetMapMode|Gets the mapping mode for the device context. + {"GetNearestColor", ui_dc_get_nearest_color, 1}, // @pymeth GetNearestColor|Returns the closest color a device can map. + {"GetPixel", ui_dc_get_pixel, 1}, // @pymeth GetPixel|Returns the value of a pixel at a location + {"GetSafeHdc", ui_dc_get_safe_hdc, 1}, // @pymeth GetSafeHdc|Returns the underlying windows handle for the DC object. + {"GetTextExtent", ui_dc_get_text_extent, 1}, // @pymeth GetTextExtent|Calculates the size of the string. + {"GetTextExtentPoint", ui_dc_get_text_extent, 1}, // @pymeth GetTextExtentPoint|Alias for GetTextExtent - Calculates the size of the string. + {"GetTextFace", ui_dc_get_text_face, 1}, // @pymeth GetTextFace|Retrieves the name of the current font. + {"GetTextMetrics", ui_dc_get_text_metrics, 1}, // @pymeth GetTextMetrics|Retrieves the metrics for the current font. + {"GetViewportExt", ui_dc_get_viewport_ext, 1}, // @pymeth GetViewportExt|Gets the viewport extent of the device context + {"GetViewportOrg", ui_dc_get_viewport_org, 1}, // @pymeth GetViewportOrg|Gets the viewport origin of the device context + {"GetWindowExt", ui_dc_get_window_ext, 1}, // @pymeth GetWindowExt|Gets the window extent of the device context + {"GetWindowOrg", ui_dc_get_window_org, 1}, // @pymeth GetWindowOrg|Retrieves the x- and y-coordinates of the origin of the window associated with the device context. + {"IntersectClipRect", ui_dc_intersect_clip_rect, 1}, // @pymeth IntersectClipRect|Creates a new clipping region by forming the intersection of the current region and the rectangle specified + {"IsPrinting", ui_dc_is_printing, 1}, // @pymeth IsPrinting|Returns 1 if the DC is currently printing, else 0 + {"LineTo", ui_dc_line_to, 1}, // @pymeth LineTo|Draws a line to a specified point. + {"LPtoDP", ui_dc_lp_to_dp, 1}, // @pymeth LPtoDP|Convert from logical points to device points + {"MoveTo", ui_dc_move_to, 1}, // @pymeth MoveTo|Moves the current position to a specifed point. + {"OffsetWindowOrg", ui_dc_offset_window_org,1}, // @pymeth OffsetWindowOrg|Modifies the coordinates of the window origin relative to the coordinates of the current window origin. + {"OffsetViewportOrg", ui_dc_offset_viewport_org,1}, // @pymeth OffsetViewportOrg|Modifies the coordinates of the viewport origin relative to the coordinates of the current viewport origin + {"PatBlt", ui_dc_patblt, 1}, // @pymeth PatBlt|Creates a bit pattern on the device. + {"Pie", ui_dc_pie, 1}, // @pymeth Pie|Draws a pie shape with specific starting and ending points in a rectangle + {"PolyBezier", ui_dc_poly_bezier, 1}, // @pymeth PolyBezier|Draws one or more Bezier splines. + {"Polygon", ui_dc_polygon, 1}, // @pymeth Polygon|Draws an Polygon. + {"Polyline", ui_dc_polyline, 1}, // @pymeth Polyline|Draws a Polyline. + {"RealizePalette", ui_dc_realize_palette, 1}, // @pymeth RealizePalette|Maps palette entries in the current logical palette to the system palette. + {"Rectangle", ui_dc_rectangle,1}, // @pymeth Rectangle|Draws a rectangle using the current pen. The interior of the rectangle is filled using the current brush. + {"RectVisible", ui_dc_rect_visible, 1}, // @pymeth RectVisible|Determines if a rectangle is currently visisble in the viewport. + {"RestoreDC", ui_dc_restore_dc, 1}, // @pymeth RestoreDC|Restores a saved DC. + {"SaveDC", ui_dc_save_dc, 1}, // @pymeth SaveDC|Saves a DC. + {"ScaleWindowExt", ui_dc_scale_window_ext, 1}, // @pymeth ScaleWindowExt|Modifies the window extents relative to the current values. + {"ScaleViewportExt", ui_dc_scale_viewport_ext, 1}, // @pymeth ScaleViewportExt|Modifies the viewport extents relative to the current values. + {"SelectClipRgn", ui_dc_select_clip_rgn,1}, // @pymeth SelectClipRgn|Selects the given region as the current clipping region for the device context + {"SelectObject", ui_dc_select_object, 1}, // @pymeth SelectObject|Selects an object into the DC. + {"SelectPalette", ui_dc_select_palette, 1}, // @pymeth SelectObject|Selects the logical palette. + {"SetBkColor", ui_dc_set_bk_color, 1}, // @pymeth SetBkColor|Sets the background color. + {"SetBkMode", ui_dc_set_bk_mode, 1}, // @pymeth SetBkMode|Sets the background mode. + {"SetBrushOrg", ui_dc_set_brush_org, 1}, // @pymeth SetBrushOrg|Specifies the origin that GDI will assign to the next brush that the application selects into the device context. + {"SetGraphicsMode", ui_dc_set_graphics_mode, 1}, // @pymeth SetGraphicsMode|Sets the graphics mode for the specified device context + {"SetMapMode", ui_dc_set_map_mode, 1}, // @pymeth SetMapMode|Sets the device mapping mode. + {"SetPixel", ui_dc_setpixel, 1}, // @pymeth SetPixel|Set a pixel to a color + {"SetPolyFillMode", ui_dc_set_poly_fill_mode, 1}, // @pymeth SetPolyFillMode|Sets the polygon-filling mode. + {"SetROP2", ui_dc_set_rop2, 1}, // @pymeth SetROP2|Sets the current drawing mode. + {"SetTextAlign", ui_dc_set_text_align, 1}, // @pymeth SetTextAlign|Sets the text alignment. + {"SetTextColor", ui_dc_set_text_color, 1}, // @pymeth SetTextColor|Sets the text foreground color. + {"SetWindowExt", ui_dc_set_window_ext, 1}, // @pymeth SetWindowExt|Sets the extents of the window. + {"SetWindowOrg", ui_dc_set_window_org, 1}, // @pymeth SetWindowOrg|Sets the window origin of the device context + {"SetViewportExt", ui_dc_set_viewport_ext, 1}, // @pymeth SetViewportExt|Sets the extents of the window's viewport. + {"SetViewportOrg", ui_dc_set_viewport_org, 1}, // @pymeth SetViewportOrg|Sets the viewport origin of the device context + {"SetWorldTransform", ui_dc_set_world_transform, 1}, // @pymeth SetWorldTransform|sets a two-dimensional linear transformation between world space and page space for the specified device context. + {"StartDoc", ui_dc_start_doc, 1}, // @pymeth StartDoc|Starts spooling a document to a printer DC + {"StartPage", ui_dc_start_page, 1}, // @pymeth StartPage|Starts a new page on a printer DC + {"StretchBlt", ui_dc_stretch_blt,1}, // @pymeth StretchBlt|Copies a bitmap from the source device context to this device context. + {"StrokeAndFillPath", ui_dc_stroke_and_fill_path, 1}, // @pymeth StrokeAndFillPath|Closes any open figures in a path, strokes the outline of the path by using the current pen, and fills its interior by using the current brush. The device context must contain a closed path. + {"StrokePath", ui_dc_stroke_path, 1}, // @pymeth StrokePath|Renders the specified path by using the current pen. + {"TextOut", ui_dc_text_out, 1}, // @pymeth TextOut|Writes text to the DC. + {NULL, NULL} +}; + +ui_type ui_dc_object::type("PyCDC", + &ui_assoc_object::type, + sizeof(ui_dc_object), + ui_dc_methods, + GET_PY_CTOR(ui_dc_object)); + diff --git a/Pythonwin/win32dc.h b/Pythonwin/win32dc.h new file mode 100644 index 0000000000..5559c9bf85 --- /dev/null +++ b/Pythonwin/win32dc.h @@ -0,0 +1,20 @@ +// device context class +class PYW_EXPORT ui_dc_object : public ui_assoc_object { +protected: + ui_dc_object() + : m_deleteDC (FALSE) + { } + ~ui_dc_object(); + virtual void SetAssocInvalid(); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); +public: + static ui_type type; + MAKE_PY_CTOR(ui_dc_object) + static CDC *GetDC(PyObject *self); + + // couple of python methods + static PyObject *create_dc( PyObject *self, PyObject *args ); + static PyObject *create_compatible_dc( PyObject *self, PyObject *args ); + static PyObject *create_printer_dc( PyObject *self, PyObject *args ); + BOOL m_deleteDC; +}; diff --git a/Pythonwin/win32dlg.cpp b/Pythonwin/win32dlg.cpp new file mode 100644 index 0000000000..1116363c46 --- /dev/null +++ b/Pythonwin/win32dlg.cpp @@ -0,0 +1,1375 @@ +/* + + dialog data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + + Coupla enhancements and bugfixes donated by Charles G. Waldman + + dialog is derived from window. + +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 "win32win.h" +#include "win32dc.h" +#include "win32dll.h" +#include "win32dlg.h" +#include "win32prinfo.h" + +typedef CPythonDlgFramework< CDialog > CPythonDlg; + +class CProtectedDialog : public CDialog +{ +public: + void BaseOnOK() {CDialog::OnOK();} + void BaseOnCancel() {CDialog::OnCancel();} + HGLOBAL GetDialogTemplate() {return m_hDialogTemplate;} + BOOL BaseOnInitDialog() {return CDialog::OnInitDialog();} +}; + +typedef CPythonPrtDlgFramework< CPrintDialog > CPythonPrtDlg; + +class CProtectedPrintDialog : public CPrintDialog +{ +public: + void BaseOnOK() {CPrintDialog::OnOK();} + void BaseOnCancel() {CPrintDialog::OnCancel();} + HGLOBAL GetDialogTemplate() {return m_hDialogTemplate;} + BOOL BaseOnInitDialog() {return CPrintDialog::OnInitDialog();} +}; + +extern HGLOBAL MakeResourceFromDlgList(PyObject *tmpl); +extern PyObject *MakeDlgListFromResource(HGLOBAL res); + +extern char *errmsgAlreadyInit; + +// @pymethod list|win32ui|LoadDialogResource|Loads a dialog resource, and returns a list detailing the objects. +PyObject *ui_get_dialog_resource( PyObject *, PyObject *args ) +{ + int idRes; + HINSTANCE hMod = NULL, hOldRes = NULL; + PyObject *obDLL = NULL; + // @pyparm int|idRes||The ID of the dialog resource to load. + // @pyparm |dll|None|The DLL object to load the dialog from. + if (!PyArg_ParseTuple(args,"i|O:LoadDialogResource", &idRes, &obDLL)) + return NULL; + if (obDLL && obDLL != Py_None) { + // passed a DLL object. + if (!ui_base_class::is_uiobject(obDLL, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL object"); + hMod = ((dll_object *)obDLL)->GetDll(); + } + if (hMod==NULL) + hMod = AfxFindResourceHandle( MAKEINTRESOURCE(idRes), RT_DIALOG ); + else { + hOldRes = AfxGetResourceHandle(); + AfxSetResourceHandle(hMod); + } + HGLOBAL hGlob; + HRSRC hrsrc; + hrsrc = ::FindResourceEx( hMod, RT_DIALOG, + MAKEINTRESOURCE(idRes), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + if (hrsrc) + hGlob = ::LoadResource( hMod, hrsrc ); + if (hOldRes) + AfxSetResourceHandle(hOldRes); + if (hrsrc==NULL) + RETURN_API_ERR("FindResourceEx"); + if (hGlob==NULL) + RETURN_API_ERR("LoadResource"); + + CDialog *pDlg = new CPythonDlg(); + PyCDialog *ret = (PyCDialog *)ui_assoc_object::make( PyCDialog::type, pDlg); + if (ret) { + ret->hTemplate = hGlob; + ret->hInstance = hMod; + } + return MakeDlgListFromResource(hGlob); +} + + +CDialog *GetDialog(PyObject *self) +{ + return (CDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCDialog::type); +} +CPythonDlg *GetPythonDlg(PyObject *self) +{ + return (CPythonDlg *)PyCWnd::GetPythonGenericWnd(self, &PyCDialog::type); +} + +CFileDialog *GetFileDialog(PyObject *self) +{ + return (CFileDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCFileDialog::type); +} +CFontDialog *GetFontDialog(PyObject *self) +{ + return (CFontDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCFontDialog::type); +} + +CColorDialog *GetColorDialog(PyObject *self) +{ + return (CColorDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCColorDialog::type); +} + +CPrintDialog *GetPrintDialog(PyObject *self) +{ + return (CPrintDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCPrintDialog::type); +} + +///////////////////////////////////////////////////////////////////// +// +// Utilities that work with dialogs! +// +////////////////////////////////////////////////////////////////////// +// @pymethod |PyCWinApp|InitDlgInstance|Calls critical InitInstance processing for a dialog based application. +PyObject * +ui_init_dlg_instance(PyObject *self, PyObject *args) +{ + CDialog *pDlg; + PyObject *obDlg; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + + if (pApp->GetMainFrame()) + RETURN_ERR(errmsgAlreadyInit); + + if (!PyArg_ParseTuple(args,"O:InitDlgInstance",&obDlg)) + return NULL; + // @pyparm |dialog||The dialog object to be used as the main window for the application. + if (!ui_base_class::is_uiobject(obDlg, &PyCDialog::type)) + RETURN_TYPE_ERR("First arg must be a PyCDialog"); + if (!(pDlg=GetDialog(obDlg))) + return NULL; + + pApp->SetMainFrame(pDlg); + RETURN_NONE; +} + +//////////////////////////////////////////////////////////////////// +// +// Dialog object +// +////////////////////////////////////////////////////////////////////// +PyCDialog::PyCDialog() +{ + // Memory tracking for dialogs is a real pain. In normal MFC, many + // dialogs are used as local variables - so lifetimes are not an issue. + // However, Python must allocate dialogs with new() - so we assume that + // when a dialog must stay alive, there will be a reference held. + bManualDelete = TRUE; + hTemplate = NULL; + hInstance = NULL; + hSaved = NULL; + ddlist = PyList_New(0); + dddict = PyDict_New(); +} +PyCDialog::~PyCDialog() +{ +// TRACE("Dialog object destructing\n"); +// CDialog *pDlg; +// if ((pDlg=GetDialog(this))) +// delete pDlg; + if (hSaved) + { + GlobalUnlock(hSaved); + GlobalFree(hSaved); + } + + PyCWnd::DoKillAssoc(TRUE); + ui_assoc_object::SetAssocInvalid(); // must call this explicitely, as I ignore SetAssocInvalid + Py_XDECREF(ddlist); + Py_XDECREF(dddict); +} +void PyCDialog::DoKillAssoc(BOOL bDestructing /*=FALSE*/ ) +{ + // we can not have the pointer deleted at window destruction time + // for a dialog (as MFC still needs it after the dialog has completed + BOOL bManDeleteSave = bManualDelete; + if (!bDestructing) + bManualDelete = FALSE; + PyCWnd::DoKillAssoc(bDestructing); + if (!bDestructing) + bManualDelete = bManDeleteSave; +} +PyObject * +PyCDialog::getattr(char *name) +{ + if (strcmp(name,"data")==0) { + Py_INCREF(dddict); + return dddict; + } + else if (strcmp(name,"datalist")==0) { + Py_INCREF(ddlist); + return ddlist; + } + else + return ui_base_class::getattr(name); +} + +static PyObject *set_exchange_error(const char *msg, int index) +{ + static char errBuf[256]; + wsprintf(errBuf, "Data exchange list index %d - %s", index, msg); + PyErr_SetString(PyExc_TypeError,errBuf); + return NULL; +} + +static PyObject *do_exchange_edit( int id, int index, char *type, PyObject *oldVal, PyObject *o1, PyObject *o2, CDataExchange *pDX ) +{ + PyObject *newOb; + switch (type[0]) { + case 'i': { + int intVal = 0; + if (oldVal) + intVal = (int)PyInt_AsLong(oldVal); + GUI_BGN_SAVE; + DDX_Text(pDX, id, intVal); + GUI_END_SAVE; + if (o1 && o2) { + if (PyInt_Check(o1) && PyInt_Check(o2)) + DDV_MinMaxInt(pDX, intVal, PyInt_AsLong(o1), PyInt_AsLong(o2)); + else + return set_exchange_error("Edit - must be tuple of control_id, key, 'i', intMin, intMax", index); + } + newOb = Py_BuildValue("i", intVal); + break; + } + case 's': { + char *strVal = NULL; + if (oldVal && PyString_Check(oldVal)) + strVal = PyString_AsString(oldVal); + CString csVal(strVal?strVal:""); + GUI_BGN_SAVE; + DDX_Text(pDX, id, csVal); + GUI_END_SAVE; + if (o1 && o2) { + if (PyInt_Check(o1) && o2==NULL) + DDV_MaxChars(pDX, csVal, PyInt_AsLong(o1)); + else + return set_exchange_error("Edit - must be tuple of control_id, key, 's', maxLength", index); + } + newOb = Py_BuildValue("s", (const char *)csVal); + break; + } + default: + return set_exchange_error("type param must be 'i' or 's' for edit controls", index); + } + return newOb; +} +static PyObject *do_exchange_list_combo( int id, int index, char *type, PyObject *oldVal, PyObject *o1, PyObject *o2, CDataExchange *pDX, BOOL bList ) +{ + if (o1 && o2) + return set_exchange_error("List/ComboBox - must be tuple of control_id, key, 'i|s'", index); + PyObject *newOb = NULL; + switch (type[0]) { + case 'i': { + int intVal = 0; + if (oldVal && oldVal != Py_None) { + if (!PyInt_Check(oldVal)) return set_exchange_error("'i' format requires integers", index); + intVal = (int)PyInt_AsLong(oldVal); + } + GUI_BGN_SAVE; + if (bList) + DDX_LBIndex(pDX, id, intVal); + else + DDX_CBIndex(pDX, id, intVal); + GUI_END_SAVE; + newOb = Py_BuildValue("i", intVal); + break; + } + case 's': { + char *strVal = NULL; + if (oldVal && oldVal != Py_None) { + if (!PyString_Check(oldVal)) return set_exchange_error("'s' format requires strings", index); + strVal = PyString_AsString(oldVal); + } + CString csVal(strVal?strVal:""); + if (bList) + DDX_LBString(pDX, id, csVal); + else + DDX_CBString(pDX, id, csVal); + newOb = Py_BuildValue("s", (const char *)csVal); + break; + } + case 'S': { + char *strVal = NULL; + if (oldVal && oldVal != Py_None) { + if (!PyString_Check(oldVal)) return set_exchange_error("'S' format requires strings", index); + strVal = PyString_AsString(oldVal); + } + CString csVal(strVal?strVal:""); + GUI_BGN_SAVE; + if (bList) + DDX_LBStringExact(pDX, id, csVal); + else + DDX_CBStringExact(pDX, id, csVal); + GUI_END_SAVE; + newOb = Py_BuildValue("s", (const char *)csVal); + break; + } + case 'l': { + char buf[128]; + HWND hWndCtrl = pDX->PrepareCtrl(id); + if (pDX->m_bSaveAndValidate) { + int count = (int)::SendMessage(hWndCtrl, bList?LB_GETCOUNT:CB_GETCOUNT, 0, 0L); + newOb = PyList_New(count); + for (int i=0;iGetDlgItem(id); + if (pWnd==NULL) + return set_exchange_error("control with that ID does not exist", index); + + // Need to check certain attributes on the window. MFC Asserts otherwise. + HWND hwnd = pWnd->GetSafeHwnd(); + DWORD dwStyle = ::GetWindowLong(hwnd, GWL_STYLE); + DWORD dwCode = ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0L); + BOOL bRadio = dwCode & DLGC_RADIOBUTTON; + BOOL bCheck = dwStyle & BS_CHECKBOX; + if (!bRadio && !bCheck) + return set_exchange_error("only radios and checkboxes are supported for button controls", index); + if ((bRadio && (dwStyle & BS_AUTORADIOBUTTON)==0) || (bCheck && (dwStyle & BS_AUTOCHECKBOX)==0)) + return set_exchange_error("the button must have the 'auto' style set", index); + if (bRadio) { + if ((dwStyle & WS_GROUP)==0) + // Not a group leader - this is not considered an error condition, + // the group leader provides data for the entire group. + return NULL; + } + int intVal = 0; + if (oldVal) { + if (!PyInt_Check(oldVal)) + return set_exchange_error("the previous value was not a number!", index); + intVal = (int)PyInt_AsLong(oldVal); + } + if (bRadio) + DDX_Radio(pDX, id, intVal); + else + DDX_Check(pDX, id, intVal); + return Py_BuildValue("i", intVal); +} + +void Python_do_exchange(CDialog *pDlg, CDataExchange *pDX) +{ + CEnterLeavePython _celp; + PyCDialog *dob = (PyCDialog *) ui_assoc_object::GetPyObject(pDlg); + if (!dob) { + TRACE("do_exchange called on dialog with no Python object!\n"); + return ; // dont print an exception + } + for (int i=0;iddlist);i++) { + PyObject *ob = PyList_GetItem(dob->ddlist, i); + if (ob==NULL) + break; + int id; + PyObject *obAttr; + PyObject *o1 = NULL, *o2 = NULL; + char *szType = "s"; + if (!PyArg_ParseTuple(ob, "iO|sOO", &id, &obAttr, &szType, &o1, &o2 )) { + set_exchange_error( "must be tuple of control_id, key ...", i); + break; + } + if (id==0 || id==-1) { + set_exchange_error( "control ID must be a value other than 0 or -1", i); + break; + } + + PyObject *oldOb = PyDict_GetItem(dob->dddict, obAttr); + char szClassName[64]; + ::GetClassName( pDlg->GetDlgItem(id)->GetSafeHwnd(), szClassName, sizeof(szClassName)); + PyObject *newOb = NULL; + try { + if (strcmp(szClassName, "Edit")==0 || strcmp(szClassName, "Static")==0) + newOb = do_exchange_edit(id, i, szType, oldOb, o1, o2, pDX); + else if (strcmp(szClassName, "ListBox")==0) + newOb = do_exchange_list_combo(id, i, szType, oldOb, o1, o2, pDX, TRUE); + else if (strcmp(szClassName, "ComboBox")==0) + newOb = do_exchange_list_combo(id, i, szType, oldOb, o1, o2, pDX, FALSE); + else if (strcmp(szClassName, "Button")==0) + newOb = do_exchange_button(pDlg, id, i, szType, oldOb, o1, o2, pDX); + if (newOb) { + PyDict_SetItem(dob->dddict,obAttr, newOb); + Py_DECREF(newOb); + } + } + catch(CNotSupportedException *e) { + e->Delete(); + set_exchange_error("No control by that name, or other MFC 'NotSupported' exception", i); + } + } + if (PyErr_Occurred()) + gui_print_error(); +} + +// @pymethod |win32ui|CreateDialog|Creates a dialog object. +PyObject *PyCDialog::create( PyObject *self, PyObject *args ) +{ + int idRes; + HINSTANCE hMod = NULL, hOldRes = NULL; + PyObject *obDLL = NULL; + if (!PyArg_ParseTuple(args,"i|O:CreateDialog", + &idRes, // @pyparm int|idRes||The ID of the dialog resource to load. + &obDLL))// @pyparm |dll|None|The DLL object to load the dialog from. + return NULL; + if (obDLL && obDLL!=Py_None) { + // passed a DLL object. + if (!ui_base_class::is_uiobject(obDLL, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL"); + hMod = ((dll_object *)obDLL)->GetDll(); + if (hMod==NULL) + RETURN_ERR("Can not load from an uninitialised DLL object"); + } + if (hMod==NULL) + hMod = AfxFindResourceHandle( MAKEINTRESOURCE(idRes), RT_DIALOG ); + else { + hOldRes = AfxGetResourceHandle(); + AfxSetResourceHandle(hMod); + } + + HGLOBAL hGlob; + HRSRC hrsrc; + hrsrc = ::FindResourceEx( hMod, RT_DIALOG, + MAKEINTRESOURCE(idRes), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + if (hrsrc) + hGlob = ::LoadResource( hMod, hrsrc ); + if (hOldRes) + AfxSetResourceHandle(hOldRes); + if (hrsrc==NULL) + RETURN_API_ERR("FindResourceEx"); + if (hGlob==NULL) + RETURN_API_ERR("LoadResource"); + + GUI_BGN_SAVE; + CDialog *pDlg = new CPythonDlg(); + GUI_END_SAVE; + PyCDialog *ret = (PyCDialog *)ui_assoc_object::make( PyCDialog::type, pDlg); + if (ret) { + ret->hTemplate = hGlob; + ret->hInstance = hMod; + } + return ret; +} + +// @pymethod |win32ui|CreateDialogIndirect|Creates a dialog object from a template. +PyObject *PyCDialog::createIndirect( PyObject *, PyObject *args ) +{ + PyObject *obList = NULL; + // @pyparm list|obList||A list of [, , ...], which describe the dialog to be created. + if (!PyArg_ParseTuple(args, "O:CreateDialogIndirect", &obList)) + return NULL; + + HGLOBAL h = MakeResourceFromDlgList(obList); + if (h == NULL) + return NULL; + CDialog *pDlg = new CPythonDlg(); + PyCDialog *ret = (PyCDialog *)ui_assoc_object::make( PyCDialog::type, pDlg); + if (ret) + { + ret->hSaved = h; + ret->hTemplate = (HGLOBAL)GlobalLock(h); + } + return ret; + // @ comm The code for Dynamic Dialogs was supplied by Curt Hagenlocher \. These notes are also from Curt. + // Error checking is thorough but cryptic. More intelligent error messages could be produced. + // obList is a list containing one or more further lists. The first of these is a description of + // the dialog box itself. The others are descriptions of the children (ie, the controls). + // Dialog Header: + // [caption (bounds) style extended-style (font) menu window-class] + // The first three parameters are required: + // * caption must currently be a string object + // * (bounds) must be a tuple (x, y, width, height) in dialog units. + // * style is the window style, and must be a long object + // The last four parameters are optional. "None" is used as a placeholder, if necessary + // * extended-style is the extended window style. It must be None or a long. + // * (font) must be a tuple (fontsize, fontname) or None + // * menu is either a menu id (int) or a menu name (string object) or None + // * window-class is the window class name of the dialog or None for default + // Dialog Item: + // [window-class text child-id (bounds) style extended-style extra-data] + // The first five parameters are required: + // * window-class describes the child window. It can be a string object + // ("EDIT" or "listbox") or a predefined integer type (eg, 133 is a combobox). + // * text is the window text (string). Each control type uses this differently. + // * child-id is the id for the item. + // * (bounds) is again a tuple (x, y, width, height) in dialog units + // * style is the window style (long) + // The next two parameters are optional: + // * extended-style is the extended window style. It must be None or a long. + // * extra-data is a string with extra initialization data to be sent to the + // control on creation. I've never actually seen any control use this! +} + +/////////////////////////////////////// +// +// Dialog Methods +// +// @pymethod int|PyCDialog|DoModal|Create a modal window for the dialog box. +static PyObject *ui_dialog_do_modal( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,DoModal); + CDialog *pDlg; + if (!(pDlg=GetDialog(self))) + return NULL; + if (pDlg->m_hWnd) + RETURN_ERR("cannot call DoModal on a dialog already constructed as modeless"); + if (pDlg->IsKindOf(RUNTIME_CLASS(CDialog))) { + CProtectedDialog *pD = (CProtectedDialog *)pDlg; + if (pD->GetDialogTemplate()) + RETURN_ERR("cannot call DoModal twice."); + } + PyCDialog *obDlg = (PyCDialog *)self; + if (obDlg->hTemplate && !pDlg->InitModalIndirect(obDlg->hTemplate)) { + RETURN_ERR("InitModalIndirect failed"); + } + + Py_INCREF(self); // make sure Python doesnt kill the object while in a modal call. + // really only for the common dialog, and other non CPythonDlg's + int ret; + GUI_BGN_SAVE; + ret = pDlg->DoModal(); // @pyseemfc CDialog|DoModal + GUI_END_SAVE; + DODECREF(self); + return Py_BuildValue("i", ret); + // @rdesc The return value from the dialog. This is the value passed to . +} +// @pymethod |PyCDialog|CreateWindow|Create a modeless window for the dialog box. +static PyObject *ui_dialog_create_window( PyObject *self, PyObject *args ) +{ + PyObject *obParent = NULL; + CWnd *pParent = NULL; + // @pyparm |obParent|None|The parent window for the new window + if (!PyArg_ParseTuple(args,"|O:CreateWindow", &obParent)) + return NULL; + if (obParent && obParent!=Py_None) { + // passed a DLL object. + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("passed object must be a PyCWnd object"); + pParent = (CWnd *)PyCWnd::GetPythonGenericWnd(obParent); + if (!pParent) + return NULL; + } + + CPythonDlg *pDlg; + if (!(pDlg=GetPythonDlg(self))) + return NULL; + if (pDlg->m_hWnd) + RETURN_ERR("win32ui: dialog already created"); + + PyCDialog *obDlg = (PyCDialog *)self; + if (obDlg->hTemplate==NULL) + RETURN_ERR("Internal Error - dialog has no template attached"); + int rc; + GUI_BGN_SAVE; + rc=pDlg->CreateIndirect(obDlg->hTemplate, pParent ); // @pyseemfc CDialog|CreateIndirect + GUI_END_SAVE; + if (!rc) + RETURN_ERR("CreateIndirect failed"); + RETURN_NONE; +} + +// @pymethod |PyCDialog|EndDialog|Ends a modal dialog box. +static PyObject *ui_dialog_end_dialog( PyObject *self, PyObject *args ) +{ + CDialog *pDlg; + if (!(pDlg=GetDialog(self))) + return NULL; + int result; + // @pyparm int|result||The value to be returned by the method. + if (!PyArg_ParseTuple( args, "i:EndDialog", &result)) + return NULL; + GUI_BGN_SAVE; + pDlg->EndDialog(result); // @pyseemfc CDialog|EndDialog + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCDialog|GotoDlgCtrl|Moves the focus to the specified control in the dialog box. +static PyObject *ui_dialog_goto_dlg_ctrl( PyObject *self, PyObject *args ) +{ + CDialog *pDlg; + if (!(pDlg=GetDialog(self))) + return NULL; + // @pyparm |control||The control to get the focus. + PyObject *obWindow; + if (!PyArg_ParseTuple( args, "O:GotoDlgCtrl", &obWindow)) + return NULL; + if (!ui_base_class::is_uiobject(obWindow, &PyCWnd::type)) + RETURN_TYPE_ERR("Argument must be a PyCWnd object"); + CWnd *pChild = PyCWnd::GetPythonGenericWnd(obWindow); + if (!pChild) + return NULL; + + GUI_BGN_SAVE; + pDlg->GotoDlgCtrl(pChild); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod (left, top, right, bottom)|PyCDialog|MapDialogRect|Converts the dialog-box units of a rectangle to screen units. +static PyObject *ui_dialog_map_dialog_rect( PyObject *self, PyObject *args ) +{ + CDialog *pDlg; + if (!(pDlg=GetDialog(self))) + return NULL; + // @pyparm (left, top, right, bottom)|rect||The rect to be converted + RECT rect; + if (!PyArg_ParseTuple( args, "(iiii):MapDialogRect", &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + GUI_BGN_SAVE; + pDlg->MapDialogRect(&rect); + GUI_END_SAVE; + return Py_BuildValue("iiii", rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod |PyCDialog|OnOK|Calls the default MFC OnOK handler. +static PyObject *ui_dialog_on_ok( PyObject *self, PyObject *args ) +{ + CProtectedDialog *pDlg; + if (!(pDlg=(CProtectedDialog *)GetPythonDlg(self))) + return NULL; + CHECK_NO_ARGS2(args, OnOK ); + // @xref + GUI_BGN_SAVE; + pDlg->BaseOnOK(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCDialog|OnCancel|Calls the default MFC OnCancel handler. +static PyObject *ui_dialog_on_cancel( PyObject *self, PyObject *args ) +{ + CPythonDlg *pDlg; + if (!(pDlg=GetPythonDlg(self))) + return NULL; + CHECK_NO_ARGS2(args, OnCancel ); + // @xref + GUI_BGN_SAVE; + // We call DoOnCancel rather than BaseOnCancel + // so we can cleanup dialog templates correctly. + pDlg->DoOnCancel(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCDialog|OnInitDialog|Calls the default MFC OnInitDialog handler. +static PyObject *ui_dialog_on_init_dialog( PyObject *self, PyObject *args ) +{ + CProtectedDialog *pDlg; + if (!(pDlg=(CProtectedDialog *)GetPythonDlg(self))) + return NULL; + CHECK_NO_ARGS2(args, OnInitDialog ); + GUI_BGN_SAVE; + int rc = pDlg->BaseOnInitDialog(); + GUI_END_SAVE; + // @xref + return Py_BuildValue("i", rc); +} + + +// inherited from window +// +/////////////////////////////////////// +// @object PyCDialog|A class which encapsulates an MFC CDialog object. Derived from a object. +static struct PyMethodDef ui_dialog_methods[] = { + {"CreateWindow", ui_dialog_create_window, 1}, // @pymeth CreateWindow|Creates a modless window for the dialog. + {"DoModal", ui_dialog_do_modal, 1}, // @pymeth DoModal|Creates a modal window for the dialog. + {"EndDialog", ui_dialog_end_dialog, 1}, // @pymeth EndDialog|Closes a modal dialog. + {"GotoDlgCtrl", ui_dialog_goto_dlg_ctrl, 1}, // @pymeth GotoDlgCtrl|Sets focus to a specific control. + {"MapDialogRect", ui_dialog_map_dialog_rect, 1}, // @pymeth MapDialogRect|Converts the dialog-box units of a rectangle to screen units. + {"OnCancel", ui_dialog_on_cancel, 1}, // @pymeth OnCancel|Calls the default MFC OnCancel handler. + {"OnOK", ui_dialog_on_ok, 1}, // @pymeth OnOK|Calls the default MFC OnOK handler. + {"OnInitDialog", ui_dialog_on_init_dialog, 1}, // @pymeth OnInitDialog|Calls the default MFC OnInitDialog handler. + {NULL, NULL} /* sentinel */ +}; + +ui_type_CObject PyCDialog::type("PyCDialog", + &PyCWnd::type, + RUNTIME_CLASS(CDialog), + sizeof(PyCDialog), + ui_dialog_methods, + GET_PY_CTOR(PyCDialog)); + +// @object PyCCommonDialog|An abstract class which encapsulates an MFC CCommonDialog object. Derived from a object. +static struct PyMethodDef ui_common_dialog_methods[] = { + { NULL, NULL } +}; + +ui_type_CObject PyCCommonDialog::type("PyCCommonDialog", + &PyCDialog::type, + NULL, // CCommonDialog doesnt have RTTI??? + sizeof(PyCCommonDialog), + ui_common_dialog_methods, + NULL); + +///////////////////////////////////////////////////////////////////// +// +// File Dialog object +// +////////////////////////////////////////////////////////////////////// +PyCFileDialog::PyCFileDialog() +{ + pObTitle = pObInitialDir = NULL; +} +PyCFileDialog::~PyCFileDialog() +{ + Py_XDECREF(pObTitle); + Py_XDECREF(pObInitialDir); +} + +// @pymethod |win32ui|CreateFileDialog|Creates a File Open/Save/etc Common Dialog. +PyObject *PyCFileDialog::ui_file_dialog_create( PyObject * /*self*/, PyObject *args ) +{ + int bFileOpen; + DWORD flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + char *szDefExt = NULL, *szFileName=NULL, *szFilter = NULL; + CWnd *pParent = NULL; // should mean same as GetApp()->m_pMainWnd + PyObject *obParent = Py_None; + + if (!PyArg_ParseTuple(args, "i|zzizO:CreateFileDialog", + &bFileOpen, // @pyparm int|bFileOpen||A flag indicating if the Dialog is a FileOpen or FileSave dialog. + &szDefExt, // @pyparm string|defExt|None|The default file extension for saved files. If None, no extension is supplied. + &szFileName, // @pyparm string|fileName|None|The initial filename that appears in the filename edit box. If None, no filename initially appears. + &flags, // @pyparm int|flags|win32con.OFN_HIDEREADONLY\|win32con.OFN_OVERWRITEPROMPT|The flags for the dialog. See the API documentation for full details. + &szFilter, // @pyparm string|filter|None|A series of string pairs that specify filters you can apply to the file. + // If you specify file filters, only selected files will appear + // in the Files list box. The first string in the string pair describes + // the filter; the second string indicates the file extension to use. + // Multiple extensions may be specified using ';' as the delimiter. + // The string ends with two '\|' characters. May be None. + &obParent )) // @pyparm |parent|None|The parent or owner window of the dialog. + return NULL; + + if (obParent != Py_None) { + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("Parent arg must be a PyCWnd object"); + if (!(pParent=(CWnd *)PyCWnd::GetPythonGenericWnd(obParent))) + return NULL; + } + + CFileDialog *pDlg = new CFileDialog( bFileOpen, szDefExt, szFileName, flags, szFilter, pParent ); + if (!pDlg) + RETURN_ERR("Creating CFileDialog failed"); // pyseemfc CFileCialog|CFileDialog + PyCFileDialog *newObj = + (PyCFileDialog *)ui_assoc_object::make( PyCFileDialog::type, pDlg); +// if (newObj) +// newObj->bManualDelete = TRUE; + return newObj; +} +// @pymethod string|PyCFileDialog|GetPathName|Retrives the path name from the file dialog. +static PyObject *ui_file_dialog_get_path_name( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetPathName); + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + GUI_BGN_SAVE; + CString cs = pDlg->GetPathName(); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)cs);// @pyseemfc CFileDialog|GetPathName +} +// @pymethod string|PyCFileDialog|GetFileName|Retrives the file name from the file dialog. +static PyObject *ui_file_dialog_get_file_name( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetFileName); + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + GUI_BGN_SAVE; + CString cs = pDlg->GetFileName(); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)cs);// @pyseemfc CFileDialog|GetFileName +} + +// @pymethod string|PyCFileDialog|GetPathNames|Retrieves the list of path names from the file dialog. +// @comm This method is useful when a multi-select dialog is used. +static PyObject *ui_file_dialog_get_path_names( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetPathName); + CFileDialog *pDlg = GetFileDialog(self); + CString str; + POSITION pos; + + if (!pDlg) + return NULL; + + PyObject *newOb = PyList_New(0); + GUI_BGN_SAVE; + pos = pDlg->GetStartPosition(); + GUI_END_SAVE; + + while (pos){ + GUI_BGN_SAVE; + str = pDlg->GetNextPathName(pos); + GUI_END_SAVE; + PyList_Append(newOb, Py_BuildValue("s",(const char *)str)); + } + return newOb;// @pyseemfc CFileDialog|GetPathNames +} + + +// @pymethod string|PyCFileDialog|GetFileExt|Retrives the file extension from the file dialog. +static PyObject *ui_file_dialog_get_file_ext( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetFileExt); + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + GUI_BGN_SAVE; + CString csRet = pDlg->GetFileExt(); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)csRet); // @pyseemfc CFileDialog|GetFileExt +} +// @pymethod string|PyCFileDialog|GetFileTitle|Retrives the file title from the file dialog. +static PyObject *ui_file_dialog_get_file_title( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetFileTitle); + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + GUI_BGN_SAVE; + CString csRet = pDlg->GetFileTitle(); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)csRet);// @pyseemfc CFileDialog|GetFileTitle +} +// @pymethod int|PyCFileDialog|GetReadOnlyPref|Retrives the value of the "Read Only" checkbox on the file dialog. +static PyObject *ui_file_dialog_get_ro_pref( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,GetReadOnlyPref); + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + GUI_BGN_SAVE; + BOOL bRet = pDlg->GetReadOnlyPref(); // @pyseemfc CFileDialog|GetReadOnlyPref + GUI_END_SAVE; + return Py_BuildValue("i",bRet); +} + +// @pymethod |PyCFileDialog|SetOFNTitle|Sets the Title for the dialog. +static PyObject *ui_file_dialog_set_ofn_title( PyObject *self, PyObject *args ) +{ + PyObject *ob; + // @pyparm string|title||The title for the dialog box. May be None. + if (!PyArg_ParseTuple(args, "O:SetOFNTitle", &ob)) + return NULL; + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + + if (ob!=Py_None && !PyString_Check(ob)) + RETURN_TYPE_ERR("Argument must be a string, or None"); + + PyCFileDialog *fdo = (PyCFileDialog *)self; + Py_XDECREF(fdo->pObTitle); + if (ob!=Py_None) { + fdo->pObTitle = ob; + Py_INCREF(fdo->pObTitle); + pDlg->m_ofn.lpstrTitle = PyString_AsString(ob); + } else { + fdo->pObTitle = NULL; + pDlg->m_ofn.lpstrTitle = NULL; + } + + RETURN_NONE; +} + +// @pymethod |PyCFileDialog|SetOFNInitialDir|Sets the initial directory for the dialog. +static PyObject *ui_file_dialog_set_ofn_initialdir( PyObject *self, PyObject *args ) +{ + PyObject *ob; + // @pyparm string|title||The initial directory for the dialog box. May be None. + if (!PyArg_ParseTuple(args, "O:SetOFNInitialDir", &ob)) + return NULL; + CFileDialog *pDlg = GetFileDialog(self); + if (!pDlg) + return NULL; + + if (ob!=Py_None && !PyString_Check(ob)) + RETURN_TYPE_ERR("Argument must be a string, or None"); + + PyCFileDialog *fdo = (PyCFileDialog *)self; + Py_XDECREF(fdo->pObInitialDir); + if (ob!=Py_None) { + fdo->pObInitialDir = ob; + Py_INCREF(fdo->pObInitialDir); + pDlg->m_ofn.lpstrInitialDir = PyString_AsString(ob); + } else { + fdo->pObInitialDir = NULL; + pDlg->m_ofn.lpstrInitialDir = NULL; + } + RETURN_NONE; +} + +/////////////////////////////////////// +// +// File Dialog Methods +// +// inherited from a Dialog +// +/////////////////////////////////////// +// @object PyCFileDialog|A class which encapsulates an MFC CFileDialog object. Derived from a object. +static struct PyMethodDef ui_file_dialog_methods[] = { + {"GetPathName", (PyCFunction)ui_file_dialog_get_path_name, 1}, // @pymeth GetPathName|Retrieves the path name. + {"GetFileName", (PyCFunction)ui_file_dialog_get_file_name, 1}, // @pymeth GetFileName|Retrieves the file name. + {"GetFileExt", (PyCFunction)ui_file_dialog_get_file_ext, 1}, // @pymeth GetFileExt|Retrieves the file extension. + {"GetFileTitle", (PyCFunction)ui_file_dialog_get_file_title, 1}, // @pymeth GetFileTitle|Retrieves the file title. + {"GetPathNames", (PyCFunction)ui_file_dialog_get_path_names, 1}, // @pymeth GetPathNames|Retrieves the list of path names from the file dialog. + {"GetReadOnlyPref", (PyCFunction)ui_file_dialog_get_ro_pref, 1}, // @pymeth GetReadOnlyPref|Retrieves the read-only preference. + {"SetOFNTitle", (PyCFunction)ui_file_dialog_set_ofn_title, 1}, // @pymeth SetOFNTitle|Sets the title for the dialog. + {"SetOFNInitialDir",(PyCFunction)ui_file_dialog_set_ofn_initialdir, 1}, // @pymeth SetOFNInitialDir|Sets the initial directory for the dialog. + {NULL, NULL} +}; + +ui_type_CObject PyCFileDialog::type("PyCFileDialog", + &PyCCommonDialog::type, + RUNTIME_CLASS(CFileDialog), + sizeof(PyCFileDialog), + ui_file_dialog_methods, + GET_PY_CTOR(PyCFileDialog)); + +///////////////////////////////////////////////////////////////////// +// +// Font Dialog object +// +////////////////////////////////////////////////////////////////////// +PyCFontDialog::PyCFontDialog() +{ + pInitLogFont = NULL; +} +PyCFontDialog::~PyCFontDialog() +{ + delete pInitLogFont; +} + +// @pymethod |win32ui|CreateFontDialog|Creates a font selection dialog box. +PyObject *PyCFontDialog::ui_font_dialog_create( PyObject * /*self*/, PyObject *args ) +{ + PyObject *obFont = Py_None; + DWORD flags = CF_EFFECTS | CF_SCREENFONTS; + PyObject *obDC = Py_None; + PyObject *obParent = Py_None; + CWnd *pParent = NULL; // should mean same as GetApp()->m_pMainWnd + LOGFONT *pFont = NULL; + CDC *pDC = NULL; + CHARFORMAT cf; + memset(&cf, 0, sizeof(cf)); + + cf.cbSize = sizeof(CHARFORMAT); + + if (!PyArg_ParseTuple(args, "|OiOO:CreateFontDialog", + &obFont, // @pyparm dict/tuple|font|None|A dictionary describing a LOGFONT, or a tuple describing a CHARFORMAT. + &flags, // @pyparm int|flags|win32con.CF_EFFECTS\|win32con.CF_SCREENFONTS|The choose-font flags to use. + &obDC, // @pyparm |dcPrinter|None|Show fonts available for the specified device. + &obParent )) // @pyparm |parent|None|The parent or owner window of the dialog. + return NULL; + + if (obParent != Py_None) { + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("Parent arg must be a PyCWnd object"); + if (!(pParent=(CWnd *)PyCWnd::GetPythonGenericWnd(obParent))) + return NULL; + } + BOOL bHaveCF = FALSE; + if (obFont != Py_None) { + if (PyTuple_Check(obFont)) { + if (!ParseCharFormatTuple( obFont, &cf)) + return NULL; + bHaveCF = TRUE; + } else if (PyMapping_Check( obFont )) { + pFont = new LOGFONT; + if (!DictToLogFont(obFont, pFont)) + return NULL; + } else { + RETURN_ERR("Unknown object type for font object"); + } + } + CFontDialog *pDlg; + if (bHaveCF) + pDlg = new CFontDialog( cf, flags, pDC, pParent ); + else + pDlg = new CFontDialog( pFont, flags, pDC, pParent ); + + if (!pDlg) { + delete pFont; + RETURN_ERR("Creating CFontDialog failed"); // pyseemfc CFontDialog|CFontDialog + } + PyCFontDialog *newObj = + (PyCFontDialog *)ui_assoc_object::make( PyCFontDialog::type, pDlg); + if (newObj && pFont) + newObj->pInitLogFont = pFont; + else + delete pFont; // may be NULL, but thats OK! + return newObj; +} + +#define MAKE_CSTRING_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CFontDialog *pDlg = GetFontDialog(self); \ + if (!pDlg) return NULL; \ + GUI_BGN_SAVE; \ + CString ret = pDlg->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("s",(const char *)ret); \ +} + +#define MAKE_INT_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CFontDialog *pDlg = GetFontDialog(self); \ + if (!pDlg) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pDlg->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +// @pymethod string|PyCFontDialog|GetFaceName|Returns the face name of the selected font. +// @pyseemfc CFontDialog|GetFaceName +MAKE_CSTRING_METH(ui_font_dialog_get_face_name,GetFaceName) +// @pymethod string|PyCFontDialog|GetStyleName|Returns the style name of the selected font. +// @pyseemfc CFontDialog|GetStyleName +MAKE_CSTRING_METH(ui_font_dialog_get_style_name,GetStyleName) + +// @pymethod int|PyCFontDialog|GetSize|Returns he font's size, in tenths of a point. +// @pyseemfc CFontDialog|GetSize +MAKE_INT_METH(ui_font_dialog_get_size,GetSize) + +// @pymethod int|PyCFontDialog|GetWeight|Returns the font's weight. +// @pyseemfc CFontDialog|GetWeight +MAKE_INT_METH(ui_font_dialog_get_weight,GetWeight) + +// @pymethod int|PyCFontDialog|IsStrikeOut|Determines whether the font is displayed with strikeout. +// @pyseemfc CFontDialog|IsStrikeOut +MAKE_INT_METH(ui_font_dialog_is_strikeout,IsStrikeOut) + +// @pymethod int|PyCFontDialog|IsUnderline|Determines whether the font is displayed with underline. +// @pyseemfc CFontDialog|IsUnderline +MAKE_INT_METH(ui_font_dialog_is_underline,IsUnderline) + +// @pymethod int|PyCFontDialog|IsBold|Determines whether the font is displayed bold. +// @pyseemfc CFontDialog|IsBold +MAKE_INT_METH(ui_font_dialog_is_bold,IsBold) + +// @pymethod int|PyCFontDialog|IsItalic|Determines whether the font is displayed with italic. +// @pyseemfc CFontDialog|IsItalic +MAKE_INT_METH(ui_font_dialog_is_italic,IsItalic) + +// @pymethod int|PyCFontDialog|DoModal|Displays a dialog and allows the user to make a selection. +// @pyseemfc CFontDialog|DoModal +MAKE_INT_METH(ui_font_dialog_do_modal,DoModal) + +// @pymethod int|PyCFontDialog|GetColor|Determines the color of the selected font. +// @pyseemfc CFontDialog|GetColor +MAKE_INT_METH(ui_font_dialog_get_color,GetColor) + +// @pymethod dict|PyCFontDialog|GetCurrentFont|Returns a dictionary describing the current font. +static PyObject *ui_font_dialog_get_current_font( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetCurrentFont); + CFontDialog *pDlg = GetFontDialog(self); + if (!pDlg) + return NULL; + if (pDlg->m_hWnd == NULL) { + return LogFontToDict(pDlg->m_lf); + } else { + LOGFONT lf; + GUI_BGN_SAVE; + pDlg->GetCurrentFont(&lf); + GUI_END_SAVE; + // @pyseemfc CFontDialog|GetCurrentFont + return LogFontToDict(lf); + } + +} + +// @pymethod tuple|PyCFontDialog|GetCharFormat|Returns the font selection in a CHARFORMAT tuple. +static PyObject *ui_font_dialog_get_char_format( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetCharFormat); + CFontDialog *pDlg = GetFontDialog(self); + if (!pDlg) + return NULL; + + CHARFORMAT fmt; + memset(&fmt, 0, sizeof(fmt)); + + GUI_BGN_SAVE; + pDlg->GetCharFormat(fmt); + GUI_END_SAVE; + // @pyseemfc CFontDialog|GetCharFormat + return MakeCharFormatTuple(&fmt); +} + +// @object PyCFontDialog|A class which encapsulates an MFC CFontDialog object. Derived from a object. +static struct PyMethodDef ui_font_dialog_methods[] = { + {"DoModal", ui_font_dialog_do_modal, 1}, // @pymeth DoModal|Displays a dialog and allows the user to make a selection. + {"GetCurrentFont", ui_font_dialog_get_current_font, 1}, // @pymeth GetCurrentFont|Returns a dictionary describing the current font. + {"GetCharFormat", ui_font_dialog_get_char_format, 1}, // @pymeth GetCharFormat|Returns the font selection in a CHARFORMAT tuple. + {"GetColor", ui_font_dialog_get_color, 1}, // @pymeth GetColor|Determines the color of the selected font. + {"GetFaceName", ui_font_dialog_get_face_name, 1}, // @pymeth GetFaceName|Returns the face name of the selected font. + {"GetStyleName", ui_font_dialog_get_style_name, 1}, // @pymeth GetStyleName|Returns the style name of the selected font. + {"GetSize", ui_font_dialog_get_size, 1 }, // @pymeth GetSize|Returns he font's size, in tenths of a point. + {"GetWeight", ui_font_dialog_get_weight, 1 }, // @pymeth GetWeight|Returns the font's weight. + {"IsStrikeOut", ui_font_dialog_is_strikeout, 1 }, // @pymeth IsStrikeOut|Determines whether the font is displayed with strikeout. + {"IsUnderline", ui_font_dialog_is_underline, 1 }, // @pymeth IsUnderline|Determines whether the font is displayed with underline. + {"IsBold", ui_font_dialog_is_bold, 1 }, // @pymeth IsBold|Determines whether the font is displayed bold. + {"IsItalic", ui_font_dialog_is_italic, 1 }, // @pymeth IsItalic|Determines whether the font is displayed with italic. + {NULL, NULL} +}; + +ui_type_CObject PyCFontDialog::type("PyCFontDialog", + &PyCCommonDialog::type, + RUNTIME_CLASS(CFontDialog), + sizeof(PyCFontDialog), + ui_font_dialog_methods, + GET_PY_CTOR(PyCFontDialog)); + +///////////////////////////////////////////////////////////////////// +// +// Color Dialog object +// +////////////////////////////////////////////////////////////////////// +PyCColorDialog::PyCColorDialog() +{ +} +PyCColorDialog::~PyCColorDialog() +{ +} + +// @pymethod |win32ui|CreateColorDialog|Creates a color selection dialog box. +PyObject *PyCColorDialog::create( PyObject * /*self*/, PyObject *args ) +{ + int color = 0; + DWORD flags = 0; + PyObject *obParent = Py_None; + CWnd *pParent = NULL; // should mean same as GetApp()->m_pMainWnd + + if (!PyArg_ParseTuple(args, "|iiO:CreateColorDialog", + &color, // @pyparm int|initColor|0|The initial color. + &flags, // @pyparm int|flags|0|The choose-color flags to use. + &obParent )) // @pyparm |parent|None|The parent or owner window of the dialog. + return NULL; + + if (obParent != Py_None) { + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("Parent arg must be a PyCWnd object"); + if (!(pParent=(CWnd *)PyCWnd::GetPythonGenericWnd(obParent))) + return NULL; + } + CColorDialog *pDlg = new CColorDialog( color, flags, pParent ); + if (!pDlg) { + RETURN_ERR("Creating CColorDialog failed"); // pyseemfc CColorDialog|CColorDialog + } + PyCColorDialog *newObj = + (PyCColorDialog *)ui_assoc_object::make( PyCColorDialog::type, pDlg); + return newObj; +} + +#undef MAKE_INT_METH +#define MAKE_INT_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CColorDialog *pDlg = GetColorDialog(self); \ + if (!pDlg) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pDlg->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +// @pymethod int|PyCColorDialog|DoModal|Displays a dialog and allows the user to make a selection. +// @pyseemfc CColorDialog|DoModal +MAKE_INT_METH(ui_color_dialog_do_modal,DoModal) + +// @pymethod int|PyCColorDialog|GetColor|Determines the selected color. +// @pyseemfc CColorDialog|GetColor +MAKE_INT_METH(ui_color_dialog_get_color,GetColor) + +// @pymethod int|PyCColorDialog|GetSavedCustomColors|Returns the saved custom colors. +static PyObject *ui_color_dialog_get_saved_custom_colors( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetSavedCustomColors); + CColorDialog *pDlg = GetColorDialog(self); + if (!pDlg) + return NULL; + // @pyseemfc CColorDialog|GetSavedCustomColors + GUI_BGN_SAVE; + COLORREF*prc = pDlg->GetSavedCustomColors(); + GUI_END_SAVE; + return PyInt_FromLong((long)*prc); +} + +// @pymethod |PyCColorDialog|SetCurrentColor|Sets the currently selected color. +static PyObject *ui_color_dialog_set_current_color( PyObject *self, PyObject *args ) +{ + int color; + // @pyparm int|color||The color to set. + if (!PyArg_ParseTuple(args, "i", &color)) + return NULL; + CColorDialog *pDlg = GetColorDialog(self); + if (!pDlg) + return NULL; + // @pyseemfc CColorDialog|SetCurrentColor + GUI_BGN_SAVE; + pDlg->SetCurrentColor((COLORREF)color); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCColorDialog|A class which encapsulates an MFC CColorDialog object. Derived from a object. +static struct PyMethodDef ui_color_dialog_methods[] = { + {"GetColor", ui_color_dialog_get_color, 1}, // @pymeth GetColor|Determines the selected color. + {"DoModal", ui_color_dialog_do_modal, 1}, // @pymeth DoModal|Displays a dialog and allows the user to make a selection. + {"GetSavedCustomColors",ui_color_dialog_get_saved_custom_colors, 1}, // @pymeth GetSavedCustomColors|Returns the saved custom colors. + {"SetCurrentColor", ui_color_dialog_set_current_color, 1}, // @pymeth SetCurrentColor|Sets the currently selected color. + {NULL, NULL} +}; + +ui_type_CObject PyCColorDialog::type("PyCColorDialog", + &PyCCommonDialog::type, + RUNTIME_CLASS(CColorDialog), + sizeof(PyCColorDialog), + ui_color_dialog_methods, + GET_PY_CTOR(PyCColorDialog)); + +///////////////////////////////////////////////////////////////////// +// +// Print Dialog object +// +////////////////////////////////////////////////////////////////////// +//Derived CPrintDialog class + +PyCPrintDialog::PyCPrintDialog() +{ +} +PyCPrintDialog::~PyCPrintDialog() +{ +} + +// @pymethod |win32ui|CreatePrintDialog|Creates a print dialog object. +PyObject *PyCPrintDialog::create( PyObject *self, PyObject *args ) +{ + int idRes; + BOOL bPrintSetupOnly = FALSE; + DWORD dwFlags = PD_ALLPAGES|PD_USEDEVMODECOPIES|PD_NOPAGENUMS|PD_HIDEPRINTTOFILE|PD_NOSELECTION; + PyObject *obParent = NULL; + CWnd* pParentWnd = NULL; + HINSTANCE hMod = NULL, hOldRes = NULL; + PyObject *obDLL = NULL; + if (!PyArg_ParseTuple(args,"i|iiOO:CreatePrintDialog", + &idRes, // @pyparm int|idRes||The ID of the dialog resource to load. + &bPrintSetupOnly,// @pyparm int|bPrintSetupOnly|FALSE|Specifies whether the standard Windows Print dialog box or Print Setup dialog box is displayed. + &dwFlags,// @pyparm int|dwFlags|PD_ALLPAGES\|PD_USEDEVMODECOPIES\|PD_NOPAGENUMS\|PD_HIDEPRINTTOFILE\|PD_NOSELECTION|One or more flags you can use to customize the settings of the dialog box, combined using the bitwise OR operator. + &obParent,// @pyparm |parent|None|A pointer to the dialog box parent or owner window. + &obDLL))// @pyparm |dll|None|The DLL object to load the dialog from. + return NULL; + if (obDLL && obDLL!=Py_None) { + // passed a DLL object. + if (!ui_base_class::is_uiobject(obDLL, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL"); + hMod = ((dll_object *)obDLL)->GetDll(); + if (hMod==NULL) + RETURN_ERR("Can not load from an uninitialised DLL object"); + } + if (obParent && obParent!=Py_None) { + // passed a PyCWnd object. + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("passed object must be a PyCWnd object"); + pParentWnd = (CWnd *)PyCWnd::GetPythonGenericWnd(obParent); + if (!pParentWnd) + return NULL; + } + if (hMod==NULL) + hMod = AfxFindResourceHandle( MAKEINTRESOURCE(idRes), RT_DIALOG ); + else { + hOldRes = AfxGetResourceHandle(); + AfxSetResourceHandle(hMod); + } + + HGLOBAL hGlob; + HRSRC hrsrc; + hrsrc = ::FindResourceEx( hMod, RT_DIALOG, + MAKEINTRESOURCE(idRes), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + if (hrsrc) + hGlob = ::LoadResource( hMod, hrsrc ); + if (hOldRes) + AfxSetResourceHandle(hOldRes); + if (hrsrc==NULL) + RETURN_API_ERR("FindResourceEx"); + if (hGlob==NULL) + RETURN_API_ERR("LoadResource"); + + GUI_BGN_SAVE; + CPrintDialog *pDlg = new CPythonPrtDlg(bPrintSetupOnly, dwFlags, pParentWnd); + GUI_END_SAVE; + PyCPrintDialog *ret = (PyCPrintDialog *)ui_assoc_object::make( PyCPrintDialog::type, pDlg); + if (ret) { + ret->hTemplate = hGlob; + ret->hInstance = hMod; + } + return ret; +} + +#undef MAKE_INT_METH +#define MAKE_INT_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args,mfcName); \ + CPrintDialog *pDlg = GetPrintDialog(self); \ + if (!pDlg) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pDlg->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue("i",ret); \ +} + +// @object PyCPrintDialog|An class which encapsulates an MFC CPrintDialog object. +// @base PyCPrintDialog|PyCCommonDialog +static struct PyMethodDef ui_print_dialog_methods[] = { + { NULL, NULL } +}; + +ui_type_CObject PyCPrintDialog::type("PyCPrintDialog", + &PyCCommonDialog::type, + RUNTIME_CLASS(CPrintDialog), + sizeof(PyCPrintDialog), + ui_print_dialog_methods, + GET_PY_CTOR(PyCPrintDialog)); diff --git a/Pythonwin/win32dlg.h b/Pythonwin/win32dlg.h new file mode 100644 index 0000000000..7b89844ea6 --- /dev/null +++ b/Pythonwin/win32dlg.h @@ -0,0 +1,91 @@ +// dialog objects + +// hack the associations a bit. When the dialog window closes, I dont +// destroy the association. + +///////////////////////////////////////////////////////// +// +// dialog +class PYW_EXPORT PyCDialog : public PyCWnd { +public: + MAKE_PY_CTOR(PyCDialog) + static PyObject *PyCDialog::create( PyObject *self, PyObject *args ); + static PyObject *PyCDialog::createIndirect( PyObject *self, PyObject *args ); + PyObject *ddlist; + PyObject *dddict; + HGLOBAL hTemplate; + HINSTANCE hInstance; // If known, the DLL we loaded from. + HGLOBAL hSaved; + static ui_type_CObject type; +protected: + PyCDialog(); + virtual ~PyCDialog(); + virtual void DoKillAssoc( BOOL bDestructing=FALSE); + virtual void SetAssocInvalid() {return; }//ignore +public: + virtual PyObject *getattr(char *name); +}; +//////////////////////////////////////////////////////// +// +// Common dialog base. +class PYW_EXPORT PyCCommonDialog : public PyCDialog { +public: + static ui_type_CObject type; +protected: +}; + +//////////////////////////////////////////////////////// +// +// file dialog +class PyCFileDialog : public PyCCommonDialog { +public: + MAKE_PY_CTOR(PyCFileDialog) + static PyObject *ui_file_dialog_create( PyObject *self, PyObject *args );// create an actual object. + PyObject *pObTitle; + PyObject *pObInitialDir; + static ui_type_CObject type; +protected: + PyCFileDialog(); + ~PyCFileDialog(); +}; + +//////////////////////////////////////////////////////// +// +// font dialog +class PyCFontDialog : public PyCCommonDialog { +public: + MAKE_PY_CTOR(PyCFontDialog) + static PyObject *ui_font_dialog_create( PyObject *self, PyObject *args );// create an actual object. + LOGFONT *pInitLogFont; + static ui_type_CObject type; +protected: + PyCFontDialog(); + ~PyCFontDialog(); +}; + + +//////////////////////////////////////////////////////// +// +// color dialog +class PyCColorDialog : public PyCCommonDialog { +public: + MAKE_PY_CTOR(PyCColorDialog) + static PyObject *create( PyObject *self, PyObject *args );// create an actual object. + static ui_type_CObject type; +protected: + PyCColorDialog(); + ~PyCColorDialog(); +}; + +//////////////////////////////////////////////////////// +// +// print dialog +class PyCPrintDialog : public PyCCommonDialog { +public: + MAKE_PY_CTOR(PyCPrintDialog) + static PyObject *create( PyObject *self, PyObject *args ); + static ui_type_CObject type; +protected: + PyCPrintDialog(); + virtual ~PyCPrintDialog(); +}; diff --git a/Pythonwin/win32dlgbar.cpp b/Pythonwin/win32dlgbar.cpp new file mode 100644 index 0000000000..53a172d2e3 --- /dev/null +++ b/Pythonwin/win32dlgbar.cpp @@ -0,0 +1,91 @@ +/* + DialogBar class for Pythonwin + + Created April 1998 by Mark Hammond. + +*/ +// @doc +#include "stdafx.h" +#include "win32win.h" +#include "win32toolbar.h" +#include "win32dlgbar.h" + + +CDialogBar *PyCDialogBar::GetDialogBar (PyObject *self) +{ + return (CDialogBar *)GetGoodCppObject( self, &type); +} + +///////////////////////////////////////////////////////////////////// +// +// Dialog Bar object +// +////////////////////////////////////////////////////////////////////// + +// @pymethod |win32ui|CreateDialogBar|Creates a object. +PyObject *PyCDialogBar::create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "Create"); + GUI_BGN_SAVE; + CDialogBar *db = new CDialogBar(); + GUI_END_SAVE; + if (db==NULL) { + PyErr_SetString(PyExc_MemoryError, "Allocating CDialogBar object"); + return NULL; + } + db->m_bAutoDelete = TRUE; // let MFC handle deletion + return ui_assoc_object::make (PyCDialogBar::type, db)->GetGoodRet(); +} + +// @pymethod |PyCDialogBar|CreateWindow|Creates the window for the object. +static PyObject *PyCDialogBar_CreateWindow(PyObject *self, PyObject *args) +{ + BOOL bHaveSz = TRUE; + char *szTemplate; + UINT style, id, idTemplate; + PyObject *obParent; + // @pyparm |parent||The parent window + // @pyparm string|template||The template to load the resource from + // @pyparm int|style||The style for the window + // @pyparm int|id||The ID of the window + if (!PyArg_ParseTuple(args, "Osii", &obParent, &szTemplate, &style, &id)) { + PyErr_Clear(); + // @pyparmalt1 |parent||The parent window + // @pyparmalt1 int|resourceId||The resource ID to load the resource from + // @pyparmalt1 int|style||The style for the window + // @pyparmalt1 int|id||The ID of the window + if (!PyArg_ParseTuple(args, "Oiii", &obParent, &idTemplate, &style, &id)) + RETURN_TYPE_ERR("CreateWindow arguments must have format of either 'Osii' or 'Oiii'"); + bHaveSz = FALSE; + } + CDialogBar *pDialog = PyCDialogBar::GetDialogBar(self); + if (pDialog==NULL) return NULL; + CWnd *pParent = NULL; + if (obParent != Py_None) { + pParent = PyCWnd::GetPythonGenericWnd(obParent, &PyCWnd::type); + if (pParent==NULL) + RETURN_TYPE_ERR("The parent window is not a valid PyCWnd"); + } + GUI_BGN_SAVE; + BOOL rc = bHaveSz ? + pDialog->Create(pParent, szTemplate, style, id) : + pDialog->Create(pParent, idTemplate, style, id); + GUI_END_SAVE; + if (!rc) + RETURN_ERR("CDialogBar::Create failed"); + RETURN_NONE; +} + +// @object PyCDialogBar|A class which encapsulates an MFC . Derived from a object. +static struct PyMethodDef PyCDialogBar_methods[] = +{ + { "CreateWindow", PyCDialogBar_CreateWindow, 1}, // @pymeth CreateWindow|Creates the window for the object. + { NULL, NULL } +}; + +ui_type_CObject PyCDialogBar::type ("PyCDialogBar", + &PyCControlBar::type, + RUNTIME_CLASS(CDialogBar), + sizeof(PyCDialogBar), + PyCDialogBar_methods, + GET_PY_CTOR(PyCDialogBar)); diff --git a/Pythonwin/win32dlgbar.h b/Pythonwin/win32dlgbar.h new file mode 100644 index 0000000000..81d21ad062 --- /dev/null +++ b/Pythonwin/win32dlgbar.h @@ -0,0 +1,15 @@ +// CDialogBar support for Pythonwin + +class PyCDialogBar : public PyCControlBar +{ +public: + MAKE_PY_CTOR(PyCDialogBar) + static ui_type_CObject type; + static PyObject *create (PyObject *self, PyObject *args); + static CDialogBar *GetDialogBar (PyObject *self); + +protected: + // virtual CString repr(); maybe add later to show id? + +private: +}; diff --git a/Pythonwin/win32dlgdyn.cpp b/Pythonwin/win32dlgdyn.cpp new file mode 100644 index 0000000000..92ace0515c --- /dev/null +++ b/Pythonwin/win32dlgdyn.cpp @@ -0,0 +1,833 @@ +/* + * win32dlgdyn.cpp - Dynamic dialog creation + * + * Copyright (C) 1995 by Motek Information Systems, Beverly Hills, CA, USA + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice and the two paragraphs following + * it appear in all copies, and that the name of Motek Information Systems + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * MOTEK INFORMATION SYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL MOTEK BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Curt Hagenlocher + */ + +// @doc - Autoduck! + +#include "stdafx.h" + +#include "win32dlgdyn.h" + +#ifdef WIN32 +#define _RES(x) L ## x +#define RESCHAR WCHAR +#define RESSTR LPWSTR +#define RESCSTR LPCWSTR +#define MAKERESSTR(x) MakeResStr(x) +#define FREERESSTR(x) FreeResStr(x) +#define strcpyR wcscpy +#define strlenR wcslen +#define alloclenR 2*wcslen +#else +#define _RES(x) x +#define RESCHAR char +#define RESSTR LPSTR +#define RESCSTR LPCSTR +#define MAKERESSTR(x) x +#define FREERESSTR(x) NULL +#define strcpyR strcpy +#define strlenR strlen +#define alloclenR strlen +#endif + +#ifdef WIN32 +static void DwordAlign(PCHAR *ptr) +{ + int offset = ((int)*ptr) & 0x03; + if (offset > 0) + { + *ptr += (4 - offset); + } +} + +RESSTR MakeResStr(LPCSTR x) +{ + if (!x) + { + return NULL; + } + + int result = ::MultiByteToWideChar(CP_ACP, 0, x, -1, NULL, 0); + if (result <= 0) + { + return NULL; + } + WCHAR *ws = new WCHAR[result+1]; + result = ::MultiByteToWideChar(CP_ACP, 0, x, -1, ws, result+1); + if (result <= 0) + { + delete ws; + return NULL; + } + ws[result] = 0; + return ws; +} + +void FreeResStr(RESCSTR x) +{ + if (x) + { + delete (LPWSTR)x; + } +} + +LPSTR UnmakeResStr(RESCSTR x) +{ + if (x == NULL) + { + return NULL; + } + + int result = ::WideCharToMultiByte(CP_ACP, 0, x, -1, NULL, 0, NULL, NULL); + if (result <= 0) + { + return NULL; + } + char *s = new char[result+1]; + result = ::WideCharToMultiByte(CP_ACP, 0, x, -1, s, result+1, NULL, NULL); + if (result <= 0) + { + delete s; + return NULL; + } + s[result] = 0; + + return s; +} + +#endif + +/* + * CPythonDialogTemplate + */ + +CPythonDialogTemplate::CPythonDialogTemplate(LPCSTR cpin, DLGTEMPLATE *tmpl, WORD fontsize, + LPCSTR font, LPCSTR mnu, LPCSTR wc) +{ + static const RESCHAR BASED_CODE _font[] = _RES("MS Sans Serif"); + + RESCSTR thefont = font ? MAKERESSTR(font) : _font; + RESCSTR capt = MAKERESSTR(cpin); + RESCSTR menu = MAKERESSTR(mnu); + RESCSTR wclass = MAKERESSTR(wc); + + m_alloc = sizeof(DLGTEMPLATE) + alloclenR(capt) + alloclenR(thefont) + 20; + if (HIWORD(menu) != 0) + { + m_alloc += alloclenR(menu); + } + if (HIWORD(wclass) != 0) + { + m_alloc += alloclenR(wclass); + } + + m_h = GlobalAlloc(GHND, m_alloc); + DLGTEMPLATE *hdr = (DLGTEMPLATE*)GlobalLock(m_h); + memcpy(hdr, tmpl, sizeof(DLGTEMPLATE)); + hdr->cdit = 0; + RESSTR ptr = (RESSTR)((char*)hdr + sizeof(DLGTEMPLATE)); + if (HIWORD(menu) != 0) + { + strcpyR(ptr, menu); + ptr += strlenR(ptr) + 1; + } + else if (LOWORD(menu) != 0) + { + *ptr++ = (RESCHAR)-1; + *(WORD*)ptr = LOWORD(menu); + ptr += (sizeof(WORD) / sizeof(RESCHAR)); + } + else + { + *ptr++ = 0; + } + if (HIWORD(wclass) != 0) + { + strcpyR(ptr, wclass); + ptr += strlenR(ptr) + 1; + } + else if (LOWORD(wclass) != 0) + { + *ptr++ = (RESCHAR)-1; + *(WORD*)ptr = LOWORD(wclass); + ptr += (sizeof(WORD) / sizeof(RESCHAR)); + } + else + { + *ptr++ = 0; + } + strcpyR(ptr, capt); + ptr += strlenR(capt) + 1; + if (hdr->style & DS_SETFONT) + { + *(WORD*)ptr = fontsize; + ptr += (sizeof(WORD) / sizeof(RESCHAR)); + strcpyR(ptr, thefont); + ptr += strlenR(ptr) + 1; + } +#ifdef WIN32 + DwordAlign((PCHAR*)&ptr); +#endif + int len = (BYTE*)ptr - (BYTE*)hdr; + ASSERT(len <= m_alloc); + m_len = len; +#ifdef WIN32 + m_ptr = hdr; +#else + GlobalUnlock(m_h); +#endif + + if (font) + { + FREERESSTR(thefont); + } + FREERESSTR(capt); + FREERESSTR(menu); + FREERESSTR(wclass); +} + +BOOL CPythonDialogTemplate::Add(LPCSTR wc, DLGITEMTEMPLATE *tmpl, LPCSTR text, + int datalen, BYTE *data) +{ +#ifdef WIN32 + GlobalUnlock(m_h); +#endif + RESCSTR wclass = MAKERESSTR(wc); + RESCSTR txt = MAKERESSTR(text); + int len = sizeof(DLGITEMTEMPLATE) + alloclenR(wclass) + datalen + 20; + if (txt) + { + len += alloclenR(txt); + } + if (m_len + len > m_alloc) + { + m_alloc += len; + m_h = GlobalReAlloc(m_h, m_alloc, 0); + ASSERT(m_h); + } + + DLGTEMPLATE *hdr = (DLGTEMPLATE*)GlobalLock(m_h); + hdr->cdit++; + DLGITEMTEMPLATE *ctrl = (DLGITEMTEMPLATE*)((char*)hdr + m_len); + memcpy(ctrl, tmpl, sizeof(DLGITEMTEMPLATE)); + RESSTR ptr = (RESSTR)((char*)ctrl + sizeof(DLGITEMTEMPLATE)); + strcpyR(ptr, wclass); + ptr += strlenR(ptr) + 1; + if (txt) + { + strcpyR(ptr, txt); + ptr += strlenR(ptr) + 1; + } + else + { + *ptr++ = 0; + } +#ifdef WIN32 + *ptr++ = (WORD)datalen; +#else + *ptr++ = (char)(BYTE)datalen; +#endif + if (datalen) + { + ASSERT(data); + memcpy(ptr, data, datalen); + ptr = (RESSTR)(((BYTE*)ptr) + datalen); + } +#ifdef WIN32 + DwordAlign((PCHAR*)&ptr); +#endif + len = (BYTE*)ptr - (BYTE*)hdr; + ASSERT(len <= m_alloc); + m_len = len; +#ifdef WIN32 + m_ptr = hdr; +#else + GlobalUnlock(m_h); +#endif + FREERESSTR(txt); + FREERESSTR(wclass); + + return TRUE; +} + +BOOL CPythonDialogTemplate::Add(BYTE wclass, DLGITEMTEMPLATE *tmpl, LPCSTR text) +{ +#ifdef WIN32 + GlobalUnlock(m_h); +#endif + RESCSTR txt = MAKERESSTR(text); + int len = sizeof(DLGITEMTEMPLATE) + 20; + if (txt) + { + len += alloclenR(txt); + } + if (m_len + len > m_alloc) + { + m_alloc += len; + m_h = GlobalReAlloc(m_h, m_alloc, 0); + ASSERT(m_h); + } + + DLGTEMPLATE *hdr = (DLGTEMPLATE*)GlobalLock(m_h); + hdr->cdit++; + DLGITEMTEMPLATE *ctrl = (DLGITEMTEMPLATE*)((char*)hdr + m_len); + memcpy(ctrl, tmpl, sizeof(DLGITEMTEMPLATE)); + RESSTR ptr = (RESSTR)((char*)ctrl + sizeof(DLGITEMTEMPLATE)); +#ifdef WIN32 + *ptr++ = (WORD)-1; + *ptr++ = (WORD)wclass; +#else + *ptr++ = (BYTE)wclass; +#endif + if (txt) + { + strcpyR(ptr, txt); + ptr += strlenR(ptr) + 1; + } + else + { + *ptr++ = 0; + } + *ptr++ = 0; +#ifdef WIN32 + DwordAlign((PCHAR*)&ptr); +#endif + len = (BYTE*)ptr - (BYTE*)hdr; + ASSERT(len <= m_alloc); + m_len = len; +#ifdef WIN32 + m_ptr = hdr; +#else + GlobalUnlock(m_h); +#endif + FREERESSTR(txt); + + return TRUE; +} + +void CPythonDialogTemplate::Get(DLGTEMPLATE *tmpl) +{ + DLGTEMPLATE *hdr = (DLGTEMPLATE*)GlobalLock(m_h); + memcpy(tmpl, hdr, sizeof(DLGTEMPLATE)); + GlobalUnlock(m_h); +} + +void CPythonDialogTemplate::Set(DLGTEMPLATE *tmpl) +{ + DLGTEMPLATE *hdr = (DLGTEMPLATE*)GlobalLock(m_h); + memcpy(hdr, tmpl, sizeof(DLGTEMPLATE)); + GlobalUnlock(m_h); +} + +HGLOBAL CPythonDialogTemplate::ClaimTemplate() +{ + register HGLOBAL h = m_h; + m_h = NULL; + m_alloc = 0; + m_len = 0; +#ifdef WIN32 + m_ptr = 0; + GlobalUnlock(h); +#endif + return h; +} + +CPythonDialogTemplate::~CPythonDialogTemplate() +{ + if (m_h) + { +#ifdef WIN32 + GlobalUnlock(m_h); +#endif + GlobalFree(m_h); + } +} + + +#if defined(WIN32) && !defined(NO_PYTHON) + +/////////////////////////////////////////////////////////////////////////// +// Python-specific dialog code + +static BOOL Py_GetAsDWORD(PyObject *obj, DWORD *ptr) +{ + int i; + if (PyArg_GetInt(obj, &i)) + { + *ptr = (DWORD)i; + return TRUE; + } + if (!PyLong_Check(obj)) + return FALSE; + double dval = PyLong_AsDouble(obj); + if (dval < 0 || dval > (double)ULONG_MAX) + return FALSE; + *(long*)ptr = (long)dval; + return TRUE; +} + +static void FillList(PyObject *list, int n) +{ + int size = PyList_Size(list); + while (n > size) + { + Py_INCREF(Py_None); + PyList_Append(list, Py_None); + size++; + } +} + +static PyObject *MakeResName(RESSTR *val) +{ + RESSTR ptr = *val; + PyObject *obj = NULL; + if (*ptr == (WORD)-1) + { + ptr++; + obj = PyInt_FromLong((WORD)*ptr++); + } + else if (*ptr != (WORD)0) + { + LPSTR wc = UnmakeResStr(ptr); + obj = PyString_FromString(wc); + delete wc; + ptr += strlenR(ptr) + 1; + } + else + ptr++; + *val = ptr; + return obj; +} + +// Given a pointer to a dialog hdr template, return a Python list to match it +static PyObject *MakeListFromDlgHdr(LPVOID *tplin, int &items) +{ + PyObject *list = PyList_New(0); + DLGTEMPLATE *tpl = (LPDLGTEMPLATE)*tplin; + + RESSTR ptr = (RESSTR)((char*)tpl + sizeof(DLGTEMPLATE)); + // DwordAlign((PCHAR*)&ptr); + PyObject *menuObj = MakeResName(&ptr); + PyObject *classObj = MakeResName(&ptr); + + // Parameter 0 - Caption + LPSTR txt = UnmakeResStr(ptr); + PyList_Append(list, PyString_FromString(txt)); + delete txt; + ptr += strlenR(ptr) + 1; + + // Parameter 1 - Bounds + PyObject *rect = PyTuple_New(4); + PyTuple_SetItem(rect, 0, PyInt_FromLong(tpl->x)); + PyTuple_SetItem(rect, 1, PyInt_FromLong(tpl->y)); + PyTuple_SetItem(rect, 2, PyInt_FromLong(tpl->cx)); + PyTuple_SetItem(rect, 3, PyInt_FromLong(tpl->cy)); + PyList_Append(list, rect); + + // Parameter 2 - Style + PyList_Append(list, PyLong_FromDouble(tpl->style)); + + // Parameter 3 - Extended Style + if (tpl->dwExtendedStyle) + PyList_Append(list, PyLong_FromDouble(tpl->dwExtendedStyle)); + + // Parameter 4 - Font tuple + if (tpl->style & DS_SETFONT) + { + FillList(list, 4); + WORD fontsize = (WORD)*ptr++; + LPSTR txt = UnmakeResStr(ptr); + PyObject *tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, PyInt_FromLong(fontsize)); + PyTuple_SetItem(tuple, 1, PyString_FromString(txt)); + PyList_Append(list, tuple); + delete txt; + ptr += strlenR(ptr) + 1; + } + + // Parameter 5 - Menu + if (menuObj) + { + FillList(list, 5); + PyList_Append(list, menuObj); + } + + // Parameter 6 - Window Class + if (classObj) + { + FillList(list, 6); + PyList_Append(list, classObj); + } + + DwordAlign((PCHAR*)&ptr); + *tplin = ptr; + items = tpl->cdit; + + return list; +} + +// Given a pointer to a dialog item template, return a Python list to match it +static PyObject *MakeListFromDlgItem(LPVOID *tplin) +{ + PyObject *list = PyList_New(0); + DLGITEMTEMPLATE *tpl = (LPDLGITEMTEMPLATE)*tplin; + + // Parameter 0 - Window class + RESSTR ptr = (RESSTR)((char*)tpl + sizeof(DLGITEMTEMPLATE)); + if (*ptr == (WORD)-1) + { + ptr++; + PyList_Append(list, PyInt_FromLong((WORD)*ptr++)); + } + else + { + LPSTR wc = UnmakeResStr(ptr); + PyList_Append(list, PyString_FromString(wc)); + delete wc; + ptr += strlenR(ptr) + 1; + } + + // Parameter 1 - Text + LPSTR txt = UnmakeResStr(ptr); + PyList_Append(list, PyString_FromString(txt)); + delete txt; + ptr += strlenR(ptr) + 1; + + // Parameter 2 - ID + PyList_Append(list, PyInt_FromLong(tpl->id)); + + // Parameter 3 - Bounds + PyObject *rect = PyTuple_New(4); + PyTuple_SetItem(rect, 0, PyInt_FromLong(tpl->x)); + PyTuple_SetItem(rect, 1, PyInt_FromLong(tpl->y)); + PyTuple_SetItem(rect, 2, PyInt_FromLong(tpl->cx)); + PyTuple_SetItem(rect, 3, PyInt_FromLong(tpl->cy)); + PyList_Append(list, rect); + + // Parameter 4 - Style + PyList_Append(list, PyLong_FromDouble(tpl->style)); + + WORD datalen = *ptr++; + // Parameter 5 - Extended Style + if (tpl->dwExtendedStyle) + PyList_Append(list, PyLong_FromDouble(tpl->dwExtendedStyle)); + else if (datalen > 0) + { + Py_INCREF(Py_None); + PyList_Append(list, Py_None); + } + + // Parameter 6 - Extra data + if (datalen > 0) + { + PyList_Append(list, PyString_FromStringAndSize((char*)ptr, datalen)); + ptr = (RESSTR)(((char*)ptr) + datalen); + } + + DwordAlign((PCHAR*)&ptr); + *tplin = ptr; + + return list; +} + +// Given a dialog resource, return a Python object to match it +PYW_EXPORT PyObject *MakeDlgListFromResource(HGLOBAL res) +{ + void *t = (void*)res; + + PyObject *list = PyList_New(0); + int n = 0; + PyList_Append(list, MakeListFromDlgHdr(&t, n)); + for (int i = 0; i < n; i++) + { + PyList_Append(list, MakeListFromDlgItem(&t)); + } + + return list; +} + +// Given a Python dialog header list, parse out a matching CPythonDialogTemplate +// @object Dialog Header Tuple|A tuple describing a dialog box, that can be used to create the dialog. +// @comm For further information, see the win32 SDK for documentation on the DLGTEMPLATE structure. +static CPythonDialogTemplate *ParseDlgHdrList(PyObject *tmpl) +{ + if (!PyList_Check(tmpl)) + return NULL; + int size = PyList_Size(tmpl); + if (size < 3 || size > 7) + return NULL; + + // @tupleitem 0|string|caption|The caption for the window + PyObject *o = PyList_GetItem(tmpl, 0); + if (!PyString_Check(o)) + RETURN_TYPE_ERR("Window caption must be a string"); + + char *capt = PyString_AsString(o); + + // @tupleitem 1|(int,int,int,int)|(x,y,cx,cy)|The bounding rectange for the dialog. + o = PyList_GetItem(tmpl, 1); + DLGTEMPLATE tpl; + tpl.cdit = 0; + int x, y, cx, cy; + if (!PyArg_ParseTuple(o, "iiii", &x, &y, &cx, &cy)) + return NULL; + tpl.x = (WORD)x; + tpl.y = (WORD)y; + tpl.cx = (WORD)cx; + tpl.cy = (WORD)cy; + + // @tupleitem 2|int|style|The style bits for the dialog. Combination of WS_* and DS_* constants. + // Note that the DS_SETFONT style need never be specified - it is determined by the font item (below) + // The following dialog style flags can be used. + // @flag DS_3DLOOK|Gives the dialog box a nonbold font and draws three-dimensional borders around control windows in the dialog box.The DS_3DLOOK style is required only by Win32-based applications compiled for versions of Windows earlier than Windows 95 or Windows NT 4.0. The system automatically applies the three-dimensional look to dialog boxes created by applications compiled for current versions of Windows. + // @flag DS_ABSALIGN|Indicates that the coordinates of the dialog box are screen coordinates. If this style is not specified, Windows assumes they are client coordinates. + // @flag DS_CENTER|Centers the dialog box in the working area; that is, the area not obscured by the tray. + // @flag DS_CENTERMOUSE|Centers the mouse cursor in the dialog box. + // @flag DS_CONTEXTHELP|Includes a question mark in the title bar of the dialog box. When the user clicks the question mark, the cursor changes to a question mark with a pointer. If the user then clicks a control in the dialog box, the control receives a WM_HELP message. The control should pass the message to the dialog box procedure, which should call the WinHelp function using the HELP_WM_HELP command. The Help application displays a pop-up window that typically contains help for the control.Note that DS_CONTEXTHELP is only a placeholder. When the dialog box is created, the system checks for DS_CONTEXTHELP and, if it is there, adds WS_EX_CONTEXTHELP to the extended style of the dialog box. WS_EX_CONTEXTHELP cannot be used with the WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles. + // @flag DS_CONTROL|Creates a dialog box that works well as a child window of another dialog box, much like a page in a property sheet. This style allows the user to tab among the control windows of a child dialog box, use its accelerator keys, and so on. + // @flag DS_FIXEDSYS|Causes the dialog box to use the SYSTEM_FIXED_FONT instead of the default SYSTEM_FONT. SYSTEM_FIXED_FONT is a monospace font compatible with the System font in Windows versions earlier than 3.0. + // @flag DS_LOCALEDIT|Applies to 16-bit applications only. This style directs edit controls in the dialog box to allocate memory from the application's data segment. Otherwise, edit controls allocate storage from a global memory object. + // @flag DS_MODALFRAME|Creates a dialog box with a modal dialog-box frame that can be combined with a title bar and System menu by specifying the WS_CAPTION and WS_SYSMENU styles. + // @flag DS_NOFAILCREATE|Windows 95 only:�Creates the dialog box even if errors occur - for example, if a child window cannot be created or if the system cannot create a special data segment for an edit control. + // @flag DS_NOIDLEMSG|Suppresses WM_ENTERIDLE messages that Windows would otherwise send to the owner of the dialog box while the dialog box is displayed. + // @flag DS_SETFOREGROUND|Causes the system to use the SetForegroundWindow function to bring the dialog box to the foreground. + // @flag DS_SYSMODAL|Creates a system-modal dialog box. This style causes the dialog box to have the WS_EX_TOPMOST style, but otherwise has no effect on the dialog box or the behavior of other windows in the system when the dialog box is displayed. + o = PyList_GetItem(tmpl, 2); + if (!Py_GetAsDWORD(o, &tpl.style)) + return NULL; + + // @tupleitem 3|int|extStyle|The extended style bits for the dialog. + tpl.dwExtendedStyle = 0; + if (size > 3) + { + o = PyList_GetItem(tmpl, 3); + if (o != Py_None && !Py_GetAsDWORD(o, &tpl.dwExtendedStyle)) + return NULL; + } + + // @tupleitem 4|(int, string)|(fontSize, fontName)|A tuple describing the font, or None if the system default font is to be used. + LPCSTR font = NULL; + WORD fontsize = 8; + tpl.style &= ~DS_SETFONT; + if (size > 4) + { + o = PyList_GetItem(tmpl, 4); + if (o != Py_None) + { + int tmp; + if (!PyArg_ParseTuple(o, "is", &tmp, &font)) + return NULL; + fontsize = (WORD)tmp; + tpl.style |= DS_SETFONT; + } + } + + // @tupleitem 5|string/int|menuResource|The resource ID of the menu to be used for the dialog, or None for no menu. + LPCSTR menu = NULL; + if (size > 5) + { + o = PyList_GetItem(tmpl, 5); + if (o != Py_None) + { + if (PyString_Check(o)) + menu = PyString_AsString(o); + else if (PyInt_Check(o)) + menu = (LPCSTR)MAKELONG((WORD)(PyInt_AsLong(o)), 0); + else + return NULL; + } + } + + // @tupleitem 6|string/int|windowClass|The window class for the dialog, or None. If an int, the value must be an atom returned from RegisterWindowClass. + LPCSTR wclass = NULL; + if (size > 6) + { + o = PyList_GetItem(tmpl, 6); + if (o != Py_None) + { + if (PyString_Check(o)) + wclass = PyString_AsString(o); + else if (PyInt_Check(o)) + wclass = (LPCSTR)MAKELONG((WORD)(PyInt_AsLong(o)), 0); + else + return NULL; + } + } + + return new CPythonDialogTemplate(capt, &tpl, fontsize, font, menu, wclass); +} + +// Given a Python dialog item list, parse out a dialog item +// @object Dialog Item Tuple|A tuple describing a control in a dialog box. +// @comm For further information, see the win32 SDK for documentation on the DLGITEMTEMPLATE structure. +static BOOL ParseDlgItemList(CPythonDialogTemplate *dlg, PyObject *tmpl) +{ + if (!PyList_Check(tmpl)) + return FALSE; + int size = PyList_Size(tmpl); + if (size < 5 || size > 7) + return FALSE; + + // @tupleitem 0|string/int|windowClass|The window class. If not a string, it must be in integer defining one of the built-in Windows controls. + // If a string, it must be a pre-registered windows class name, a built-in class, or the CLSID of an OLE controls. + // Built-in classes include: + // @flagh Control Type|String Class Name + // @flag Check Box|Button + // @flag Combo Box|ComboBox + // @flag Command Button|Button + // @flag Header|SysHeader32 + // @flag Label|Static + // @flag List Box|ListBoxSysListView32 + // @flag Option Button|Button + // @flag Tab|SysTabControl32 + // @flag Text Box|EditRICHEDIT + // @flag Tool Bar|ToolbarWindow32 + // @flag Tool Tips|tooltips_class32tooltips_class + // @flag Tree View|SysTreeView32 + // The built-in windows controls are: + // @flagh Integer Value|Window Type + // @flag 0x0080|Button + // @flag 0x0081|Edit + // @flag 0x0082|Static + // @flag 0x0083|List box + // @flag 0x0084|Scroll bar + // @flag 0x0085|Combo box + PyObject *o = PyList_GetItem(tmpl, 0); + BOOL isBuiltin; + LPCSTR wclass; + int bclass; + if (PyString_Check(o)) + { + wclass = (LPCSTR)PyString_AsString(o); + isBuiltin = FALSE; + } + else if (PyInt_Check(o)) + { + bclass = PyInt_AsLong(o); + isBuiltin = TRUE; + } + else + return FALSE; + + if (size == 7 && isBuiltin) + return FALSE; + + // @tupleitem 1|text|caption|Caption for the control, or None + LPCSTR text = NULL; + o = PyList_GetItem(tmpl, 1); + if (PyString_Check(o)) + text = (LPCSTR)PyString_AsString(o); + else if (o != Py_None) + return FALSE; + + // @tupleitem 2|int|ID|The child ID of this control. All children should have unique + // IDs. This ID can be used by to retrieve the actual control + // object at runtime. + o = PyList_GetItem(tmpl, 2); + if (!PyInt_Check(o)) + return FALSE; + int id = PyInt_AsLong(o); + DLGITEMTEMPLATE tpl; + tpl.id = (WORD)id; + + // @tupleitem 3|(int,int,int,int)|(x,y,cx,cy)|The bounding rectange for the control, relative to the upper left of the dialog, in dialog units.. + o = PyList_GetItem(tmpl, 3); + int x, y, cx, cy; + if (!PyArg_ParseTuple(o, "iiii", &x, &y, &cx, &cy)) + return FALSE; + tpl.x = (WORD)x; + tpl.y = (WORD)y; + tpl.cx = (WORD)cx; + tpl.cy = (WORD)cy; + + // @tupleitem 4|int|style|The window style of the control (WS_* constants). Depending on the type of control, + // other constants may also be valid (eg, BS_* for Button, ES_* for Edit controls, etc). + o = PyList_GetItem(tmpl, 4); + if (!Py_GetAsDWORD(o, &tpl.style)) + return FALSE; + + // @tupleitem 5|int|extStyle|The extended style of the control. + tpl.dwExtendedStyle = 0; + if (size > 5) + { + o = PyList_GetItem(tmpl, 5); + if (!Py_GetAsDWORD(o, &tpl.dwExtendedStyle)) + return FALSE; + } + + // @tupleitem 6|string|extraData|A string of bytes used as extra data for the control. The value depends on the control. + BYTE *data = NULL; + int datalen = 0; + if (size > 6) + { + o = PyList_GetItem(tmpl, 6); + if (o != Py_None) + { + if (PyString_Check(o)) + return FALSE; + data = (BYTE*)PyString_AsString(o); + datalen = PyString_Size(o); + } + } + + if (isBuiltin) + dlg->Add(bclass, &tpl, text); + else + dlg->Add(wclass, &tpl, text, datalen, data); + + return TRUE; +} + +// Given a Python dialog template object, parse out a dialog resource +PYW_EXPORT HGLOBAL MakeResourceFromDlgList(PyObject *tmpl) +{ + if (!PyList_Check(tmpl)) + { + RETURN_ERR("Passed object must be a dialog template list"); + } + int size = PyList_Size(tmpl); + if (size < 1) + { + RETURN_ERR("Passed object must be a dialog template list"); + } + PyObject *o = PyList_GetItem(tmpl, 0); + CPythonDialogTemplate *dlg = ParseDlgHdrList(o); + if (dlg == NULL) + { + RETURN_ERR("Unable to parse dialog header"); + } + for (register int i = 1; i < size; i++) + { + if (!ParseDlgItemList(dlg, PyList_GetItem(tmpl, i))) + { + delete dlg; + RETURN_ERR("Unable to parse a dialog item"); + } + } + HGLOBAL h = dlg->ClaimTemplate(); + delete dlg; + return h; +} + +#endif diff --git a/Pythonwin/win32dlgdyn.h b/Pythonwin/win32dlgdyn.h new file mode 100644 index 0000000000..87ff24dd4c --- /dev/null +++ b/Pythonwin/win32dlgdyn.h @@ -0,0 +1,157 @@ +/* + * win32dlgdyn.h - Dynamic dialog creation + * + * Copyright (C) 1995 by Motek Information Systems, Beverly Hills, CA, USA + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice and the two paragraphs following + * it appear in all copies, and that the name of Motek Information Systems + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * MOTEK INFORMATION SYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL MOTEK BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Curt Hagenlocher + */ + +#ifndef WIN32DLGDYN_H +#define WIN32DLGDYN_H + +#ifndef WIN32 + +#pragma pack(1) +struct DLGTEMPLATE +{ + DWORD style; + BYTE cdit; + WORD x, y; + WORD cx, cy; + // char menu[] + // char class[] + // char caption[] + // WORD points; (only if DS_SETFONT) + // char fontname[]; (only if DS_SETFONT) +}; + +struct DLGITEMTEMPLATE +{ + WORD x, y; + WORD cx, cy; + WORD id; + DWORD style; + // union + // { + // BYTE idClass; + // char szClass[2]; + // }; + // char text[] + // BYTE datalen; + // char data[datalen] +}; +#pragma pack() + +#endif + +typedef DLGTEMPLATE *LPDLGTEMPLATE; +typedef DLGITEMTEMPLATE *LPDLGITEMTEMPLATE; + +inline void SetDlgTemplate(DLGTEMPLATE *t, DWORD s, WORD x, WORD y, WORD w, WORD h) +{ + t->style = s; +#ifdef WIN32 + t->dwExtendedStyle = 0; +#endif + t->cdit = 0; + t->x = x; + t->y = y; + t->cx = w; + t->cy = h; +} + +inline void SetDlgItemTemplate(DLGITEMTEMPLATE *t, DWORD s, WORD x, WORD y, + WORD w, WORD h, WORD id) +{ + t->style = s; +#ifdef WIN32 + t->dwExtendedStyle = 0; +#endif + t->x = x; + t->y = y; + t->cx = w; + t->cy = h; + t->id = id; +} + +const BYTE dlgButton = 0x80; +const BYTE dlgEdit = 0x81; +const BYTE dlgStatic = 0x82; +const BYTE dlgListbox = 0x83; +const BYTE dlgScrollbar = 0x84; +const BYTE dlgCombobox = 0x85; + +class CPythonDialogTemplate +{ +protected: + HGLOBAL m_h; + int m_alloc, m_len; +#ifdef WIN32 + DLGTEMPLATE *m_ptr; +#endif + +public: + CPythonDialogTemplate(LPCSTR capt, DLGTEMPLATE *tmpl, WORD fontsize = 8, + LPCSTR font = NULL, LPCSTR menu = NULL, LPCSTR wclass = NULL); + ~CPythonDialogTemplate(); + BOOL Add(LPCSTR wclass, DLGITEMTEMPLATE *tmpl, LPCSTR txt = NULL, + int datalen = 0, BYTE *data = NULL); + BOOL Add(BYTE wclass, DLGITEMTEMPLATE *tmpl, LPCSTR txt = NULL); + void Get(DLGTEMPLATE *tmpl); + void Set(DLGTEMPLATE *tmpl); +#ifndef WIN32 + HGLOBAL GetTemplate() { return m_h; } +#else + HGLOBAL GetTemplate() { return (HGLOBAL)m_ptr; } +#endif + HGLOBAL ClaimTemplate(); +}; + +#ifdef WIN32 + +#define _RES(x) L ## x +#define RESCHAR WCHAR +#define RESSTR LPWSTR +#define RESCSTR LPCWSTR +#define MAKERESSTR(x) MakeResStr(x) +#define FREERESSTR(x) FreeResStr(x) +#define strcpyR wcscpy +#define strlenR wcslen +#define alloclenR 2*wcslen + +void DwordAlign(PCHAR *ptr); +RESSTR MakeResStr(LPCSTR x); +void FreeResStr(RESCSTR x); + +#else + +#define _RES(x) x +#define RESCHAR char +#define RESSTR LPSTR +#define RESCSTR LPCSTR +#define MAKERESSTR(x) x +#define FREERESSTR(x) NULL +#define strcpyR strcpy +#define strlenR strlen +#define alloclenR strlen + +#endif + +#endif diff --git a/Pythonwin/win32dll.cpp b/Pythonwin/win32dll.cpp new file mode 100644 index 0000000000..e8b2a3b902 --- /dev/null +++ b/Pythonwin/win32dll.cpp @@ -0,0 +1,149 @@ +/* + +win32dll - A Python interface to a windows DLL. + +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 "win32dll.h" + +dll_object::dll_object() +{ + bDidLoadLibrary = FALSE; + pMFCExt = NULL; + pCDLL = NULL; +} +dll_object::~dll_object() +{ + if (bDidLoadLibrary) + dll_object::DoKillAssoc(TRUE); // must explicitly call, as virtuals dont work in dtors! +} +// @pymethod |win32ui|LoadLibrary|Creates a DLL object, and loads a Windows DLL into the object. +PyObject * +dll_object::create (PyObject *self, PyObject *args) +{ + char *file; + int flags=0; + // @pyparm string|fileName||The name of the DLL file to load. + if (!PyArg_ParseTuple(args, "s|i", &file, &flags )) + return NULL; + BOOL bDidLoadLib = FALSE; + // must convert to full path, else GetModuleHandle may fail. + char fullPath[MAX_PATH]; + if (!GetFullPath(fullPath, file )) + RETURN_ERR("The filename is invalid"); + + HINSTANCE dll = ::GetModuleHandle(fullPath); + if (dll==NULL) { + bDidLoadLib = TRUE; + dll = ::LoadLibraryEx (fullPath, NULL, flags); + if (dll == NULL) { + if (GetLastError()==ERROR_MOD_NOT_FOUND) { + PyErr_SetString(PyExc_IOError,"Module not found"); + return NULL; + } else + RETURN_API_ERR ("LoadLibraryEx"); + } + } + dll_object *ret = (dll_object *)ui_assoc_object::make(dll_object::type, dll); + if (bDidLoadLib) + ret->bDidLoadLibrary = TRUE; + return ret; +} +void dll_object::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + if (bDidLoadLibrary = TRUE) { + ::FreeLibrary (GetDll()); + TRACE("Python object freeing DLL reference\n"); + ui_assoc_object::DoKillAssoc(bDestructing); + } + if (pMFCExt) { + AfxTermExtensionModule(*pMFCExt); // this deletes the DLL. + delete pMFCExt; + pMFCExt = NULL; + pCDLL = NULL; + } +} +// @pymethod |PyDLL|AttachToMFC|Attaches the DLL object to the MFC list of DLL's. +// @comm After calling this method, MFC will search this DLL when looking for resources. +// A program can use this function once, instead of specifying the DLL +// in each call to load/find a resource.In addition, this is the only way that +// an application can provide status bar messages and tool tips for custom control +// ID's in an external DLL. +static PyObject * +dll_object_attach_to_mfc( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + dll_object *dll = (dll_object *)self; + if (dll->pMFCExt) + RETURN_ERR("The DLL has already been attached to MFC"); + HINSTANCE hInst = dll->GetDll(); + if (hInst==NULL) + RETURN_ERR("There is no DLL attached to the object"); + + dll->pMFCExt = new AFX_EXTENSION_MODULE; // this will except rather than return NULL + dll->pMFCExt->bInitialized = 0; + dll->pMFCExt->hModule = 0; + if (!AfxInitExtensionModule( *dll->pMFCExt, hInst )) + RETURN_ERR("AfxInitExtensionModule failed."); + dll->pCDLL = new CDynLinkLibrary( *dll->pMFCExt ); + RETURN_NONE; +} + +// @pymethod string|PyDLL|GetFileName|Returns the name of the module associated with the DLL. +// @comm Note that this is the name that Windows knows the DLL by, not necessarily +// the name that was specified! +static PyObject * +dll_object_get_file_name( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + HINSTANCE hInst = ((dll_object *)self)->GetDll(); + if (hInst==NULL) + RETURN_ERR("There is no DLL attached to the object"); + CString csFileName; + char *buf = csFileName.GetBuffer(MAX_PATH); + ::GetModuleFileName(hInst, buf, MAX_PATH); + csFileName.ReleaseBuffer(); + return Py_BuildValue("s", (const char *)csFileName ); +} + +// @pymethod string|PyDLL|__repr__|Returns the HINSTANCE and filename of the DLL. +CString +dll_object::repr() +{ + HINSTANCE dll = GetDll(); + CString csRet; + char *buf = csRet.GetBuffer(256); + sprintf (buf, " HINSTANCE 0x%X, file = ", dll); + csRet.ReleaseBuffer(); + + CString csFileName; + buf = csFileName.GetBuffer(MAX_PATH); + if (dll) + ::GetModuleFileName(dll, buf, MAX_PATH); // @pyseeapi GetModuleFileName + else + strcpy(buf, ""); + csFileName.ReleaseBuffer(); + + return ui_base_class::repr() + csRet + csFileName; +} + +// @object PyDLL|A DLL object. A general utility object, and not associated with an MFC object. +static struct PyMethodDef dll_methods[] = +{ + {"GetFileName", dll_object_get_file_name, 1}, // @pymeth GetFileName|Returns the file name of the DLL associated with the object. + {"AttachToMFC", dll_object_attach_to_mfc, 1}, // @pymeth AttachToMFC|Attaches the DLL to the internal list of MFC DLL's. + {NULL, NULL} +}; + +ui_type dll_object::type ("PyDLL", + &ui_assoc_object::type, + sizeof(dll_object), + dll_methods, + GET_PY_CTOR(dll_object)); diff --git a/Pythonwin/win32dll.h b/Pythonwin/win32dll.h new file mode 100644 index 0000000000..4aff72436c --- /dev/null +++ b/Pythonwin/win32dll.h @@ -0,0 +1,21 @@ +// dll holder class +// by Dave Brennan (brennan@hal.com) + +class dll_object : public ui_assoc_object +{ +public: + MAKE_PY_CTOR(dll_object) + static ui_type type; + static PyObject *create (PyObject *self, PyObject *args); + AFX_EXTENSION_MODULE *pMFCExt; + CDynLinkLibrary *pCDLL; + HINSTANCE GetDll() + { return (HINSTANCE)GetGoodCppObject(&type); } +protected: + dll_object(); + ~dll_object(); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); + virtual CString repr(); +private: + BOOL bDidLoadLibrary; +}; diff --git a/Pythonwin/win32doc.cpp b/Pythonwin/win32doc.cpp new file mode 100644 index 0000000000..262ac32f54 --- /dev/null +++ b/Pythonwin/win32doc.cpp @@ -0,0 +1,411 @@ +/* + + win32 document data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32template.h" + +///////////////////////////////////////////////////////////////////// +// +// Document object +// +////////////////////////////////////////////////////////////////////// +PyCDocument::PyCDocument() +{ +} + +PyCDocument::~PyCDocument() +{ +} +/*static*/CDocument *PyCDocument::GetDoc(PyObject *self) +{ + return (CDocument *)GetGoodCppObject( self, &type); +} + +// @pymethod |PyCDocument|DoFileSave|Checks the file attributes. +// If the file is read only, a new name is prompted, else the +// file is saved (by calling DoSave) +PyObject * +ui_doc_do_file_save(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,DoFileSave); + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pDoc->CDocument::DoFileSave(); // @pyundocmfc CDocument|DoFileSave + GUI_END_SAVE; + // @xref + if (rc==FALSE) + RETURN_ERR("DoFileSave failed"); + RETURN_NONE; +} + +// @pymethod |PyCDocument|DeleteContents|Call the MFC DeleteContents method. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_doc_delete_contents(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + pDoc->CDocument::DeleteContents(); // @pyseemfc CDocument|DeleteContents + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocument|DoSave|Calls the underlying MFC DoSave method. +PyObject * +ui_doc_do_save(PyObject *self, PyObject *args) +{ + // @comm If invalid or no filename, will prompt for a name, else + // will perform the actual saving of the document. + char *fileName; + int bReplace = TRUE; + if (!PyArg_ParseTuple(args,"s|i", + &fileName, // @pyparm string|fileName||The name of the file to save to. + &bReplace)) // @pyparm int|bReplace|1|Should an existing file be silently replaced?. + return NULL; + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + BOOL rc = pDoc->CDocument::DoSave(fileName, bReplace); // @pyundocmfc CDocument|DoSave + GUI_END_SAVE; + if (rc==FALSE) + RETURN_ERR("DoSave failed"); + RETURN_NONE; +} + +// @pymethod |PyCDocument|GetFirstView|Returns the first view object attached to this document. +PyObject * +ui_doc_get_first_view(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,GetFirstView); + POSITION pos = pDoc->GetFirstViewPosition(); // @pyseemfc CDocument|GetFirstViewPosition + if (pos==NULL) + RETURN_NONE; + GUI_BGN_SAVE; + CView *pWnd = pDoc->GetNextView( pos ); // @pyseemfc CDocument|GetNextView + GUI_END_SAVE; + + // @comm For more info, see + ASSERT(pWnd); // shouldnt be possible. + return ui_assoc_object::make(UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); +} + +// @pymethod [,...]|PyCDocument|GetAllViews|Returns a list of all views for the current document. +PyObject * +ui_doc_get_all_views(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,GetAllViews); + PyObject *retList = PyList_New(0); + GUI_BGN_SAVE; + POSITION pos = pDoc->GetFirstViewPosition(); // @pyseemfc CDocument|GetFirstViewPosition + GUI_END_SAVE; + while (pos!=NULL) { + GUI_BGN_SAVE; + CView *pWnd = pDoc->GetNextView( pos ); // @pyseemfc CDocument|GetNextView + GUI_END_SAVE; + ASSERT(pWnd); // shouldnt be possible. + if (pWnd==NULL) { + Py_DECREF(retList); + RETURN_ERR("No view was available!"); + } + PyObject *newObj = ui_assoc_object::make(UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + if (newObj==NULL) { + Py_DECREF(retList); + return NULL; + } + PyList_Append(retList, newObj); + Py_DECREF(newObj); + } + return retList; +} + +// @pymethod string|PyCDocument|GetPathName|Returns the full path name of the current document. +// The string will be empty if no path name has been set. +PyObject * +ui_doc_get_path_name(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,GetPathName); + GUI_BGN_SAVE; + CString path = pDoc->GetPathName(); // @pyseemfc CDocument|GetPathName + GUI_END_SAVE; + return Py_BuildValue("s", (const char *)path); +} +// @pymethod |PyCDocument|GetDocTemplate|Returns the template for the document. +PyObject * +ui_doc_get_template(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,GetDocTemplate); + GUI_BGN_SAVE; + CDocTemplate *ret = pDoc->GetDocTemplate(); + GUI_END_SAVE; + // @pyseemfc CDocument|GetDocTemplate + return ui_assoc_object::make(PyCDocTemplate::type, ret)->GetGoodRet(); +} + +// @pymethod string|PyCDocument|GetTitle|Returns the title of the current document. +// This will often be the file name portion of the path name. +PyObject * +ui_doc_get_title(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,GetTitle); + GUI_BGN_SAVE; + CString path = pDoc->GetTitle(); // @pyseemfc CDocument|GetTitle + GUI_END_SAVE; + return Py_BuildValue("s", (const char *)path); +} +// @pymethod int|PyCDocument|IsModified|Return a flag indicating if the document has been modified. +PyObject * +ui_doc_is_modified(PyObject *self, PyObject *args) +{ + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + CHECK_NO_ARGS2(args,IsModified); + GUI_BGN_SAVE; + int rc = pDoc->IsModified(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); // @pyseemfc CDocument|IsModified +} + +// @pymethod |PyCDocument|OnCloseDocument|Call the MFC OnCloseDocument handler. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_doc_on_close(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,OnCloseDocument); + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + pDoc->CDocument::OnCloseDocument(); // @pyseemfc CDocument|OnCloseDocument + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocument|OnNewDocument|Call the MFC OnNewDocument handler. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_doc_on_new(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + BOOL ok = pDoc->CDocument::OnNewDocument(); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDocument|OnNewDocument + RETURN_ERR("OnNewDocument failed"); + RETURN_NONE; +} + +// @pymethod |PyCDocument|OnOpenDocument|Call the MFC OnOpenDocument handler. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_doc_on_open(PyObject *self, PyObject *args) +{ + char *pathName; + if (!PyArg_ParseTuple(args, "s", &pathName)) // @pyparm string|pathName||The full path of the file to open. + return NULL; + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pDoc->OnOpenDocument(pathName); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDocument|OnOpenDocument + RETURN_ERR("OnOpenDocument failed"); + RETURN_NONE; +} + +// @pymethod |PyCDocument|OnSaveDocument|Call the MFC OnSaveDocument handler. +// This routine is provided so a document object which overrides this method +// can call the original MFC version if required. +static PyObject * +ui_doc_on_save(PyObject *self, PyObject *args) +{ + char *pathName; + if (!PyArg_ParseTuple(args, "s", &pathName)) // @pyparm string|pathName||The full path of the file to save. + return NULL; + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pDoc->OnSaveDocument(pathName); + GUI_END_SAVE; + if (!ok) // @pyseemfc CDocument|OnSaveDocument + RETURN_ERR("OnSaveDocument failed"); + RETURN_NONE; +} + +// @pymethod int|PyCDocument|SaveModified|Call the underlying MFC method. +static PyObject * +ui_doc_save_modified(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,SaveModified); + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + // @xref + GUI_BGN_SAVE; + BOOL rc = pDoc->CDocument::SaveModified(); // @pyseemfc CDocument|SaveModified + GUI_END_SAVE; + // @rdesc Nonzero if it is safe to continue and close the document; 0 if the document should not be closed. + return PyInt_FromLong(rc); +} + +// @pymethod |PyCDocument|SetModifiedFlag|Set the "dirty" flag for the document. +static PyObject * +ui_doc_set_modified_flag(PyObject *self, PyObject *args) +{ + BOOL bModified = TRUE; + if (!PyArg_ParseTuple(args, "|i:SetModifiedFlag", &bModified)) // @pyparm int|bModified|1|Set dirty flag + return NULL; + + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + pDoc->SetModifiedFlag(bModified); // @pyseemfc CDocument|SetModifiedFlag + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocument|SetPathName|Set the full path name for the document. +static PyObject * +ui_doc_set_path_name(PyObject *self, PyObject *args) +{ + char *path; + if (!PyArg_ParseTuple(args, "s:SetPathName", &path)) // @pyparm string|path||The full path of the file. + return NULL; + + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + pDoc->SetPathName(path); // @pyseemfc CDocument|SetPathName + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocument|SetTitle|Set the title of the document (ie, the name +// to appear in the window caption for the document. +static PyObject * +ui_doc_set_title(PyObject *self, PyObject *args) +{ + char *title; + if (!PyArg_ParseTuple(args, "s", &title)) // @pyparm string|title||The new title. + return NULL; + + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + GUI_BGN_SAVE; + pDoc->SetTitle(title); // @pyseemfc CDocument|SetTitle + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocument|UpdateAllViews|Informs each view when a document changes. +static PyObject * +ui_doc_update_all_views(PyObject *self, PyObject *args) +{ + PyObject *obSender; + PyObject *obHint = Py_None; + CDocument *pDoc; + if (!(pDoc=PyCDocument::GetDoc(self))) + return NULL; + if (!PyArg_ParseTuple(args, "O|O:UpdateAllViews", + &obSender, // @pyparm |sender||The view who initiated the update + &obHint)) // @pyparm object|hint|None|A hint for the update. + return NULL; + CView *pView = NULL; + if (obSender!=Py_None) { + if (!(pView=PyCView::GetViewPtr(obSender))) + return NULL; + } + if (obHint==Py_None) + obHint = NULL; + GUI_BGN_SAVE; + pDoc->UpdateAllViews(pView, (LPARAM)obHint);// @pyseemfc CDocument|UpdateAllViews + GUI_END_SAVE; + RETURN_NONE; +} + +/////////////////////////////////////// +// +// Document Methods +// +// inherited from assoc_object +// +/////////////////////////////////////// +// @object PyCDocument|A document class. Encapsulates an MFC class +static struct PyMethodDef ui_doc_methods[] = { + {"DeleteContents", ui_doc_delete_contents, 1}, // @pymeth DeleteContents|Call the MFC DeleteContents method. + {"DoSave", ui_doc_do_save, 1}, // @pymeth DoSave|Save the file. If necessary, prompt for file name. + {"DoFileSave", ui_doc_do_file_save, 1}, // @pymeth DoFileSave|Check file attributes, and save the file. + {"GetDocTemplate", ui_doc_get_template, 1}, // @pymeth GetDocTemplate|Returns the for the document. + {"GetAllViews", ui_doc_get_all_views, 1}, // @pymeth GetAllViews|Returns a list of all views for the current document. + {"GetFirstView", ui_doc_get_first_view, 1}, // @pymeth GetFirstView|Returns the first view object attached to this document. + {"GetPathName", ui_doc_get_path_name, 1}, // @pymeth GetPathName|Returns the full path name of the current document. + {"GetTitle", ui_doc_get_title, 1}, // @pymeth GetTitle|Returns the title of the current document. + {"IsModified", ui_doc_is_modified, 1}, // @pymeth IsModified|Return a flag indicating if the document has been modified. + {"OnCloseDocument", ui_doc_on_close, 1}, // @pymeth OnCloseDocument|Call the MFC OnCloseDocument handler. + {"OnNewDocument", ui_doc_on_new, 1}, // @pymeth OnNewDocument|Call the MFC OnNewDocument handler. + {"OnOpenDocument", ui_doc_on_open, 1}, // @pymeth OnOpenDocument|Call the MFC OnOpenDocument handler. + {"OnSaveDocument", ui_doc_on_save, 1}, // @pymeth OnSaveDocument|Call the MFC OnSaveDocument handler. + {"SetModifiedFlag", ui_doc_set_modified_flag, 1}, // @pymeth SetModifiedFlag|Set the "dirty" flag for the document. + {"SaveModified", ui_doc_save_modified, 1}, // @pymeth SaveModified|Call the underlying MFC method. + {"SetPathName", ui_doc_set_path_name, 1}, // @pymeth SetPathName|Set the full path name for the document. + {"SetTitle", ui_doc_set_title, 1}, // @pymeth SetTitle|Set the title of the document. + {"UpdateAllViews", ui_doc_update_all_views, 1}, // @pymeth UpdateAllViews|Informs each view when a document changes. + {NULL, NULL} /* sentinel */ +}; +ui_type_CObject PyCDocument::type("PyCDocument", + &PyCCmdTarget::type, + RUNTIME_CLASS(CDocument), + sizeof(PyCDocument), + ui_doc_methods, + GET_PY_CTOR(PyCDocument) ); diff --git a/Pythonwin/win32doc.h b/Pythonwin/win32doc.h new file mode 100644 index 0000000000..634a32749f --- /dev/null +++ b/Pythonwin/win32doc.h @@ -0,0 +1,18 @@ +// +// Document Object +// + +class PYW_EXPORT PyCDocument : public PyCCmdTarget { +protected: + PyCDocument(); + ~PyCDocument(); +public: + static PyObject *create_edit(PyObject *self, PyObject *args); + static PyObject *create(PyObject *self, PyObject *args); + + static CDocument *GetDoc(PyObject *self); + static PyObject *ui_doc_create(PyObject *self, PyObject *args); + + MAKE_PY_CTOR(PyCDocument); + static ui_type_CObject type; +}; diff --git a/Pythonwin/win32font.cpp b/Pythonwin/win32font.cpp new file mode 100644 index 0000000000..1c5c1e2c83 --- /dev/null +++ b/Pythonwin/win32font.cpp @@ -0,0 +1,77 @@ +/* + python font class + + Created September 1994, by Dave Brennan (brennan@hal.com) + +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 "win32gdi.h" +#include "win32font.h" +#include "win32dc.h" + +// @pymethod |win32ui|CreateFont|Creates a object. +PyObject * +PyCFont::create (PyObject *self, PyObject *args) +{ + // @comm The code for the PyCFont was contributed by Dave Brennan + // (Last known address is brennan@hal.com, but I hear he is now at Microsoft) + // args contains a dict of font properties + PyObject *font_props; + PyObject *pydc = NULL; // @pyparm dict|properties||A dictionary containing the font + // properties. Valid dictionary keys are: + // name + // size + // weight + // italic + // underline + if (!PyArg_ParseTuple (args, "O|O", + &font_props, &pydc) || + !PyDict_Check (font_props)) + { + PyErr_Clear(); + RETURN_ERR ("Expected dictionary of font properties."); + } + // populate LOGFONT struct with values from dictionary + LOGFONT lf; + if (!DictToLogFont(font_props, &lf)) + return NULL; + + CDC *pDC = pydc ? ui_dc_object::GetDC (pydc) : NULL; + + CFont *pFont = new CFont; // will except rather than fail! + if (!pDC) { + if (!pFont->CreateFontIndirect (&lf)) { + delete pFont; + RETURN_ERR ("CreateFontIndirect call failed"); + } + } else { + if (!pFont->CreatePointFontIndirect (&lf, pDC)) { + delete pFont; + RETURN_ERR ("CreatePointFontIndirect call failed"); + } + } + return ui_assoc_object::make (PyCFont::type, pFont); +} + +// @object PyCFont|A windows font object. Encapsulates an MFC class. +// Derived from a . +static struct PyMethodDef ui_font_methods[] = +{ + {NULL, NULL} // sentinel +}; + +ui_type_CObject PyCFont::type("PyCFont", + &PyCGdiObject::type, + RUNTIME_CLASS(CFont), + sizeof(PyCFont), + ui_font_methods, + GET_PY_CTOR(PyCFont)); + diff --git a/Pythonwin/win32font.h b/Pythonwin/win32font.h new file mode 100644 index 0000000000..05028fcfc5 --- /dev/null +++ b/Pythonwin/win32font.h @@ -0,0 +1,12 @@ +// font class + +class PyCFont : public PyCGdiObject { + +public: + MAKE_PY_CTOR(PyCFont) + static ui_type_CObject type; + static PyObject *create (PyObject *self, PyObject *args); + +protected: +private: +}; diff --git a/Pythonwin/win32gdi.cpp b/Pythonwin/win32gdi.cpp new file mode 100644 index 0000000000..03c58e649e --- /dev/null +++ b/Pythonwin/win32gdi.cpp @@ -0,0 +1,70 @@ +/* + python GDI class + + Access to a CGdiObject. + +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 "win32gdi.h" + +bool PyCGdiObject::CheckCppObject(ui_type *ui_type_check) const +{ +// if (!ui_assoc_CObject::CheckCppObject(ui_type_check)) +// return false; + CGdiObject *pGDI = (CGdiObject *)assoc; + ASSERT_VALID(pGDI); + if (!IsGdiHandleValid(pGDI->m_hObject)) + RETURN_ERR("The associated object is invalid"); + return true; +} + +// utility functions. +// according to MFC2 sources, these pointers are permanent. +CGdiObject *PyCGdiObject::GetGdiObject (PyObject *self, DWORD gtype) +{ + CGdiObject *pGdi = (CGdiObject *)GetGoodCppObject( self, &type); + if (gtype && !IsWin32s() && pGdi->m_hObject && ::GetObjectType(pGdi->m_hObject) != gtype) + RETURN_ERR("The associated GDI object is not of the required type"); + return pGdi; +} + +void PyCGdiObject::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + CGdiObject *pGDI; + if (m_deleteObject && (pGDI = GetGdiObject())) + { + m_deleteObject = FALSE; + delete pGDI; + } + ui_assoc_object::DoKillAssoc(bDestructing); +} + +PyCGdiObject::~PyCGdiObject() +{ + DoKillAssoc(TRUE); +} + +CString PyCGdiObject::repr() +{ + return ui_assoc_object::repr() + CString(m_deleteObject ? ", delObject=1" : ", delObject=0"); +} + +// @object PyCGdiObject|A class which encapsulates an MFC CGdiObject. +static struct PyMethodDef ui_gdi_methods[] = { + {NULL, NULL} // sentinel +}; + +ui_type_CObject PyCGdiObject::type("gdi object", + &ui_assoc_CObject::type, + RUNTIME_CLASS(CGdiObject), + sizeof(PyCGdiObject), + ui_gdi_methods, + NULL); diff --git a/Pythonwin/win32gdi.h b/Pythonwin/win32gdi.h new file mode 100644 index 0000000000..a85e8eed6f --- /dev/null +++ b/Pythonwin/win32gdi.h @@ -0,0 +1,30 @@ +// gdiobject class +#pragma once + +class PyCGdiObject : public ui_assoc_CObject { +friend static PyObject *ui_dc_select_object (PyObject *self, PyObject *args); +public: + static ui_type_CObject type; + static CGdiObject *GetGdiObject( PyObject *self, DWORD type=0 ); + CGdiObject *GetGdiObject( DWORD type=0 ) {return GetGdiObject( this, type );} + static CFont *GetFont (PyObject *self) { return (CFont *)GetGdiObject(self, OBJ_FONT); } + CFont *GetFont() { return GetFont (this); } + static CPen *GetPen (PyObject *self) { return (CPen *)GetGdiObject (self, OBJ_PEN); } + CPen *GetPen() { return GetPen (this); } + static CBrush *GetBrush (PyObject *self) { return (CBrush *)GetGdiObject (self, OBJ_BRUSH); } + CBrush *GetBrush() { return GetBrush (this); } + static CBitmap *GetBitmap (PyObject *self) { return (CBitmap *)GetGdiObject(self, OBJ_BITMAP); } + CBitmap *GetBitmap() { return GetBitmap (this); } + BOOL m_deleteObject; + +protected: + PyCGdiObject() + : m_deleteObject(TRUE) + { } + ~PyCGdiObject(); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); + virtual bool CheckCppObject(ui_type *ui_type_check) const; + + // virtuals for the Python interface. + virtual CString repr(); +}; diff --git a/Pythonwin/win32hl.h b/Pythonwin/win32hl.h new file mode 100644 index 0000000000..ca67d0305a --- /dev/null +++ b/Pythonwin/win32hl.h @@ -0,0 +1,36 @@ +///////////////////////////////////////////////////////// +// +// hierlist + +// fwd declare +class CPythonHierControl; +class CPythonHierListItem; +///////////////////////// + +class ui_hierlist_object : public PyCWnd { +public: + static PyObject *create(PyObject *self, PyObject *args); + CPythonHierControl *GetListObject(); + static CPythonHierControl *GetListObject(PyObject *self); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); + + static ui_type_CObject type; + MAKE_PY_CTOR(ui_hierlist_object) +protected: + ui_hierlist_object(); + virtual ~ui_hierlist_object(); + +}; +class ui_hierlist_item : public ui_assoc_object { +public: + static CPythonHierListItem *GetHLI(PyObject *self); + + +protected: + ui_hierlist_item() {return;} + virtual ~ui_hierlist_item() {return;} +public: + static ui_type type; + MAKE_PY_CTOR(ui_hierlist_item) + +}; diff --git a/Pythonwin/win32menu.cpp b/Pythonwin/win32menu.cpp new file mode 100644 index 0000000000..a4e05d27ea --- /dev/null +++ b/Pythonwin/win32menu.cpp @@ -0,0 +1,340 @@ +/* + + menu data types + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + + Note - menus are implemented totally in the API, and not using + MFC at all (ie, using HMENU's rather than CMenu's) + + However, some of the menus that exist, and can be manipulated with this + data type have underlying MFC menus. + +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 "win32menu.h" +#include "win32dll.h" + +extern CWnd *GetWndPtr(PyObject *); + +bool PyCMenu::CheckCppObject(ui_type *ui_type_check) const +{ + if (!ui_assoc_object::CheckCppObject(ui_type_check)) + return false; + HMENU handle = (HMENU)assoc; + if (!::IsMenu(handle)) + RETURN_ERR("The menu associated with the object is not valid"); + return true; +} +// this returns a pointer that should not be stored. +HMENU PyCMenu::GetMenu(PyObject *self) +{ + return (HMENU)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|CreateMenu|Creates a menu object. +PyObject *PyCMenu::create_menu(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + HMENU hMenu = CreateMenu(); + if (!hMenu) + RETURN_API_ERR("CreateMenu"); + return ui_assoc_object::make(PyCMenu::type, hMenu); +} +// @pymethod |win32ui|CreatePopupMenu|Creates a popup menu object. +PyObject *PyCMenu::create_popup(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + HMENU hMenu = CreatePopupMenu(); + if (!hMenu) + RETURN_API_ERR("CreatePopupMenu"); + return ui_assoc_object::make(PyCMenu::type, hMenu); +} +// @pymethod |win32ui|LoadMenu|Creates and loads a menu resource from a DLL. +PyObject *PyCMenu::load_menu(PyObject *self, PyObject *args) +{ + int id; + PyObject *dllObj = NULL; + HMODULE hMod = NULL; + // @pyparm int|id||The Id of the menu to load. + // @pyparm |dll|None|The DLL to load from. + if (!PyArg_ParseTuple(args, "i|O", &id, &dllObj)) + return NULL; + if (dllObj && dllObj!=Py_None) { + // passed a DLL object. + if (!is_uiobject(dllObj, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL object"); + hMod = ((dll_object *)dllObj)->GetDll(); + } + else + hMod = AfxFindResourceHandle( MAKEINTRESOURCE(id), RT_MENU ); + HMENU hMenu = ::LoadMenu(hMod, MAKEINTRESOURCE(id)); + if (!hMenu) + RETURN_API_ERR("LoadMenu"); + return ui_assoc_object::make(PyCMenu::type, hMenu); +} + +///////////////////////////////////////////////////////////////////// +// +// menu +// +////////////////////////////////////////////////////////////////////// +void PyCMenu::SetAssocInvalid() +{ + return; // do nothing. Dont call base as dont want my handle wiped. +} +// Menu Methods +// @pymethod |PyCMenu|AppendMenu|Appends a new item to the end of a menu. Python can specify the state of the menu item by setting values in nFlags. +PyObject *PyCMenu::AppendMenu(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + char *value = NULL; + int id=0; + int flags; + if (!PyArg_ParseTuple(args,"i|iz", + &flags, // @pyparm int|flags||Specifies information about the state of the new menu item when it is added to the menu. May be a combination of the win32con.MF_* values. + &id, // @pyparm int|id|0|Specifies either the command ID of the new menu item. + &value)) // @pyparm string/None|value|None|Specifies the content of the new menu item. If used, flags must contain win32con.MF_STRING. + return NULL; + if (!::AppendMenu( hMenu, flags, id, value)) + RETURN_API_ERR("::AppendMenu"); + RETURN_NONE; +} + +// @pymethod string|PyCMenu|DeleteMenu|Deletes the specified menu item. +PyObject *PyCMenu::DeleteMenu(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + int id,flags; + if (!PyArg_ParseTuple(args,"ii", + &id, // @pyparm int|id||The id of the item being deleted. + &flags)) // @pyparm int|flags||Specifies how the id parameter is interpreted. It must be one of win32con.MF_BYCOMMAND or win32con.MF_BYPOSITION. + return NULL; + if (!::DeleteMenu(hMenu, id, flags)) + RETURN_API_ERR("::DeleteMenu"); + RETURN_NONE; +} + +// @pymethod int|PyCMenu|EnableMenuItem|Enables, disables, or dims a menu item. +PyObject *PyCMenu::EnableMenuItem(PyObject *self, PyObject *args) +{ + // @comm The , , , + // and member functions can also set the state + // (enabled, disabled, or dimmed) of a menu item. + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + int id,flags; + if (!PyArg_ParseTuple(args,"ii", + &id, // @pyparm int|id||Specifies the command ID of the menu item. This parameter can specify pop-up menu items as well as standard menu items. + &flags)) // @pyparm int|flags||Specifies the action to take. It can be a combination of MF_DISABLED, MF_ENABLED, or MF_GRAYED, with MF_BYCOMMAND or MF_BYPOSITION + return NULL; + GUI_BGN_SAVE; + int rc = ::EnableMenuItem(hMenu,id,flags); + GUI_END_SAVE; + + return Py_BuildValue("i",rc); +} +// @pymethod int|PyCMenu|GetHandle|Returns the menu object's underlying hMenu. +PyObject *PyCMenu::GetHandle(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu(self); + if (!hMenu) + return NULL; + return Py_BuildValue("i",hMenu); +} +// @pymethod int|PyCMenu|GetMenuItemCount|Determines the number of items in a menu. +PyObject *PyCMenu::GetMenuItemCount(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + return Py_BuildValue("i",::GetMenuItemCount(hMenu)); + // @rdesc The number of items in the menu if the function is successful; otherwise -1. +} +// @pymethod int|PyCMenu|GetMenuItemID|Returns the item ID for the specified item in a pop-up menu. +PyObject *PyCMenu::GetMenuItemID(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + int pos; + // @pyparm int|pos||The position (zero-based) of the menu item whose ID is being retrieved. + if (!PyArg_ParseTuple(args,"i",&pos)) + return NULL; + // @comm If the specified item is a pop-up menu (as opposed to an item within the pop-up menu), + // the return value is -1. If nPos corresponds to a SEPARATOR menu item, + // the return value is 0. + return Py_BuildValue("i",::GetMenuItemID(hMenu, pos)); +} +// @pymethod string|PyCMenu|GetMenuString|Returns the string for a specified menu item. +PyObject *PyCMenu::GetMenuString(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + int id,flags = MF_BYCOMMAND; + if (!PyArg_ParseTuple(args,"i|i", + &id, // @pyparm int|id||The id of the item being requested. + &flags)) // @pyparm int|flags|win32con.MF_BYCOMMAND|Specifies how the id parameter is interpreted. It must be one of win32con.MF_BYCOMMAND or win32con.MF_BYPOSITION. + return NULL; + char buf[128]; + if (::GetMenuString(hMenu, id, buf, sizeof(buf), flags)==0) + buf[0] = '\0'; + return Py_BuildValue("s",buf); +} +// @pymethod |PyCMenu|GetSubMenu|Returns a submenu. +PyObject *PyCMenu::GetSubMenu(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + int pos; + // @pyparm int|pos||The position (zero-based) of the menu item being retrieved. + if (!PyArg_ParseTuple(args,"i",&pos)) + return NULL; + HMENU hSubMenu = ::GetSubMenu(hMenu,pos); + if (hSubMenu==NULL) + RETURN_ERR("There is no sub-menu at that position"); + return ui_assoc_object::make( PyCMenu::type, hSubMenu); +} +// @pymethod |PyCMenu|InsertMenu|Inserts an item into a menu. +PyObject *PyCMenu::InsertMenu(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + char *value = NULL; + int id=0; + int flags; + int pos; + BOOL bHaveInt = TRUE; + PyObject *subMenu; + if (!PyArg_ParseTuple(args,"ii|iz", + &pos, // @pyparm int|pos||The position (zero-based) the item should be inserted. + &flags, // @pyparm int|flags||Flags for the new item. + &id, // @pyparm int|id|0|The ID for the new item. + &value)) { // @pyparm string/None|value|None|A string for the menu item. + bHaveInt = FALSE; + value = NULL; // Probably not necessary, but just incase. + PyErr_Clear(); + if (!PyArg_ParseTuple(args,"iiO|z", + &pos, // @pyparmalt1 int|pos||The position (zero-based) the item should be inserted. + &flags, // @pyparmalt1 int|flags||Flags for the new item. + &subMenu, // @pyparmalt1 int|id|0|The ID for the new item. + &value)) // @pyparmalt1 string/None|value|None|A string for the menu item. + return NULL; + } + if (bHaveInt) { + if (!::InsertMenu( hMenu, pos, flags, id, value)) + RETURN_API_ERR("::InsertMenu"); + } else { + HMENU hSubMenu = GetMenu(subMenu); + if (!::InsertMenu(hMenu, pos, flags, (int)hSubMenu, value)) + RETURN_API_ERR("::InsertMenu"); + } + RETURN_NONE; +} + +// @pymethod |PyCMenu|ModifyMenu|Modify an item in a menu. +PyObject *PyCMenu::ModifyMenu(PyObject *self, PyObject *args) +{ + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + char *value = NULL; + int id=0; + int flags; + int pos; + if (!PyArg_ParseTuple(args,"ii|iz", + &pos, // @pyparm int|pos||The position (zero-based) the item to be changed. + &flags, // @pyparm int|flags||Flags for the item. + &id, // @pyparm int|id|0|The ID for the item. + &value)) // @pyparm string/None|value|None|A string for the menu item. + { + return NULL; + } + if (!::ModifyMenu( hMenu, pos, flags, id, value)) + RETURN_API_ERR("::ModifyMenu"); + RETURN_NONE; +} +// @pymethod |PyCMenu|TrackPopupMenu|Creates a popup menu anywhere on the screen. +PyObject *PyCMenu::TrackPopupMenu(PyObject *self, PyObject *args) +{ + // @comm The TrackPopupMenu function displays a floating pop-up menu at the + // specified location and tracks the selection of items on the pop-up menu. + // The floating pop-up menu can appear anywhere on the screen. + HMENU hMenu = GetMenu( self ); + if (!hMenu) + return NULL; + HWND hTarget; + PyObject *wndObject = NULL; + int flags=TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON; + int x,y; + if (!PyArg_ParseTuple(args,"(ii)|iO", + &x, &y, // @pyparm (int, int)|(x,y)||The position for the menu.. + &flags, // @pyparm int|flags|win32con.TPM_LEFTALIGN\|win32con.TPM_LEFTBUTTON\|win32con.TPM_RIGHTBUTTON|Flags for the menu. + &wndObject)) // @pyparm |owner|(main application frame)|The owner of the menu. + + return NULL; + if (wndObject) { + CWnd *wnd=GetWndPtr(wndObject); + if (wnd==NULL) + return NULL; + hTarget = wnd->m_hWnd; + } else { + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + hTarget=pApp->m_pMainWnd->GetSafeHwnd(); + } + + GUI_BGN_SAVE; + BOOL rc = ::TrackPopupMenu(hMenu,flags,x,y,0,hTarget,NULL); + GUI_END_SAVE; + // @rdesc If the underlying MFC function fails, but TPM_RETURNCMD is set in the flags parameter, then None is returned instead of the normal exception. + if (!rc) { + if (flags & TPM_RETURNCMD) { + RETURN_NONE; + } else { + RETURN_API_ERR("TrackPopupMenu"); + } + } + return Py_BuildValue("i",rc); +} + +// Menu Methods +// @object PyCMenu|A windows menu. Encapsulates an MFC class +static struct PyMethodDef ui_menu_methods[] = { + {"AppendMenu", (PyCFunction)PyCMenu::AppendMenu, 1}, // @pymeth AppendMenu|Appends a new item to the end of a menu. Python can specify the state of the menu item by setting values in nFlags. + {"DeleteMenu", (PyCFunction)PyCMenu::DeleteMenu, 1}, // @pymeth DeleteMenu|Deletes the specified menu item. + {"Detach", (PyCFunction)PyCMenu::GetHandle, 1}, + {"EnableMenuItem", (PyCFunction)PyCMenu::EnableMenuItem, 1}, // @pymeth EnableMenuItem|Enables, disables, or dims a menu item. + {"GetHandle", (PyCFunction)PyCMenu::GetHandle, 1}, // @pymeth GetHandle|Returns the menu object's underlying hMenu. + {"GetMenuItemCount", (PyCFunction)PyCMenu::GetMenuItemCount, 1}, // @pymeth GetMenuItemCount|Determines the number of items in a menu. + {"GetMenuItemID", (PyCFunction)PyCMenu::GetMenuItemID, 1}, // @pymeth GetMenuItemID|Returns the item ID for the specified item in a pop-up menu. + {"GetMenuString", (PyCFunction)PyCMenu::GetMenuString, 1}, // @pymeth GetMenuString|Returns the string for a specified menu item. + {"GetSubMenu", (PyCFunction)PyCMenu::GetSubMenu, 1}, // @pymeth GetSubMenu|Returns a submenu. + {"InsertMenu", (PyCFunction)PyCMenu::InsertMenu, 1}, // @pymeth InsertMenu|Inserts an item into a menu. + {"ModifyMenu", (PyCFunction)PyCMenu::ModifyMenu, 1}, // @pymeth ModifyMenu|Modify an item in a menu. + {"TrackPopupMenu", (PyCFunction)PyCMenu::TrackPopupMenu, 1}, // @pymeth TrackPopupMenu|Creates a popup menu anywhere on the screen. + {NULL, NULL} +}; + +ui_type PyCMenu::type("PyCMenu", + &ui_assoc_object::type, + sizeof(PyCMenu), + ui_menu_methods, + GET_PY_CTOR(PyCMenu)); + diff --git a/Pythonwin/win32menu.h b/Pythonwin/win32menu.h new file mode 100644 index 0000000000..41ec00a344 --- /dev/null +++ b/Pythonwin/win32menu.h @@ -0,0 +1,27 @@ +// menu class +class PyCMenu : public ui_assoc_object { +protected: + PyCMenu::PyCMenu() {return;} + virtual void SetAssocInvalid(); + virtual bool CheckCppObject(ui_type *ui_type_check) const; +public: + static ui_type type; + MAKE_PY_CTOR(PyCMenu) + static HMENU GetMenu(PyObject *self); + + static PyObject *create_popup(PyObject *self, PyObject *args); + static PyObject *create_menu(PyObject *self, PyObject *args); + static PyObject *load_menu(PyObject *self, PyObject *args); + + static PyObject *AppendMenu(PyObject *self, PyObject *args); + static PyObject *DeleteMenu(PyObject *self, PyObject *args); + static PyObject *InsertMenu(PyObject *self, PyObject *args); + static PyObject *ModifyMenu(PyObject *self, PyObject *args); + static PyObject *EnableMenuItem(PyObject *self, PyObject *args); + static PyObject *GetHandle(PyObject *self, PyObject *args); + static PyObject *GetMenuItemCount(PyObject *self, PyObject *args); + static PyObject *GetMenuItemID(PyObject *self, PyObject *args); + static PyObject *GetMenuString(PyObject *self, PyObject *args); + static PyObject *GetSubMenu(PyObject *self, PyObject *args); + static PyObject *TrackPopupMenu(PyObject *self, PyObject *args); +}; diff --git a/Pythonwin/win32notify.cpp b/Pythonwin/win32notify.cpp new file mode 100644 index 0000000000..41ad1187d1 --- /dev/null +++ b/Pythonwin/win32notify.cpp @@ -0,0 +1,327 @@ +/* + + win32 Notify Handler + + Created May 1995, Mark Hammond (MHammond@skippinet.com.au) + +*/ +#include "stdafx.h" +#include "win32win.h" + +// Not very general purpose notify parser! +PyObject *PyNotifyMakeExtraTuple( NMHDR *ptr, char *fmt) +{ + char *pUse = (char *)(ptr+1); + int argNo = 0; + int tupleLen = 0; + for (char *szTemp = fmt;*szTemp; ++szTemp) { + if (*szTemp == '-') + ++szTemp; // skip next one. + else if (isdigit(*szTemp)) + ; // ignore it. + else + ++tupleLen; // count it + } + PyObject *ret = PyTuple_New(tupleLen); + PyObject *ob; + BOOL bIgnore; + while (*fmt) { +#ifdef _DEBUG + ob = NULL; +#endif + bIgnore = *fmt=='-'; + if (bIgnore) ++fmt; + switch (*fmt) { + case 'i': + ob = bIgnore ? NULL : PyInt_FromLong( * ((int *)pUse) ); + pUse += (sizeof(int)); + break; + case 'P': { // point + LONG l1 = * ((LONG *)pUse); + pUse += (sizeof(long)); + LONG l2 = * ((LONG *)pUse); + pUse += (sizeof(long)); + ob = bIgnore ? NULL : Py_BuildValue("ll", l1, l2); + break; + } + case 'z': // string pointer + case 's': // string buffer - same for this parse + { + char *use = (*fmt=='z') ? * (char **) pUse : pUse; + ob = bIgnore ? NULL : PyString_FromString(""); // HACK HACK - FIX ME FIX ME + if (*fmt=='s') { // followed by buffer size; + int val = 0; + while (fmt[1] && isdigit(fmt[1])) { + val = val * 10 + (fmt[1]-'0'); + fmt++; + } + pUse += sizeof(char) * val; + } else { + pUse += sizeof(char *); + } + break; + } + case 'Z': // Unicode string pointer + case 'S': // Unicode buffer - same for this parse + { + char *use = (*fmt=='Z') ? * (char **) pUse : pUse; + ob = bIgnore ? NULL : PyString_FromString(""); // HACK HACK - FIX ME FIX ME + if (*fmt=='S') { // followed by buffer size; + int val = 0; + while (fmt[1] && isdigit(fmt[1])) { + val = val * 10 + (fmt[1]-'0'); + fmt++; + } + pUse += sizeof(wchar_t) * val; + } else { + pUse += sizeof(wchar_t *); + } + break; + } + + case 'O': // object with no reference count maintained + ob = bIgnore ? NULL : (PyObject *)pUse; + Py_INCREF(ob); + pUse += (sizeof(PyObject *)); + break; + case 'T': {// TV_ITEM structure + TV_ITEM *ptv = (TV_ITEM *)pUse; + ob = bIgnore ? NULL : MakeTV_ITEMTuple(ptv); + pUse += (sizeof(TV_ITEM)); + break; + } + case 'L': {// LV_ITEM structure + LV_ITEM *plv = (LV_ITEM *)pUse; + ob = bIgnore ? NULL : MakeLV_ITEMTuple(plv); + pUse += (sizeof(LV_ITEM)); + break; + } + /* + case 'H': {// HD_ITEM structure + HD_ITEM *phd = (HD_ITEM *)pUse; + ob = bIgnore ? NULL : MakeHD_ITEMTuple(phd); + pUse += (sizeof(HD_ITEM)); + break; + } + */ + default: + ASSERT(FALSE); + Py_DECREF(ret); + RETURN_ERR("Bad format char in internal WM_NOTIFY tuple conversion"); + } + if (!bIgnore) { + PyTuple_SET_ITEM(ret, argNo, ob); + argNo++; + } + ASSERT(bIgnore==FALSE || ob==NULL); // check bIgnore logic + fmt++; + } + return ret; +} + +#define MY_RET_ERR(msg) { PyErr_SetString(ui_module_error, msg); return;} +// Not very general purpose notify parser! +void PyNotifyParseExtraTuple( NMHDR *ptr, PyObject *args, char *fmt) +{ + char *pUse = (char *)(ptr+1); + BOOL bIgnore; + int argNum = 0; + while (*fmt) { + PyObject *ob = PyTuple_GetItem(args, argNum); + if (ob==NULL) return; + bIgnore = *fmt=='-'; + if (bIgnore) ++fmt; + switch (*fmt) { + case 'i': + if (!bIgnore) { + if (!PyInt_Check(ob)) MY_RET_ERR("Expected integer object") + *((int *)pUse) = PyInt_AsLong(ob); + } + pUse += (sizeof(int)); + break; + case 'P': { // point + ASSERT(FALSE); + break; + } + case 'T': { // TV_ITEM + ASSERT(FALSE); + break; + } + case 'z': // string pointer + if (!bIgnore) { + ASSERT(FALSE); + } + pUse += (sizeof(char *)); + break; + case 'Z': // wide string pointer + if (!bIgnore) { + ASSERT(FALSE); + } + pUse += (sizeof(wchar_t *)); + break; + case 's': // string buffer + { + int bufSize = 0; + while (fmt[1] && isdigit(fmt[1])) { + bufSize = bufSize * 10 + (fmt[1]-'0'); + fmt++; + } + ASSERT(bufSize); + if (!bIgnore) { + if (!PyString_Check(ob)) MY_RET_ERR("Expected string object") + char *val = PyString_AsString(ob); + int slen = strlen(val); + int copylen = max(bufSize-1, slen); + strncpy( pUse, val, copylen); + pUse[copylen] = '\0'; + } + pUse += bufSize; + break; + } + case 'S': // string buffer + { + int bufSize = 0; + while (fmt[1] && isdigit(fmt[1])) { + bufSize = bufSize * 10 + (fmt[1]-'0'); + fmt++; + } + ASSERT(bufSize); + if (!bIgnore) { + if (!PyString_Check(ob)) MY_RET_ERR("Expected string object") + char *szVal = PyString_AsString(ob); + int slen = strlen(szVal); + mbstowcs( (wchar_t *)pUse, szVal, bufSize ); + } + pUse += bufSize + sizeof(wchar_t); + break; + } + case 'O': // object with no reference count maintained + ASSERT(FALSE); + break; + default: + ASSERT(FALSE); + MY_RET_ERR("Bad format char in internal WM_NOTIFY tuple conversion"); + } + fmt++; + if (!bIgnore) + argNum ++; + } + return; +} + +/////////////////////////////////////////////////////// +// General notify handler for Python. +BOOL +Python_OnNotify (CWnd *pFrom, WPARAM, LPARAM lParam, LRESULT *pResult) +{ + CEnterLeavePython _celp; + PyCCmdTarget *pPyWnd = (PyCCmdTarget *) ui_assoc_CObject::GetPyObject(pFrom); + NMHDR *pHdr = (NMHDR *)lParam; + if (pHdr==NULL) return FALSE; // bad data passed? + UINT code = pHdr->code; + if (pPyWnd==NULL) return FALSE; // no object. + if (!pPyWnd->is_uiobject (&PyCWnd::type)) return FALSE; // unexpected object type. + PyObject *method; + + if (!pPyWnd->pNotifyHookList || + !pPyWnd->pNotifyHookList->Lookup (code, (void *&)method)) + return FALSE; // no hook installed. + + // have method to call. Build arguments. + PyObject *ob1 = Py_BuildValue("iii", pHdr->hwndFrom, pHdr->idFrom, pHdr->code); + char *fmt; + /* + if (code >= LVN_LAST && code <= LVN_FIRST) // These are negative, hence the reversal. + fmt = "iiiiiPi"; //this is a NM_LISTVIEW + else if (code >= TVN_LAST && code <= TVN_FIRST) // These are negative, hence the reversal. + fmt = "iTTP"; //this is NM_TREEVIEW + else if (code==TTN_NEEDTEXTA) + fmt = "-zs80ii"; + else if (code==TTN_NEEDTEXTW) + fmt = "-ZS80ii"; + else + fmt = NULL; + */ + if (code >= UDN_LAST && code <= UDN_FIRST) + fmt = "ii"; //NM_UPDOWN + else if (code == TBN_GETBUTTONINFOW) + fmt = "iiibbiiiZ"; //TBNOTIFY + else if (code == TBN_QUERYDELETE || code == TBN_QUERYINSERT || (code >= TBN_ENDDRAG && code <= TBN_FIRST )) + fmt = "iiibbiiiz"; + else if (code == TBN_CUSTHELP || code == TBN_TOOLBARCHANGE || (code >= TBN_RESET && code <= TBN_BEGINADJUST)) + fmt = NULL; //NMHDR only + else if (code >= TCN_LAST && code <= TCN_SELCHANGE) + fmt = "i"; //HWND + else if (code == TCN_KEYDOWN) + fmt = "ii"; //TC_KEYDOWN + else if (code == TTN_NEEDTEXTW) + fmt = "-ZS80ii"; //TOOLTIPTEXT + else if (code == TTN_POP || code == TTN_SHOW) + fmt = NULL; //NMHDR only + else if (code == TTN_NEEDTEXTA) + fmt = "-zs80ii"; //TOOLTIPTEXT + else if (code == TVN_ENDLABELEDITW || code == TVN_BEGINLABELEDITW || code == TVN_SETDISPINFOW + || code == TVN_GETDISPINFOW || code == TVN_ENDLABELEDITA || code == TVN_BEGINLABELEDITA + || code == TVN_SETDISPINFOA || code == TVN_GETDISPINFOA) + fmt = "T"; //TV_DISPINFO + else if (code == TVN_KEYDOWN) + fmt = "ii"; //TV_KEYDOWN + else if (code >= TVN_LAST && code <= TVN_FIRST) + fmt = "iTTP"; //NM_TREEVIEW + else if (code == HDN_ITEMDBLCLICKW || code == HDN_ITEMDBLCLICKA) + fmt = NULL; //NMHDR only + else if (code >= HDN_LAST && code <= HDN_FIRST) + fmt = "iiH"; //HD_NOTIFY + else if (code == LVN_KEYDOWN) + fmt = "ii"; //LV_KEYDOWN + else if ((code >= LVN_LAST && code <= LVN_GETDISPINFOW) || code == LVN_ENDLABELEDITA || code == LVN_BEGINLABELEDITA) + fmt = "L"; //LV_DISPINFO + else if (code >= LVN_BEGINRDRAG && code <= LVN_FIRST) + fmt = "iiiiiPi"; //NM_LISTVIEW + else + fmt = NULL; + + PyObject *ob2; + if (fmt==NULL) + ob2 = PyInt_FromLong((int)(pHdr + 1)); + else + ob2 = PyNotifyMakeExtraTuple(pHdr, fmt); + if (ob2==NULL) { + gui_print_error(); + return FALSE; + } + + // make the call with my params. + PyObject *args = Py_BuildValue("OO", ob1, ob2); + Py_DECREF(ob1); + Py_DECREF(ob2); + int rc = 0; + BOOL bPassOn = FALSE; + PyObject *obOther; + PyObject *result = Python_do_callback(method, args); + if (result==NULL) { + PyErr_SetString(ui_module_error, "Exception in OnNotify() handler"); + gui_print_error(); + } else if (result==Py_None) // allow for None "dont pass on", else result to windows + bPassOn = TRUE; + else if (PyInt_Check(result)) { + // Simple integer return val + rc = PyInt_AsLong(result); + } else if (PyArg_ParseTuple(result, "iO", &rc, &obOther)) { + // parse off obOther + PyErr_Clear(); + PyNotifyParseExtraTuple( pHdr, obOther, fmt); + if (PyErr_Occurred()) { + gui_print_error(); + PyErr_SetString(ui_module_error, "Error parsing OnNotify() extra return info"); + gui_print_error(); + } + } else { + PyErr_SetString(ui_module_error, "Unable to parse result from OnNotify()"); + gui_print_error(); + rc = 0; + } + Py_XDECREF(result); + *pResult = rc; + return !bPassOn; +} diff --git a/Pythonwin/win32oleDlgInsert.cpp b/Pythonwin/win32oleDlgInsert.cpp new file mode 100644 index 0000000000..8a41227fc6 --- /dev/null +++ b/Pythonwin/win32oleDlgInsert.cpp @@ -0,0 +1,166 @@ +#include "stdafxole.h" + +#include "win32dlg.h" +#include "win32oleDlgs.h" +#include "pythoncom.h" +#include "pywintypes.h" +// @doc + +class OLEUIINSERTOBJECTHelper +{ +public: + OLEUIINSERTOBJECTHelper( OLEUIINSERTOBJECT *pCon ); + ~OLEUIINSERTOBJECTHelper(); + BOOL ParseDict(PyObject *dict); + BOOL BuildDict(PyObject *dict); +private: + char fileNameBuf[MAX_PATH]; + OLEUIINSERTOBJECT *pConv; +}; + +OLEUIINSERTOBJECTHelper::OLEUIINSERTOBJECTHelper( OLEUIINSERTOBJECT *pCon ) +{ + ASSERT(pCon); + memset(pCon, 0, sizeof( OLEUIINSERTOBJECT ) ); + pCon->cbStruct = sizeof( OLEUIINSERTOBJECT ); + pCon->lpszFile = fileNameBuf; + pCon->cchFile = sizeof(fileNameBuf); + pConv = pCon; +} +OLEUIINSERTOBJECTHelper::~OLEUIINSERTOBJECTHelper() +{ +} + +BOOL OLEUIINSERTOBJECTHelper::ParseDict( PyObject *obDict ) +{ + PyObject *ob; + ob = PyObject_GetAttrString(obDict, "Flags"); + if (ob) pConv->dwFlags = PyInt_AsLong(ob); + ob = PyObject_GetAttrString(obDict, "WndOwner"); + if (ob) { + if (PyInt_Check(ob)) + pConv->hWndOwner = (HWND)PyInt_AsLong(ob); + else { + CWnd *pParent = (CWnd *)PyCWnd::GetPythonGenericWnd(ob); + if (pParent==NULL) { + PyErr_SetString(PyExc_TypeError, "The WndOwner element must be a integer HWND or a window object"); + return FALSE; + } + pConv->hWndOwner = pParent->GetSafeHwnd(); + } + } + + ob = PyObject_GetAttrString(obDict, "Caption"); + if (ob) pConv->lpszCaption = PyString_AsString(PyObject_Str(ob)); + // Hook not implemented + // CustData not implemented + ob = PyObject_GetAttrString(obDict, "Instance"); + if (ob) pConv->hInstance = (HINSTANCE)PyInt_AsLong(ob); + ob = PyObject_GetAttrString(obDict, "Template"); + if (ob) pConv->lpszTemplate = PyString_AsString(PyObject_Str(ob)); + ob = PyObject_GetAttrString(obDict, "Resource"); + if (ob) pConv->hResource = (HRSRC)PyInt_AsLong(ob); + + // OLEUIINSERTOBJECT specifics + // CLSID is out. + ob = PyObject_GetAttrString(obDict, "File"); + if (ob) { + char *szTemp = PyString_AsString(PyObject_Str(ob)); + if (szTemp==NULL) return FALSE; + strncpy(fileNameBuf, szTemp, sizeof(fileNameBuf)); + fileNameBuf[sizeof(fileNameBuf)-1]='\0'; + } + // CLSIDExcludeList not yet supported. + ob = PyObject_GetAttrString(obDict, "iid"); + if (ob) { + if (!PyWinObject_AsIID(ob, &pConv->iid)) + return FALSE; + } + ob = PyObject_GetAttrString(obDict, "oleRender"); + if (ob) pConv->oleRender = PyInt_AsLong(ob); + // lpFormatEtc not supported. + // lpIOleCloientSite not supported. + // lpIStorage not supported + // hMetaPict not supported. + return TRUE; +} + +BOOL OLEUIINSERTOBJECTHelper::BuildDict( PyObject *obDict ) +{ + if (PyObject_SetAttrString(obDict, "Flags", PyInt_FromLong(pConv->dwFlags))) + return FALSE; + return TRUE; +} + +// @pymethod |win32uiole|CreateInsertDialog|Creates a InsertObject dialog. +PyObject *PyCOleInsertDialog::create( PyObject * /*self*/, PyObject *args ) +{ + CHECK_NO_ARGS2(args, CreateInsertDialog); + COleInsertDialog *pDlg = new COleInsertDialog(); + if (!pDlg) + RETURN_ERR("Creating COleInsertDialog failed"); // pyseemfc COleInsertDialog|COleInsertDialog + PyCOleInsertDialog *newObj = + (PyCOleInsertDialog *)ui_assoc_object::make( PyCOleInsertDialog::type, pDlg); + return newObj; +} + +COleInsertDialog *GetOleInsertDialog(PyObject *self) +{ + return (COleInsertDialog *)PyCWnd::GetPythonGenericWnd(self, &PyCOleInsertDialog::type); +} + +// @pymethod CLSID|PyCOleInsertDialog|GetClassID|Returns the CLSID associated with the selected item +PyObject *PyCOleInsertDialog_GetClassID( PyObject * self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetClassID); + + COleInsertDialog *pDlg = GetOleInsertDialog(self); + if (!pDlg) return NULL; + GUI_BGN_SAVE; + IID iid = pDlg->GetClassID(); + GUI_END_SAVE; + return PyWinObject_FromIID(iid); +} +// @pymethod CLSID|PyCOleInsertDialog|GetSelectionType|Returns the type of selection made +PyObject *PyCOleInsertDialog_GetSelectionType( PyObject * self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetSelectionType); + + COleInsertDialog *pDlg = GetOleInsertDialog(self); + if (!pDlg) return NULL; + GUI_BGN_SAVE; + long rc = pDlg->GetSelectionType(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod CLSID|PyCOleInsertDialog|GetPathName|Returns the full path to the file selected in the dialog box +PyObject *PyCOleInsertDialog_GetPathName( PyObject * self, PyObject *args ) +{ + CHECK_NO_ARGS2(args, GetPathName); + + COleInsertDialog *pDlg = GetOleInsertDialog(self); + if (!pDlg) return NULL; + GUI_BGN_SAVE; + CString ret = pDlg->GetPathName(); + GUI_END_SAVE; + return PyString_FromString((char *)(const char *)ret); + // @comm Do not call this if the selection type is createNewItem, +} + + +// @object PyCOleInsertDialog|An OLE 'Insert Object' dialog. Encapsulates an MFC class +static struct PyMethodDef PyCOleInsertDialog_methods[] = { + { "GetClassID", PyCOleInsertDialog_GetClassID, 1}, // @pymeth GetClassID|Returns the CLSID associated with the selected item + { "GetSelectionType", PyCOleInsertDialog_GetSelectionType, 1}, // @pymeth GetSelectionType|Returns the type of selection made + { "GetPathName", PyCOleInsertDialog_GetPathName, 1}, // @pymeth GetPathName|Returns the full path to the file selected in the dialog box + { NULL, NULL } +}; + +ui_type_CObject PyCOleInsertDialog::type("PyCOleInsertDialog", + &PyCOleDialog::type, + RUNTIME_CLASS(COleInsertDialog), + sizeof(PyCOleInsertDialog), + PyCOleInsertDialog_methods, + GET_PY_CTOR(PyCOleInsertDialog) ); + diff --git a/Pythonwin/win32oleDlgs.cpp b/Pythonwin/win32oleDlgs.cpp new file mode 100644 index 0000000000..c3c21e6437 --- /dev/null +++ b/Pythonwin/win32oleDlgs.cpp @@ -0,0 +1,22 @@ + +#include "stdafxole.h" + +#include "win32dlg.h" +#include "win32oleDlgs.h" + +// @doc + + +// @object PyCOleDialog|An abstract class which encapsulates an MFC COleDialog object. Derived from a object. +static struct PyMethodDef PyCOleDialog_methods[] = { + { NULL, NULL } +}; + +ui_type_CObject PyCOleDialog::type("PyCOleDialog", + &PyCCommonDialog::type, + RUNTIME_CLASS(COleDialog), + sizeof(PyCOleDialog), + PyCOleDialog_methods, + NULL); + + diff --git a/Pythonwin/win32oleDlgs.h b/Pythonwin/win32oleDlgs.h new file mode 100644 index 0000000000..80ad37622f --- /dev/null +++ b/Pythonwin/win32oleDlgs.h @@ -0,0 +1,13 @@ + +class PyCOleDialog : public PyCCommonDialog { +public: + static ui_type_CObject type; +}; + +class PyCOleInsertDialog : public PyCOleDialog { +public: + static PyObject *create(PyObject *self, PyObject *args); + MAKE_PY_CTOR(PyCOleInsertDialog); + static ui_type_CObject type; +}; + diff --git a/Pythonwin/win32pen.cpp b/Pythonwin/win32pen.cpp new file mode 100644 index 0000000000..02ed9c47d1 --- /dev/null +++ b/Pythonwin/win32pen.cpp @@ -0,0 +1,56 @@ +// -*- Mode: C++; tab-width: 4 -*- +// +// Python pen wrapper. +// +// Created Dec 1995, by Sam Rushing (rushing@nightmare.com) +// +// +// 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 "win32gdi.h" +#include "win32pen.h" + +// @pymethod |win32ui|CreatePen|Creates a object. +/*static*/ PyObject * +ui_pen_object::create (PyObject *self, PyObject *args) +{ + int n_pen_style; + int n_width; + long cr_color; + LOGPEN lp; + + if (!PyArg_ParseTuple (args, "iil", + &n_pen_style, // @pyparm int|style||The pen style. + &n_width, // @pyparm int|width||The pen width. + &cr_color)) { // @pyparm long|color||The pen color. + return NULL; + } + lp.lopnStyle = n_pen_style; + lp.lopnWidth.x = n_width; + lp.lopnWidth.y = 0; + lp.lopnColor = cr_color; + + CPen *pPen = new CPen; + if (!pPen->CreatePenIndirect (&lp)) { + delete pPen; // clean up on error. + RETURN_ERR ("CreatePenIndirect call failed"); + } + return ui_assoc_object::make (ui_pen_object::type, pPen); +} + +static struct PyMethodDef ui_pen_methods[] = { + {NULL, NULL} // sentinel +}; + +ui_type_CObject ui_pen_object::type ("PyCPen", + &PyCGdiObject::type, + RUNTIME_CLASS(CPen), + sizeof(ui_pen_object), + ui_pen_methods, + GET_PY_CTOR(ui_pen_object)); diff --git a/Pythonwin/win32pen.h b/Pythonwin/win32pen.h new file mode 100644 index 0000000000..877fdd493d --- /dev/null +++ b/Pythonwin/win32pen.h @@ -0,0 +1,12 @@ +// -*- Mode: C++; tab-width: 4 -*- +// font class + +class ui_pen_object : public PyCGdiObject { +public: + static ui_type_CObject type; + MAKE_PY_CTOR(ui_pen_object) + static PyObject *create (PyObject *self, PyObject *args); + +protected: +private: +}; diff --git a/Pythonwin/win32prinfo.cpp b/Pythonwin/win32prinfo.cpp new file mode 100644 index 0000000000..d01b12ac8f --- /dev/null +++ b/Pythonwin/win32prinfo.cpp @@ -0,0 +1,846 @@ +/* + + printer info data types + + Created March 1999, Roger Burnham (rburnham@cri-inc.com) + + These are implemented using CPrintInfo, CPrintDialog and PRINTDLG. + + NOT implemented: + CPrintDialog::GetDevMode + typedef struct tagPD { // pd + DWORD lStructSize; + HWND hwndOwner; + HANDLE hDevMode; + HANDLE hDevNames; + ... + HINSTANCE hInstance; + DWORD lCustData; + LPPRINTHOOKPROC lpfnPrintHook; + LPSETUPHOOKPROC lpfnSetupHook; + LPCTSTR lpPrintTemplateName; + LPCTSTR lpSetupTemplateName; + HANDLE hPrintTemplate; + HANDLE hSetupTemplate; + } PRINTDLG; + + Note: If you use the custom print dialog, see the Knowledge Base article, + + HOWTO: Customize the Common Print Dialog Box + Article ID: Q132909 + + And, you MUST NOT change the control ID's in this dialog. + + 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 + +#include "win32win.h" +#include "win32dlg.h" +#include "win32prinfo.h" + +extern CPrintDialog *GetPrintDialog(PyObject *self); + +// this returns a pointer that should not be stored. +CPrintInfo *ui_prinfo_object::GetPrintInfo(PyObject *self) +{ + return (CPrintInfo *)GetGoodCppObject( self, &type); +} + +void ui_prinfo_object::SetAssocInvalid() +{ + return; // do nothing. Dont call base as dont want my handle wiped. +} + +void ui_prinfo_object::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + if (m_deletePrInfo) { + CPrintInfo *pPrInfo = GetPrintInfo(this); + if (pPrInfo) { + if (pPrInfo->m_lpUserData) { + XDODECREF((PyObject*)pPrInfo->m_lpUserData); + pPrInfo->m_lpUserData = NULL; + } + if (pPrInfo->m_pPD->m_pd.lCustData) { + XDODECREF((PyObject*)pPrInfo->m_pPD->m_pd.lCustData); + pPrInfo->m_pPD->m_pd.lCustData = NULL; + } + }; + } + ui_assoc_object::DoKillAssoc(bDestructing); +} + +ui_prinfo_object::~ui_prinfo_object() +{ + DoKillAssoc(TRUE); +} + +// CPrintInfo member access + +// @pymethod |PyCPrintInfo|DocObject|Return true if the document being printed is a DocObject. +// @pyseemfc CPrintInfo|m_bDocObject +static PyObject *ui_is_doc_object(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, DocObject); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_bDocObject); +} + +// @pymethod |PyCPrintInfo|GetDwFlags|A flags specifying DocObject printing operations. Valid only if data member m_bDocObject is TRUE. +// @pyseemfc CPrintInfo|m_dwFlags +static PyObject *ui_get_dwflags(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetDwFlags); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_dwFlags); +} + +// @pymethod |PyCPrintInfo|SetDwFlags|Set a flag specifying DocObject printing operations. Valid only if data member m_bDocObject is TRUE. +// @pyseemfc CPrintInfo|m_dwFlags +static PyObject *ui_set_dwflags(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetDwFlags",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_dwFlags = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetDocOffsetPage|Get the number of pages preceding the first page of a particular DocObject in a combined DocObject print job. +// @pyseemfc CPrintInfo|m_nOffsetPage +static PyObject *ui_get_doc_offset_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetDocOffsetPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_nOffsetPage); +} + +// @pymethod |PyCPrintInfo|SetDocOffsetPage|Set the number of pages preceding the first page of a particular DocObject in a combined DocObject print job. +// @pyseemfc CPrintInfo|m_nOffsetPage +static PyObject *ui_set_doc_offset_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetDocOffsetPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_nOffsetPage = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|SetPrintDialog|Set a pointer to the CPrintDialog object used to display the Print dialog box for the print job. +// @pyseemfc CPrintInfo|m_pPD +static PyObject *ui_set_print_dialog(PyObject * self, PyObject * args) +{ + PyObject *pyDlg; + if (!PyArg_ParseTuple(args,"O:SetPrintDialog",&pyDlg)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + // Dont use GetPrintDlg so we can convert the pyDlg from + // possibly an instance to the PyCPrintDialog. + CPrintDialog *pDlg = (CPrintDialog *)ui_assoc_object::GetGoodCppObject(pyDlg, &PyCPrintDialog::type); + if (!pDlg) + return NULL; + + PyCPrintDialog *pyPrintDialog = (PyCPrintDialog *)pyDlg; + + delete pInfo->m_pPD; + pInfo->m_pPD = (CPrintDialog *)pDlg; + pInfo->m_pPD->m_pd.nMinPage = 1; + pInfo->m_pPD->m_pd.nMaxPage = 0xffff; + pInfo->m_pPD->m_pd.hInstance = pyPrintDialog->hInstance; + pInfo->m_pPD->m_pd.lpPrintTemplateName = MAKEINTRESOURCE(PRINTDLGORD); + pInfo->m_pPD->m_pd.Flags |= PD_ENABLEPRINTTEMPLATE; + pInfo->m_pPD->m_pd.Flags |= PD_PAGENUMS; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetDirect|TRUE if the Print dialog box will be bypassed for direct printing; FALSE otherwise. +// @pyseemfc CPrintInfo|m_bDirect +static PyObject *ui_get_direct(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetDirect); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_bDirect); +} + +// @pymethod |PyCPrintInfo|SetDirect|Sets to TRUE if the Print dialog box will be bypassed for direct printing; FALSE otherwise. +// @pyseemfc CPrintInfo|m_bDirect +static PyObject *ui_set_direct(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetDirect",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_bDirect = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetPreview|A flag indicating whether the document is being previewed. +// @pyseemfc CPrintInfo|m_bPreview +static PyObject *ui_get_preview(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetPreview); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_bPreview); +} + +// @pymethod |PyCPrintInfo|SetPreview|Set whether the document is being previewed. +// @pyseemfc CPrintInfo|m_bPreview +static PyObject *ui_set_preview(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetPreview",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_bPreview = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetContinuePrinting|A flag indicating whether the framework should continue the print loop. +// @pyseemfc CPrintInfo|m_bContinuePrinting +static PyObject *ui_get_continue_printing(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetContinuePrinting); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_bContinuePrinting); +} + +// @pymethod |PyCPrintInfo|SetContinuePrinting|Set whether the framework should continue the print loop. +// @pyseemfc CPrintInfo|m_bContinuePrinting +static PyObject *ui_set_continue_printing(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetContinuePrinting",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_bContinuePrinting = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetCurPage|Get the number of the current page. +// @pyseemfc CPrintInfo|m_nCurPage +static PyObject *ui_get_cur_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetCurPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_nCurPage); +} + +// @pymethod |PyCPrintInfo|SetCurPage|Set the number of the current page. +// @pyseemfc CPrintInfo|m_nCurPage +static PyObject *ui_set_cur_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetCurPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_nCurPage = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetNumPreviewPages|Get the number of pages displayed in preview mode. +// @pyseemfc CPrintInfo|m_nNumPreviewPages +static PyObject *ui_get_num_preview_pages(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetNumPreviewPages); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("i", pInfo->m_nNumPreviewPages); +} + +// @pymethod |PyCPrintInfo|SetNumPreviewPages|Set the number of pages displayed in preview mode. +// @pyseemfc CPrintInfo|m_nNumPreviewPages +static PyObject *ui_set_num_preview_pages(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetNumPreviewPages",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_nNumPreviewPages = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetUserData|Get a user-created structure. +// @pyseemfc CPrintInfo|m_lpUserData +static PyObject *ui_get_user_data(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetUserData); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + if (pInfo->m_lpUserData) { + return Py_BuildValue ("O", pInfo->m_lpUserData); + } else { + return Py_BuildValue ("z", NULL); + } +} + +// @pymethod |PyCPrintInfo|SetUserData|Set a user-created structure. +// @pyseemfc CPrintInfo|m_lpUserData +static PyObject *ui_set_user_data(PyObject * self, PyObject * args) +{ + PyObject *var; + if (!PyArg_ParseTuple(args,"O:SetUserData",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + if (pInfo->m_lpUserData) XDODECREF((PyObject*)pInfo->m_lpUserData); + pInfo->m_lpUserData = var; + DOINCREF(var); + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetDraw|Get the usable drawing area of the page in logical coordinates. +// @pyseemfc CPrintInfo|m_rectDraw +static PyObject *ui_get_draw(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetDraw); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("(iiii)", + pInfo->m_rectDraw.left, + pInfo->m_rectDraw.top, + pInfo->m_rectDraw.right, + pInfo->m_rectDraw.bottom); +} + +// @pymethod |PyCPrintInfo|SetDraw|Set the usable drawing area of the page in logical coordinates. +// @pyseemfc CPrintInfo|m_rectDraw +static PyObject *ui_set_draw(PyObject * self, PyObject * args) +{ + int left, top, right, bottom; + if (!PyArg_ParseTuple(args,"(iiii):SetDraw",&left,&top,&right,&bottom)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_rectDraw.left = left; + pInfo->m_rectDraw.top = top; + pInfo->m_rectDraw.right = right; + pInfo->m_rectDraw.bottom = bottom; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetPageDesc|Get the format string used to display the page numbers during print preview +// @pyseemfc CPrintInfo|m_strPageDesc +static PyObject *ui_get_page_desc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetPageDesc); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + return Py_BuildValue ("s", pInfo->m_strPageDesc); +} + +// @pymethod |PyCPrintInfo|SetPageDesc|Set the format string used to display the page numbers during print preview +// @pyseemfc CPrintInfo|m_strPageDesc +static PyObject *ui_set_page_desc(PyObject * self, PyObject * args) +{ + char *var; + if (!PyArg_ParseTuple(args,"s:SetPageDesc",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_strPageDesc = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetMinPage|Get the number of the first page of the document. +// @pyseemfc CPrintInfo|GetMinPage +static PyObject *ui_get_min_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetMinPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + UINT ret = pInfo->GetMinPage(); + GUI_END_SAVE; + return Py_BuildValue ("i", ret); +} + +// @pymethod |PyCPrintInfo|SetMinPage|Set the number of the first page of the document. +// @pyseemfc CPrintInfo|SetMinPage +static PyObject *ui_set_min_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetMinPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + pInfo->SetMinPage(var); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetMaxPage|Get the number of the last page of the document. +// @pyseemfc CPrintInfo|GetMaxPage +static PyObject *ui_get_max_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetMaxPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + UINT ret = pInfo->GetMaxPage(); + GUI_END_SAVE; + return Py_BuildValue ("i", ret); +} + +// @pymethod |PyCPrintInfo|SetMaxPage|Set the number of the last page of the document. +// @pyseemfc CPrintInfo|SetMaxPage +static PyObject *ui_set_max_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetMaxPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + pInfo->SetMaxPage(var); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetOffsetPage|Get the number of pages preceding the first page of a DocObject item being printed in a combined DocObject print job. This currently does NOT work, as, if I include the symbol pInfo->GetOffsetPage(), the link fails to find its definition. Allways returns 0. +// @pyseemfc CPrintInfo|GetOffsetPage +static PyObject *ui_get_offset_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetOffsetPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + UINT ret = 0; /* pInfo->GetOffsetPage() */ + GUI_END_SAVE; + return Py_BuildValue ("i", ret); +} + +// @pymethod |PyCPrintInfo|GetFromPage|The number of the first page to be printed. +// @pyseemfc CPrintInfo|GetFromPage +static PyObject *ui_get_from_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetFromPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + UINT ret = pInfo->GetFromPage(); + GUI_END_SAVE; + return Py_BuildValue ("i", ret); +} + +// @pymethod |PyCPrintInfo|GetToPage|The number of the last page to be printed. +// @pyseemfc CPrintInfo|GetToPage +static PyObject *ui_get_to_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetToPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + UINT ret = pInfo->GetToPage(); + GUI_END_SAVE; + return Py_BuildValue ("i", ret); +} + +// CPrintInfo->CPrintDialog access + +// @pymethod |PyCPrintInfo|SetHDC|Sets the printer DC compatible with the users choices, call after the print dialog DoModal finishes. +// @pyseemfc CPrintInfo|m_pPD +// @pyseemfc CPrintDialog|m_pd.hDC +static PyObject *ui_set_hdc(PyObject * self, PyObject * args) +{ + int dc; + // @pyparm int|hdc||The DC. + if (!PyArg_ParseTuple(args, "i:SetHDC", &dc)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.Flags |= PD_RETURNDC; + pInfo->m_pPD->m_pd.hDC = (HDC)dc; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|CreatePrinterDC|Handle to the newly created printer device context, call only after DoModal finishes. +// @pyseemfc CPrintDialog|CreatePrinterDC +static PyObject *ui_create_printer_dc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, CreatePrinterDC); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + HDC hDC = pInfo->m_pPD->CreatePrinterDC(); + GUI_END_SAVE; + return Py_BuildValue("l", (long)hDC); +} + +// @pymethod |PyCPrintInfo|DoModal|Call DoModal on the dialog. +// @pyseemfc CPrintDialog|DoModal +static PyObject *ui_do_modal(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, DoModal); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + int res = pInfo->m_pPD->DoModal(); + GUI_END_SAVE; + return Py_BuildValue("i", res); +} + +#undef MAKE_INT_METH +#define MAKE_INT_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args, mfcName); \ + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); \ + if (!pInfo) return NULL; \ + GUI_BGN_SAVE; \ + int ret = pInfo->m_pPD->mfcName(); \ + GUI_END_SAVE; \ + return Py_BuildValue ("i", ret); \ +} + +#undef MAKE_STR_METH +#define MAKE_STR_METH(fnname, mfcName) \ +static PyObject *fnname( PyObject *self, PyObject *args ) { \ + CHECK_NO_ARGS2(args, mfcName); \ + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); \ + if (!pInfo) return NULL; \ + GUI_BGN_SAVE; \ + CString ret = pInfo->m_pPD->mfcName(); \ + ::GlobalUnlock(pInfo->m_pPD->m_pd.hDevMode); \ + GUI_END_SAVE; \ + return Py_BuildValue ("s", ret); \ +} + +// @pymethod |PyCPrintInfo|GetCopies|The number of copies requested, call only after DoModal finishes. +// @pyseemfc CPrintDialog|GetCopies +MAKE_INT_METH(ui_get_copies, GetCopies) + +// @pymethod |PyCPrintInfo|GetDefaults|Nonzero if the function was successful; otherwise 0. Call this function to retrieve the device defaults of the default printer without displaying a dialog box. The retrieved values are placed in the m_pd structure. In some cases, a call to this function will call the constructor for CPrintDialog with bPrintSetupOnly set to FALSE. In these cases, a printer DC and hDevNames and hDevMode (two handles located in the m_pd data member) are automatically allocated. If the constructor for CPrintDialog was called with bPrintSetupOnly set to FALSE, this function will not only return hDevNames and hDevMode (located in m_pd.hDevNames and m_pd.hDevMode) to the caller, but will also return a printer DC in m_pd.hDC. It is the responsibility of the caller to delete the printer DC and call the WindowsGlobalFree function on the handles when you are finished with the CPrintDialog object. +// @pyseemfc CPrintDialog|GetDefaults +MAKE_INT_METH(ui_get_defaults, GetDefaults) + +// @pymethod |PyCPrintInfo|FreeDefaults|After a call to GetDefaults, and you are through with the CPrintDialog object, this call deletes the printer DC and calls GlobalFree function on the handles. +// @pyseemfc CPrintDialog|GetDefaults +static PyObject *ui_free_defaults(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, FreeDefaults); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + BOOL ok = DeleteDC(pInfo->m_pPD->GetPrinterDC()); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("DeleteDC failed"); + char complaint[256]; + HGLOBAL res = GlobalFree(pInfo->m_pPD->m_pd.hDevNames); + if (res) { + sprintf (complaint, "GlobalFree(pInfo->m_pPD->m_pd.hDevNames) failed: %ld", GetLastError()); + RETURN_ERR(complaint); + } + res = GlobalFree(pInfo->m_pPD->m_pd.hDevMode); + if (res) { + sprintf (complaint, "GlobalFree(pInfo->m_pPD->m_pd.hDevMode) failed: %ld", GetLastError()); + RETURN_ERR(complaint); + } + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetDeviceName|The name of the currently selected printer, call only after DoModal finishes. +// @pyseemfc CPrintDialog|GetDeviceName +MAKE_STR_METH(ui_get_device_name, GetDeviceName) + +// @pymethod |PyCPrintInfo|GetDriverName|The name of the currently selected printer device driver, call only after DoModal finishes. +// @pyseemfc CPrintDialog|GetDriverName +MAKE_STR_METH(ui_get_driver_name, GetDriverName) + +// @pymethod |PyCPrintInfo|GetDlgFromPage|Retrieves the starting page of the print range. +// @pyseemfc CPrintDialog|GetDlgFromPage +MAKE_INT_METH(ui_get_dlg_from_page, GetFromPage) + +// @pymethod |PyCPrintInfo|GetDlgToPage|Retrieves the ending page of the print range. +// @pyseemfc CPrintDialog|GetDlgToPage +MAKE_INT_METH(ui_get_dlg_to_page, GetToPage) + +// @pymethod |PyCPrintInfo|GetPortName|The name of the currently selected printer port, call only after DoModal finishes. +// @pyseemfc CPrintDialog|GetPortName +MAKE_STR_METH(ui_get_port_name, GetPortName) + +// @pymethod |PyCPrintInfo|GetPrinterDC|A handle to the printer device context if successful; otherwise NULL. If the bPrintSetupOnly parameter of the CPrintDialog constructor was FALSE (indicating that the Print dialog box is displayed), then GetPrinterDC returns a handle to the printer device context. You must call the WindowsDeleteDC function to delete the device context when you are done using it. +// @pyseemfc CPrintDialog|GetPrinterDC +static PyObject *ui_get_printer_dc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetPrinterDC); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + GUI_BGN_SAVE; + HDC hDC = pInfo->m_pPD->GetPrinterDC(); + GUI_END_SAVE; + return Py_BuildValue("l", (long)hDC); +} + +// @pymethod |PyCPrintInfo|PrintAll|Nonzero if all pages in the document are to be printed; otherwise 0, call only after DoModal finishes. +// @pyseemfc CPrintDialog|PrintAll +MAKE_INT_METH(ui_print_all, PrintAll) + +// @pymethod |PyCPrintInfo|PrintCollate|Nonzero if the user selects the collate check box in the dialog box; otherwise 0, call only after DoModal finishes. +// @pyseemfc CPrintDialog|PrintCollate +MAKE_INT_METH(ui_print_collate, PrintCollate) + +// @pymethod |PyCPrintInfo|PrintRange|Nonzero if only a range of pages in the document are to be printed; otherwise 0, call only after DoModal finishes. +// @pyseemfc CPrintDialog|PrintRange +MAKE_INT_METH(ui_print_range, PrintRange) + +// @pymethod |PyCPrintInfo|PrintSelection|Nonzero if only the selected items are to be printed; otherwise 0., call only after DoModal finishes +// @pyseemfc CPrintDialog|PrintSelection +MAKE_INT_METH(ui_print_selection, PrintSelection) + + +// CPrintInfo->CPrintDialog->PRINTDLG access + +// @pymethod |PyCPrintInfo|GetHDC|Identifies a device context or an information context, depending on whether the Flags member specifies the PD_RETURNDC or PC_RETURNIC flag. If neither flag is specified, the value of this member is undefined. If both flags are specified, PD_RETURNDC has priority. +// @pyseemfc PRINTDLG|hDC +static PyObject *ui_get_hdc(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetHDC); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + HDC hDC = pInfo->m_pPD->m_pd.hDC; + return Py_BuildValue("l", (long)hDC); +} + +// @pymethod |PyCPrintInfo|GetFlags|A set of bit flags that you can use to initialize the Print common dialog box. When the dialog box returns, it sets these flags to indicate the user's input. This member can be a combination of the following flags: PD_ALLPAGES, PD_COLLATE, PD_DISABLEPRINTTOFILE, PD_ENABLEPRINTHOOK, PD_ENABLEPRINTTEMPLATE, PD_ENABLEPRINTTEMPLATEHANDLE, PD_ENABLESETUPHOOK, PD_ENABLESETUPTEMPLATE, PD_ENABLESETUPTEMPLATEHANDLE, PD_HIDEPRINTTOFILE, PD_NONETWORKBUTTON, PD_NOPAGENUMS, PD_NOSELECTION, PD_NOWARNING, PD_PAGENUMS, PD_PRINTSETUP, PD_PRINTTOFILE, PD_RETURNDC, PD_RETURNDEFAULT, PD_RETURNIC, PD_SELECTION, PD_SHOWHELP, PD_USEDEVMODECOPIES, PD_USEDEVMODECOPIESANDCOLLATE. +// @pyseemfc PRINTDLG|Flags +static PyObject *ui_get_flags(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetFlags); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + DWORD ret = pInfo->m_pPD->m_pd.Flags; + return Py_BuildValue ("i", ret); +} + +// @pymethod |PyCPrintInfo|SetFlags|A set of bit flags that you can use to initialize the Print common dialog box. When the dialog box returns, it sets these flags to indicate the user's input. This member can be a combination of the following flags: PD_ALLPAGES, PD_COLLATE, PD_DISABLEPRINTTOFILE, PD_ENABLEPRINTHOOK, PD_ENABLEPRINTTEMPLATE, PD_ENABLEPRINTTEMPLATEHANDLE, PD_ENABLESETUPHOOK, PD_ENABLESETUPTEMPLATE, PD_ENABLESETUPTEMPLATEHANDLE, PD_HIDEPRINTTOFILE, PD_NONETWORKBUTTON, PD_NOPAGENUMS, PD_NOSELECTION, PD_NOWARNING, PD_PAGENUMS, PD_PRINTSETUP, PD_PRINTTOFILE, PD_RETURNDC, PD_RETURNDEFAULT, PD_RETURNIC, PD_SELECTION, PD_SHOWHELP, PD_USEDEVMODECOPIES, PD_USEDEVMODECOPIESANDCOLLATE. +// @pyseemfc PRINTDLG|Flags +static PyObject *ui_set_flags(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetFlags",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.Flags = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|SetFromPage|The number of the first page to be printed. +// @pyseemfc PRINTDLG|nFromPage +static PyObject *ui_set_from_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetFromPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.nFromPage = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|SetToPage|The number of the last page to be printed. +// @pyseemfc PRINTDLG|nToPage +static PyObject *ui_set_to_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetToPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.nToPage = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetPRINTDLGMinPage|Get the minimum value for the page range specified in the From and To page edit controls. If nMinPage equals nMaxPage, the Pages radio button and the starting and ending page edit controls are disabled. +// @pyseemfc PRINTDLG|nMinPage +static PyObject *ui_get_printdlg_min_page(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetPRINTDLGMinPage); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + DWORD res = pInfo->m_pPD->m_pd.nMinPage; + return Py_BuildValue ("i", res); +} + +// @pymethod |PyCPrintInfo|SetPRINTDLGMinPage|Set the minimum value for the page range specified in the From and To page edit controls. If nMinPage equals nMaxPage, the Pages radio button and the starting and ending page edit controls are disabled. +// @pyseemfc PRINTDLG|nMinPage +static PyObject *ui_set_printdlg_min_page(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetPRINTDLGMinPage",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.nMinPage = var; + RETURN_NONE; +} + +// @pymethod |PyCPrintInfo|GetPRINTDLGCopies|Get the initial number of copies for the Copies edit control if hDevMode is NULL; otherwise, the dmCopies member of theDEVMODE structure contains the initial value. When PrintDlg returns, nCopies contains the actual number of copies to print. This value depends on whether the application or the printer driver is responsible for printing multiple copies. If the PD_USEDEVMODECOPIESANDCOLLATE flag is set in the Flags member, nCopies is always 1 on return, and the printer driver is responsible for printing multiple copies. If the flag is not set, the application is responsible for printing the number of copies specified by nCopies. For more information, see the description of the PD_USEDEVMODECOPIESANDCOLLATE flag. +// @pyseemfc PRINTDLG|nCopies +static PyObject *ui_get_printdlg_copies(PyObject * self, PyObject * args) +{ + CHECK_NO_ARGS2(args, GetPRINTDLGCopies); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + DWORD res = pInfo->m_pPD->m_pd.nCopies; + return Py_BuildValue ("i", res); +} + +// @pymethod |PyCPrintInfo|SetPRINTDLGCopies|Set the initial number of copies for the Copies edit control if hDevMode is NULL; otherwise, the dmCopies member of theDEVMODE structure contains the initial value. When PrintDlg returns, nCopies contains the actual number of copies to print. This value depends on whether the application or the printer driver is responsible for printing multiple copies. If the PD_USEDEVMODECOPIESANDCOLLATE flag is set in the Flags member, nCopies is always 1 on return, and the printer driver is responsible for printing multiple copies. If the flag is not set, the application is responsible for printing the number of copies specified by nCopies. For more information, see the description of the PD_USEDEVMODECOPIESANDCOLLATE flag. +// @pyseemfc PRINTDLG|nCopies +static PyObject *ui_set_printdlg_copies(PyObject * self, PyObject * args) +{ + int var; + if (!PyArg_ParseTuple(args,"i:SetPRINTDLGCopies",&var)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(self); + if (!pInfo) + return NULL; + pInfo->m_pPD->m_pd.nCopies = var; + RETURN_NONE; +} + +///////////////////////////////////////////////////////////////////// +// PrintInfo Methods +// @object PyCPrintInfo|Encapsulates an MFC CPrintInfo class, its member class, and the structure member of the CPrintDialog. +static struct PyMethodDef ui_prinfo_methods[] = { + + // CPrintInfo + + {"DocObject", ui_is_doc_object, 1}, // @pymeth DocObject|A flag indicating whether the document being printed is a DocObject. + {"GetDwFlags", ui_get_dwflags, 1}, // @pymeth GetDwFlags|A flags specifying DocObject printing operations. Valid only if data member m_bDocObject is TRUE. + {"SetDwFlags", ui_set_dwflags, 1}, // @pymeth SetDwFlags|Set a flag specifying DocObject printing operations. Valid only if data member m_bDocObject is TRUE. + {"GetDocOffsetPage", ui_get_doc_offset_page, 1}, // @pymeth GetDocOffsetPage|Get the number of pages preceding the first page of a particular DocObject in a combined DocObject print job. + {"SetDocOffsetPage", ui_set_doc_offset_page, 1}, // @pymeth SetDocOffsetPage|Set the number of pages preceding the first page of a particular DocObject in a combined DocObject print job. + {"SetPrintDialog", ui_set_print_dialog, 1}, // @pymeth SetPrintDialog|Set a pointer to the CPrintDialog object used to display the Print dialog box for the print job. + {"GetDirect", ui_get_direct, 1}, // @pymeth GetDirect|TRUE if the Print dialog box will be bypassed for direct printing; FALSE otherwise. + {"SetDirect", ui_set_direct, 1}, // @pymeth SetDirect|Sets to TRUE if the Print dialog box will be bypassed for direct printing; FALSE otherwise. + {"GetPreview", ui_get_preview, 1}, // @pymeth GetPreview|A flag indicating whether the document is being previewed. + {"SetPreview", ui_set_preview, 1}, // @pymeth SetPreview|Set whether the document is being previewed. + {"GetContinuePrinting", ui_get_continue_printing, 1}, // @pymeth GetContinuePrinting|A flag indicating whether the framework should continue the print loop. + {"SetContinuePrinting", ui_set_continue_printing, 1}, // @pymeth SetContinuePrinting|Set whether the framework should continue the print loop. + {"GetCurPage", ui_get_cur_page, 1}, // @pymeth GetCurPage|Get the number of the current page. + {"SetCurPage", ui_set_cur_page, 1}, // @pymeth SetCurPage|Set the number of the current page. + {"GetNumPreviewPages", ui_get_num_preview_pages, 1}, // @pymeth GetNumPreviewPages|Get the number of pages displayed in preview mode. + {"SetNumPreviewPages", ui_set_num_preview_pages, 1}, // @pymeth SetNumPreviewPages|Set the number of pages displayed in preview mode. + {"GetUserData", ui_get_user_data, 1}, // @pymeth GetUserData|Get a user-created structure. + {"SetUserData", ui_set_user_data, 1}, // @pymeth SetUserData|Set a user-created structure. + {"GetDraw", ui_get_draw, 1}, // @pymeth GetDraw|Get the usable drawing area of the page in logical coordinates. + {"SetDraw", ui_set_draw, 1}, // @pymeth SetDraw|Set the usable drawing area of the page in logical coordinates. + {"GetPageDesc", ui_get_page_desc, 1}, // @pymeth GetPageDesc|Get the format string used to display the page numbers during print preview + {"SetPageDesc", ui_set_page_desc, 1}, // @pymeth SetPageDesc|Set the format string used to display the page numbers during print preview + {"GetMinPage", ui_get_min_page, 1}, // @pymeth GetMinPage|Get the number of the first page of the document. + {"SetMinPage", ui_set_min_page, 1}, // @pymeth SetMinPage|Set the number of the first page of the document. + {"GetMaxPage", ui_get_max_page, 1}, // @pymeth GetMaxPage|Get the number of the last page of the document. + {"SetMaxPage", ui_set_max_page, 1}, // @pymeth SetMaxPage|Set the number of the last page of the document. + {"GetOffsetPage", ui_get_offset_page, 1}, // @pymeth GetOffsetPage|Get the number of pages preceding the first page of a DocObject item being printed in a combined DocObject print job. + {"GetFromPage", ui_get_from_page, 1}, // @pymeth GetFromPage|The number of the first page to be printed. + {"GetToPage", ui_get_to_page, 1}, // @pymeth GetToPage|The number of the last page to be printed. + + // CPrintDialog (CPrintInfo->m_pPD) + + {"SetHDC", ui_set_hdc, 1}, // @pymeth SetHDC|Sets the printer DC compatible with the users choices, call after the print dialog DoModal finishes. + {"CreatePrinterDC", ui_create_printer_dc, 1}, // @pymeth CreatePrinterDC|Handle to the newly created printer device context, call only after DoModal finishes. + {"DoModal", ui_do_modal, 1}, // @pymeth DoModal|Call DoModal on the dialog. + {"GetCopies", ui_get_copies, 1}, // @pymeth GetCopies|The number of copies requested, call only after DoModal finishes. + {"GetDefaults", ui_get_defaults, 1}, // @pymeth GetDefaults|Retrieves device defaults without displaying a dialog box. + {"FreeDefaults", ui_free_defaults, 1}, // @pymeth FreeDefaults|After a call to GetDefaults, and you are through with the CPrintDialog object, this call deletes the printer DC and calls GlobalFree function on the handles. + {"GetDeviceName", ui_get_device_name, 1}, // @pymeth GetDeviceName|The name of the currently selected printer, call only after DoModal finishes. + {"GetDriverName", ui_get_driver_name, 1}, // @pymeth GetDriverName|The name of the currently selected printer device driver, call only after DoModal finishes. + {"GetDlgFromPage", ui_get_dlg_from_page, 1}, // @pymeth GetDlgFromPage|Retrieves the starting page of the print range. + {"GetDlgToPage", ui_get_dlg_to_page, 1}, // @pymeth GetDlgToPage|Retrieves the ending page of the print range. + {"GetPortName", ui_get_port_name, 1}, // @pymeth GetPortName|The name of the currently selected printer port, call only after DoModal finishes. + {"GetPrinterDC", ui_get_printer_dc, 1}, // @pymeth GetPrinterDC|A handle to the printer device context if successful; otherwise NULL. If the bPrintSetupOnly parameter of the CPrintDialog constructor was FALSE (indicating that the Print dialog box is displayed), then GetPrinterDC returns a handle to the printer device context. You must call the WindowsDeleteDC function to delete the device context when you are done using it. + {"PrintAll", ui_print_all, 1}, // @pymeth PrintAll|Nonzero if all pages in the document are to be printed; otherwise 0, call only after DoModal finishes. + {"PrintCollate", ui_print_collate, 1}, // @pymeth PrintCollate|Nonzero if the user selects the collate check box in the dialog box; otherwise 0, call only after DoModal finishes. + {"PrintRange", ui_print_range, 1}, // @pymeth PrintRange|Nonzero if only a range of pages in the document are to be printed; otherwise 0, call only after DoModal finishes. + {"PrintSelection", ui_print_selection, 1}, // @pymeth PrintSelection|Nonzero if only the selected items are to be printed; otherwise 0., call only after DoModal finishes + + // PRINTDLG (CPrintInfo->CPrintDialog->m_pd) + + {"GetHDC", ui_get_hdc, 1}, // @pymeth GetHDC|Identifies a device context or an information context, depending on whether the Flags member specifies the PD_RETURNDC or PC_RETURNIC flag. If neither flag is specified, the value of this member is undefined. If both flags are specified, PD_RETURNDC has priority. + {"GetFlags", ui_get_flags, 1}, // @pymeth GetFlags|A set of bit flags that you can use to initialize the Print common dialog box. When the dialog box returns, it sets these flags to indicate the user's input. + {"SetFlags", ui_set_flags, 1}, // @pymeth SetFlags|A set of bit flags that you can use to initialize the Print common dialog box. When the dialog box returns, it sets these flags to indicate the user's input. + {"SetFromPage", ui_set_from_page, 1}, // @pymeth SetFromPage|The number of the first page to be printed. + {"SetToPage", ui_set_to_page, 1}, // @pymeth SetToPage|The number of the first page to be printed. + {"GetPRINTDLGMinPage", ui_get_printdlg_min_page, 1}, // @pymeth GetPRINTDLGMinPage|Get the minimum value for the page range specified in the From and To page edit controls. If nMinPage equals nMaxPage, the Pages radio button and the starting and ending page edit controls are disabled. + {"SetPRINTDLGMinPage", ui_set_printdlg_min_page, 1}, // @pymeth SetPRINTDLGMinPage|Set the minimum value for the page range specified in the From and To page edit controls. If nMinPage equals nMaxPage, the Pages radio button and the starting and ending page edit controls are disabled. + {"GetPRINTDLGCopies", ui_get_printdlg_copies, 1}, // @pymeth GetPRINTDLGCopies|Gets the initial number of copies for the Copies edit control if hDevMode is NULL; otherwise, the dmCopies member of the DEVMODE structure contains the initial value. + {"SetPRINTDLGCopies", ui_set_printdlg_copies, 1}, // @pymeth SetPRINTDLGCopies|Sets the initial number of copies for the Copies edit control if hDevMode is NULL; otherwise, the dmCopies member of the DEVMODE structure contains the initial value. + {NULL, NULL} +}; + +ui_type ui_prinfo_object::type( + "PyCPrintInfo", + &ui_assoc_object::type, + sizeof(ui_prinfo_object), + ui_prinfo_methods, + GET_PY_CTOR(ui_prinfo_object)); + diff --git a/Pythonwin/win32prinfo.h b/Pythonwin/win32prinfo.h new file mode 100644 index 0000000000..f6101509e6 --- /dev/null +++ b/Pythonwin/win32prinfo.h @@ -0,0 +1,13 @@ +// printer info class +class PYW_EXPORT ui_prinfo_object : public ui_assoc_object { +protected: + ui_prinfo_object() : m_deletePrInfo (FALSE) { } + ~ui_prinfo_object(); + virtual void SetAssocInvalid(); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); +public: + static ui_type type; + MAKE_PY_CTOR(ui_prinfo_object) + static CPrintInfo *GetPrintInfo(PyObject *self); + BOOL m_deletePrInfo; +}; diff --git a/Pythonwin/win32prop.cpp b/Pythonwin/win32prop.cpp new file mode 100644 index 0000000000..aae6ad6471 --- /dev/null +++ b/Pythonwin/win32prop.cpp @@ -0,0 +1,926 @@ +/* + + propsheet data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + + propsheet is derived from window. + +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 "win32win.h" +#include "win32dlg.h" +#include "win32prop.h" +#include + +#include "pythonpsheet.h" +#include "pythonppage.h" +#include "reswin32ui.h" + +extern HGLOBAL MakeResourceFromDlgList(PyObject *tmpl); + +CTabCtrl *PyGetTabCtrl(PyObject *self) +{ + return (CTabCtrl *)PyCWnd::GetPythonGenericWnd(self, &ui_tabctrl_object::type); +} +CTabCtrl *PyGetTabCtrlWithWnd(PyObject *self) +{ + CTabCtrl *pTab = PyGetTabCtrl(self); + if (pTab->m_hWnd==NULL || !::IsWindow(pTab->m_hWnd)) + RETURN_ERR("The tab control has no window"); + return pTab; +} + +// +// property sheet helpers +// + +CPropertySheet *GetPropSheet(PyObject *self) +{ + return (CPropertySheet *)PyCWnd::GetPythonGenericWnd(self, &PyCPropertySheet::type); +} +CPythonPropertySheet *GetPythonPropSheet(PyObject *self) +{ + CPythonPropertySheet *ret = + (CPythonPropertySheet *)PyCWnd::GetPythonGenericWnd(self, &PyCPropertySheet::type); + if (!ret->IsKindOf(RUNTIME_CLASS(CPythonPropertySheet))) + RETURN_TYPE_ERR("Object is not of the correct type"); + return ret; +} +// +// property page helpers +// +CPythonPropertyPage *GetPropPage(PyObject *self) +{ + return (CPythonPropertyPage *)PyCWnd::GetPythonGenericWnd(self, &PyCPropertyPage::type); +} + +BOOL PropSheetCheckForPageCreate( UINT id ) +{ +// if (!CPythonPropertyPage::CheckTemplate(id)) +// RETURN_ERR("The property page can not be located"); + return TRUE; +} +BOOL PropSheetCheckForPageCreate( LPCTSTR id ) +{ +// if (!CPythonPropertyPage::CheckTemplate(id)) +// RETURN_ERR("The property page can not be located"); + return TRUE; +} + +BOOL PropSheetCheckForDisplay( CPropertySheet *pSheet ) +{ + int max = pSheet->GetPageCount(); + if (max==0) + RETURN_ERR("The property sheet has no pages"); +// for (int i=0; iGetPage(i); + // use help ID here +// if (!pPage->CheckTemplate()) { +// char buf[80]; +// sprintf(buf, "Property Page %d can not be located", i ); +// RETURN_ERR(buf); +// } +// } + return TRUE; +} + +///////////////////////////////////////////////////////////////////// +// +// PropSheet object +// +////////////////////////////////////////////////////////////////////// +PyCPropertySheet::PyCPropertySheet() +{ +// bManualDelete = TRUE; +} +PyCPropertySheet::~PyCPropertySheet() +{ +} +/******** + TRACE("PropSheet object destructing\n"); + CPythonPropertySheet *pSheet = GetPythonPropSheet(this); + ASSERT(pSheet); + PyCWnd::DoKillAssoc(TRUE); + ui_assoc_object::SetAssocInvalid(); // must call this explicitely, as I ignore SetAssocInvalid +} + +void PyCPropertySheet::DoKillAssoc(BOOL bDestructing ) +{ + return; +} +*************/ +// @pymethod |win32ui|CreatePropertySheet|Creates a property sheet object. +PyObject *PyCPropertySheet::create( PyObject *self, PyObject *args ) +{ + PyObject *obParent = NULL, + *obCaption; + CWnd *pParent = NULL; + int iSelect = 0; + if (!PyArg_ParseTuple(args,"O|Oi", + &obCaption, // @pyparm string|caption||The caption for the property sheet. + &obParent, // @pyparm |parent|None|The parent window of the property sheet. + &iSelect)) // @pyparm int|select|0|The index of the first page to be selected. + return NULL; + if (obParent) { + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parameter 2 must be a PyCWnd object"); + pParent = (CWnd *)PyCWnd::GetPythonGenericWnd(obParent); + } + CPythonPropertySheet *pPS; + if (PyInt_Check(obCaption)) { + int id = (int)PyInt_AsLong(obCaption); + GUI_BGN_SAVE; + pPS = new CPythonPropertySheet(id, pParent, iSelect); + GUI_END_SAVE; + } + else if (PyString_Check(obCaption)) { + char *value = PyString_AsString(obCaption); + GUI_BGN_SAVE; + pPS = new CPythonPropertySheet(value, pParent, iSelect); + GUI_END_SAVE; + } + else + RETURN_TYPE_ERR("parameter 1 must be an integer or string object"); + PyCPropertySheet *ret = (PyCPropertySheet *)ui_assoc_object::make( PyCPropertySheet::type, pPS); + return ret; +} + +/////////////////////////////////////// +// +// PropSheet Methods +// +// @pymethod |PyCPropertySheet|AddPage|Adds the supplied page with the rightmost tab in the property sheet. +PyObject *ui_propsheet_add_page( PyObject *self, PyObject *args ) +{ + PyObject *obPage; + CPythonPropertyPage *pPage; + if (!PyArg_ParseTuple(args,"O", &obPage)) + // @pyparm |page||The page to be added. + return NULL; + if (!ui_base_class::is_uiobject(obPage, &PyCPropertyPage::type)) { + RETURN_TYPE_ERR("passed object must be a PyCPropertyPage object"); + } + pPage = GetPropPage(obPage); + if (!pPage) + return NULL; + + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + pPS->AddPage(pPage); // @pyseemfc PyCPropertySheet|AddPage + GUI_END_SAVE; + // @comm Add pages to the property sheet in the left-to-right order you want them to appear. + RETURN_NONE; +} +// @pymethod int|PyCPropertySheet|GetActiveIndex|Retrieves the index of the active page of the property sheet. +PyObject *ui_propsheet_get_active_index( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + int rc = pPS->GetActiveIndex(); + GUI_END_SAVE; + + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCPropertySheet|GetPageIndex|Retrieves the index of the specified page of the property sheet. +PyObject *ui_propsheet_get_page_index( PyObject *self, PyObject *args ) +{ + PyObject *obPage; + // @pyparm |page||The page. + if (!PyArg_ParseTuple(args, "O:GetPageIndex", &obPage)) + return NULL; + CPythonPropertySheet *pPS = GetPythonPropSheet(self); + if (!pPS) return NULL; + CPythonPropertyPage *pPage = GetPropPage(obPage); + if (!pPage) return NULL; + GUI_BGN_SAVE; + int rc = pPS->GetPageIndex(pPage); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod |PyCPropertySheet|EnableStackedTabs|Enables or disables stacked tabs. +PyObject *ui_propsheet_enable_stacked_tabs( PyObject *self, PyObject *args ) +{ + BOOL stacked; + if (!PyArg_ParseTuple(args,"i", &stacked)) + // @pyparm int|stacked||A boolean flag + return NULL; + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + pPS->EnableStackedTabs(stacked); + GUI_END_SAVE; + RETURN_NONE; +} + + +// @pymethod |PyCPropertySheet|GetPage|Returns the specified property page. +PyObject *ui_propsheet_get_page( PyObject *self, PyObject *args ) +{ + int pagenum; + CPropertyPage *pPage; + if (!PyArg_ParseTuple(args,"i", &pagenum)) + // @pyparm int|pageNo||The index of the page toretrieve. + return NULL; + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + pPage = pPS->GetPage(pagenum); + GUI_END_SAVE; + if (!pPage) + RETURN_ERR("The page does not exist"); + // @pyseemfc PyCPropertySheet|GetPage + PyCPropertyPage *ret = (PyCPropertyPage *)ui_assoc_object::make( PyCPropertyPage::type, pPage)->GetGoodRet(); + return ret; +} + +// @pymethod |PyCPropertySheet|GetActivePage|Returns the currently active property page. +PyObject *ui_propsheet_get_active_page( PyObject *self, PyObject *args ) +{ + CPropertyPage *pPage; + if (!PyArg_ParseTuple(args,":GetActivePage")) + return NULL; + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + pPage = pPS->GetActivePage(); + GUI_END_SAVE; + if (!pPage) + RETURN_ERR("The page does not exist"); + // @pyseemfc PyCPropertySheet|GetActivePage + PyCPropertyPage *ret = (PyCPropertyPage *)ui_assoc_object::make( PyCPropertyPage::type, pPage)->GetGoodRet(); + return ret; +} + +// @pymethod |PyCPropertySheet|SetActivePage|Programmatically sets the active page object. +PyObject *ui_propsheet_set_active_page( PyObject *self, PyObject *args ) +{ + PyObject *obPage; + // @pyparm |page||The page. + if (!PyArg_ParseTuple(args, "O:SetActivePage", &obPage)) + return NULL; + CPythonPropertySheet *pPS = GetPythonPropSheet(self); + if (!pPS) return NULL; + CPythonPropertyPage *pPage = GetPropPage(obPage); + if (!pPage) return NULL; + GUI_BGN_SAVE; + BOOL ok = pPS->SetActivePage(pPage); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetActivePage failed"); + RETURN_NONE; +} + +// @pymethod int|PyCPropertySheet|GetPageCount|Returns the number of pages. +PyObject *ui_propsheet_get_page_count( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + int rc = pPS->GetPageCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod |PyCPropertySheet|GetTabCtrl|Returns the tab control used by the sheet. +PyObject *ui_propsheet_get_tab_ctrl( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + GUI_BGN_SAVE; + CTabCtrl *pTab = pPS->GetTabControl(); + GUI_END_SAVE; + if (pTab==NULL) + RETURN_ERR("The property page does not have a tab control"); + return ui_assoc_object::make( ui_tabctrl_object::type, pTab)->GetGoodRet(); +} + +// @pymethod int|PyCPropertySheet|DoModal|Displays the property sheet as a modal dialog. +PyObject *ui_propsheet_do_modal( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CPropertySheet *pPS; + if (!(pPS=GetPropSheet(self))) + return NULL; + if (!PropSheetCheckForDisplay(pPS)) + return NULL; + Py_INCREF(self); // make sure Python doesnt kill the object while in a modal call. + // really only for the common dialog(!?), and other non CPythonPropSheet's + int ret; + GUI_BGN_SAVE; + ret = pPS->DoModal(); + GUI_END_SAVE; + DODECREF(self); + return Py_BuildValue("i", ret); +} + +// @pymethod |PyCPropertySheet|CreateWindow|Displays the property sheet as a modeless dialog. +PyObject *ui_propsheet_create_window( PyObject *self, PyObject *args ) +{ + PyObject *obParent = NULL; + int dwStyle = WS_SYSMENU | WS_POPUP | WS_CAPTION | DS_MODALFRAME | WS_VISIBLE; + int dwExStyle = WS_EX_DLGMODALFRAME; + CWnd *pParent = NULL; + // @pyparm |parent|None|The parent of the dialog. + // @pyparm int|style|WS_SYSMENU\|WS_POPUP\|WS_CAPTION\|DS_MODALFRAME\|WS_VISIBLE|The style for the window. + // @pyparm int|exStyle|WS_EX_DLGMODALFRAME|The extended style for the window. + if (!PyArg_ParseTuple(args,"|Oll", &obParent, &dwStyle, &dwExStyle)) + return NULL; + if (obParent && obParent!=Py_None) { + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parameter 1 must be a PyCWnd object"); + pParent = (CWnd *)PyCWnd::GetPythonGenericWnd(obParent); + if (!pParent) + return NULL; + } + CPythonPropertySheet *pPS; + if (!(pPS=GetPythonPropSheet(self))) + return NULL; + if (!PropSheetCheckForDisplay(pPS)) + return NULL; + int rc; + const char *failMsg = "Create() failed"; + GUI_BGN_SAVE; + try { + rc=pPS->Create(pParent, dwStyle, dwExStyle ); + } + catch (...) { + rc = NULL; + failMsg = "Create() caused an exception - it is likely that the specified template can not be located"; + } + GUI_END_SAVE; + if (!rc) + RETURN_ERR((char *)failMsg); + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|EndDialog|Closes the dialog, with the specified result. +PyObject *ui_propsheet_end_dialog( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + int result; + // @pyparm int|result||The result to be returned by DoModal. + if (!PyArg_ParseTuple( args, "i", &result)) + return NULL; + GUI_BGN_SAVE; + pPS->EndDialog(result); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertySheet|RemovePage|Removes the specified page from the sheet. +PyObject *ui_propsheet_remove_page( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS; + if (!(pPS=GetPropSheet(self))) + return NULL; + PyObject *ob; + // @pyparm int|offset||The page number to remove + // @pyparmalt1 |page||The page to remove + if (!PyArg_ParseTuple( args, "O", &ob)) + return NULL; + if (PyInt_Check(ob)) { + int id = (int)PyInt_AsLong(ob); + GUI_BGN_SAVE; + pPS->RemovePage(id); + GUI_END_SAVE; + } + else if (ui_base_class::is_uiobject(ob, &PyCPropertyPage::type)) { + CPythonPropertyPage * pPage = GetPropPage(ob); + if (!pPage) + return NULL; + GUI_BGN_SAVE; + pPS->RemovePage(pPage); + GUI_END_SAVE; + } else + RETURN_TYPE_ERR("passed object must be an integer or PyCPropertyPage object"); + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|SetTitle|Sets the caption for the property sheet. +PyObject *ui_propsheet_set_title( PyObject *self, PyObject *args ) +{ + char *caption; + // @pyparm string|title||The new caption + if (!PyArg_ParseTuple(args, "s:SetTitle", &caption)) + return NULL; + CPythonPropertySheet *pPS = GetPythonPropSheet(self); + if (!pPS) return NULL; + GUI_BGN_SAVE; + pPS->SetTitle(caption); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|PressButton|Simulates the choice of the specified button in a property sheet. +PyObject *ui_propsheet_press_button( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + int button; + // @pyparm int|button||The button to press + if (!PyArg_ParseTuple( args, "i", &button)) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pPS->PressButton(button); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("PressButton failed"); + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|SetWizardButtons|Enables the wizard buttons +PyObject *ui_propsheet_set_wizard_buttons( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + int flags; + // @pyparm int|flags||The wizard flags + if (!PyArg_ParseTuple( args, "i", &flags)) + return NULL; + GUI_BGN_SAVE; + pPS->SetWizardButtons(flags); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|SetWizardMode|Enables the wizard mode +PyObject *ui_propsheet_set_wizard_mode( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + CHECK_NO_ARGS2(args,SetWizardMode); + GUI_BGN_SAVE; + pPS->SetWizardMode(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|SetFinishText|Sets the text for the Finish button +PyObject *ui_propsheet_set_finish_text( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + char *text; + // @pyparm string|text||The next for the button + if (!PyArg_ParseTuple( args, "s", &text)) + return NULL; + GUI_BGN_SAVE; + pPS->SetFinishText(text); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCPropertySheet|SetPSHBit|Sets or clears a bit in m_psh.dwFlags +PyObject *ui_propsheet_set_pshbit( PyObject *self, PyObject *args ) +{ + CPropertySheet *pPS = pPS=GetPropSheet(self); + if (!pPS) return NULL; + DWORD bitMask = 0; + BOOL bitValue = 0; + + if (!PyArg_ParseTuple( args, "ii", + &bitMask, // @pyparm int|bitMask||The PSH_* bit mask constant + &bitValue)) // @pyparm int|bitValue||1 to set, 0 to clear + return NULL; + GUI_BGN_SAVE; + if (bitValue) { + pPS->m_psh.dwFlags |= bitMask; + } else { + pPS->m_psh.dwFlags &= ~bitMask; + } + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCPropertySheet|OnInitDialog|Calls the default MFC OnInitDialog handler. +static PyObject *ui_propsheet_on_init_dialog( PyObject *self, PyObject *args ) +{ + class HackProtected : public CPythonPropertySheet + { + public: + BOOL BaseOnInitDialog(void) {return CPropertySheet::OnInitDialog();} + }; + CPythonPropertySheet *pPS = GetPythonPropSheet(self); + if (pPS==NULL) + return NULL; + CHECK_NO_ARGS2(args, OnInitDialog ); + GUI_BGN_SAVE; + BOOL rc = ((HackProtected *)pPS)->BaseOnInitDialog(); + GUI_END_SAVE; + // @xref + return Py_BuildValue("i", rc); +} + +// inherited from window +// +/////////////////////////////////////// +// @object PyCPropertySheet|A class which encapsulates an MFC CPropertySheet object. Derived from a object. +static struct PyMethodDef ui_propsheet_methods[] = { + {"AddPage", ui_propsheet_add_page, 1}, // @pymeth AddPage|Adds the supplied page with the rightmost tab in the property sheet. + {"CreateWindow", ui_propsheet_create_window, 1}, // @pymeth CreateWindow|Displays the property sheet as a modeless dialog. + {"DoModal", ui_propsheet_do_modal, 1}, // @pymeth DoModal|Displays the property sheet as a modal dialog. + {"EnableStackedTabs",ui_propsheet_enable_stacked_tabs,1}, // @pymeth EnableStackedTabs|Enables or disables stacked tabs. + {"EndDialog", ui_propsheet_end_dialog, 1}, // @pymeth EndDialog|Closes the dialog, with the specified result. + {"GetActiveIndex", ui_propsheet_get_active_index, 1}, // @pymeth GetActiveIndex|Retrieves the index of the active page of the property sheet. + {"GetActivePage", ui_propsheet_get_active_page, 1}, // @pymeth GetActivePage|Returns the currently active property page. + {"GetPage", ui_propsheet_get_page, 1}, // @pymeth GetPage|Returns the specified property page. + {"GetPageIndex", ui_propsheet_get_page_index, 1}, // @pymeth GetPageIndex|Retrieves the index of the specified page of the property sheet. + {"GetPageCount", ui_propsheet_get_page_count, 1}, // @pymeth GetPageCount|Returns the number of pages. + {"GetTabCtrl", ui_propsheet_get_tab_ctrl, 1}, // @pymeth GetTabCtrl|Returns the tab control used by the sheet. + {"OnInitDialog", ui_propsheet_on_init_dialog, 1}, // @pymeth OnInitDialog|Calls the default MFC OnInitDialog handler. + {"PressButton", ui_propsheet_press_button, 1}, // @pymeth PressButton|Simulates the choice of the specified button in a property sheet. + {"RemovePage", ui_propsheet_remove_page, 1}, // @pymeth RemovePage|Removes the specified page from the sheet. + {"SetActivePage", ui_propsheet_set_active_page, 1}, // @pymeth SetActivePage|Programmatically sets the active page object. + {"SetTitle", ui_propsheet_set_title, 1}, // @pymeth SetTitle|Sets the caption for the property sheet. + {"SetFinishText", ui_propsheet_set_finish_text, 1}, // @pymeth SetFinishText|Sets the text for the Finish button + {"SetWizardMode", ui_propsheet_set_wizard_mode, 1}, // @pymeth SetWizardMode|Enables the wizard mode + {"SetWizardButtons",ui_propsheet_set_wizard_buttons,1}, // @pymeth SetWizardButtons|Enables the wizard buttons + {"SetPSHBit", ui_propsheet_set_pshbit, 1}, // @pymeth SetPSHBit|Sets (or clears) a bit in m_psh.dwFlags. + {NULL, NULL} +}; + +ui_type_CObject PyCPropertySheet::type("PyCPropertySheet", + &PyCWnd::type, + RUNTIME_CLASS(CPropertySheet), + sizeof(PyCPropertySheet), + ui_propsheet_methods, + GET_PY_CTOR(PyCPropertySheet)); + +///////////////////////////////////////////////////////////////////// +// +// Property Page object +// +////////////////////////////////////////////////////////////////////// +PyCPropertyPage::PyCPropertyPage() +{ + bManualDelete = FALSE; // dont "delete" the CWnd. +} +PyCPropertyPage::~PyCPropertyPage() +{ +// CPythonPropertyPage *pPage = GetPropPage(this); +// delete pPage; +} +// @pymethod |win32ui|CreatePropertyPage|Creates a property page object. +PyObject *PyCPropertyPage::create( PyObject *self, PyObject *args ) +{ + PyObject *obTemplate = NULL; + int idCaption = 0; + if (!PyArg_ParseTuple(args,"O|i", + &obTemplate, // @pyparm int/string|resource||The resource ID to use for the page. + &idCaption)) // @pyparm int|caption|0|The ID if the string resource to use for the caption. + return NULL; + CPythonPropertyPage *pPP; + if (PyInt_Check(obTemplate)) { + int id = (int)PyInt_AsLong(obTemplate); + if (!PropSheetCheckForPageCreate(id)) + return NULL; + GUI_BGN_SAVE; + pPP = new CPythonPropertyPage(id, idCaption); + GUI_END_SAVE; + } + else if (PyString_Check(obTemplate)) { + char *value = PyString_AsString(obTemplate); + if (!PropSheetCheckForPageCreate(value)) + return NULL; + GUI_BGN_SAVE; + pPP = new CPythonPropertyPage(value, idCaption); + GUI_END_SAVE; + } + else + RETURN_TYPE_ERR("parameter 1 must be an integer or string object"); + PyCPropertyPage *ret = (PyCPropertyPage *)ui_assoc_object::make( PyCPropertyPage::type, pPP); + return ret; +} + +// @pymethod |win32ui|CreatePropertyPageIndirect|Creates a property page object from a template. +PyObject *PyCPropertyPage::createIndirect(PyObject *, PyObject *args) +{ + PyObject *obTemplate = NULL; + int idCaption = 0; + // @pyparm list|resourceList||A list of [, , ...], which describe the page to be created. + // @pyparm int|caption|0|The ID if the string resource to use for the caption. + if (!PyArg_ParseTuple(args, "O|i", &obTemplate, &idCaption)) + return NULL; + + HGLOBAL h = MakeResourceFromDlgList(obTemplate); + if (h == NULL) + return NULL; + + CPythonPropertyPage *pPP = new CPythonPropertyPage(IDD_DUMMYPROPPAGE, idCaption); + if (!pPP->SetTemplate(h)) + return NULL; + PyCPropertyPage *ret = (PyCPropertyPage *)ui_assoc_object::make( PyCPropertyPage::type, pPP); + return ret; +} + +// @pymethod |PyCPropertyPage|CancelToClose|Changes the Cancel button to Close. +PyObject *ui_proppage_cancel_to_close( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS(args); + GUI_BGN_SAVE; + pPP->CancelToClose(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertyPage|SetModified|Sets the modified flag. +PyObject *ui_proppage_set_modified( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + BOOL bChanged = TRUE; + // @pyparm int|bChanged|1|A flag to indicate the new modified state. + if (!PyArg_ParseTuple( args, "|i", &bChanged)) + return NULL; + GUI_BGN_SAVE; + pPP->SetModified(bChanged); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertyPage|SetPSPBit|Sets or clears a bit in m_psp.dwFlags +PyObject *ui_proppage_set_pspbit( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + DWORD bitMask = 0; + BOOL bitValue = 0; + + if (!PyArg_ParseTuple( args, "ii", + &bitMask, // @pyparm int|bitMask||The PSP_* bit mask constant + &bitValue)) // @pyparm int|bitValue||1 to set, 0 to clear + return NULL; + GUI_BGN_SAVE; + if (bitValue) { + pPP->m_psp.dwFlags |= bitMask; + } else { + pPP->m_psp.dwFlags &= ~bitMask; + } + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertyPage|OnOK|Calls the default MFC OnOK handler. +PyObject *ui_proppage_on_ok( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnOK ); + // @xref + GUI_BGN_SAVE; + pPP->CPropertyPage::OnOK(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertyPage|OnApply|Calls the default MFC OnApply handler. +PyObject *ui_proppage_on_apply( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnApply ); + // @xref + GUI_BGN_SAVE; + BOOL bOk = pPP->CPropertyPage::OnApply(); + GUI_END_SAVE; + return PyInt_FromLong((long)bOk); +} +// @pymethod |PyCPropertyPage|OnReset|Calls the default MFC OnReset handler. +PyObject *ui_proppage_on_reset( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnReset ); + // @xref + GUI_BGN_SAVE; + pPP->CPropertyPage::OnReset(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCPropertyPage|OnQueryCancel|Calls the default MFC OnQueryCancel handler. +PyObject *ui_proppage_on_query_cancel( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnQueryCancel ); + // @xref + GUI_BGN_SAVE; + BOOL bOk = pPP->CPropertyPage::OnQueryCancel(); + GUI_END_SAVE; + return PyInt_FromLong((long)bOk); +} +// @pymethod |PyCPropertyPage|OnWizardBack|Calls the default MFC OnWizardBack handler. +PyObject *ui_proppage_on_wizard_back( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args,OnWizardBack ); + // @xref + GUI_BGN_SAVE; + LRESULT result = pPP->CPropertyPage::OnWizardBack(); + GUI_END_SAVE; + return PyInt_FromLong(result); +} +// @pymethod |PyCPropertyPage|OnWizardNext|Calls the default MFC OnWizardNext handler. +PyObject *ui_proppage_on_wizard_next( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args,OnWizardNext ); + // @xref + GUI_BGN_SAVE; + LRESULT result = pPP->CPropertyPage::OnWizardNext(); + GUI_END_SAVE; + return PyInt_FromLong(result); +} +// @pymethod |PyCPropertyPage|OnWizardFinish|Calls the default MFC OnWizardFinish handler. +PyObject *ui_proppage_on_wizard_finish( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args,OnWizardFinish ); + // @xref + GUI_BGN_SAVE; + BOOL bOk = pPP->CPropertyPage::OnWizardFinish(); + GUI_END_SAVE; + return PyInt_FromLong((long)bOk); +} +// @pymethod |PyCPropertyPage|OnCancel|Calls the default MFC OnCancel handler. +PyObject *ui_proppage_on_cancel( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnCancel ); + // @xref + GUI_BGN_SAVE; + pPP->CPropertyPage::OnCancel(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCPropertyPage|OnSetActive|Calls the default MFC OnSetActive handler. +// @xref +PyObject *ui_proppage_on_set_active( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnSetActive ); + // @xref + GUI_BGN_SAVE; + long rc = pPP->CPropertyPage::OnSetActive(); + GUI_END_SAVE; + return PyInt_FromLong(rc); + // @rdesc The result is true if the page should be made active. + // Typically this result should be passed to the original OnSetActive handler. +} +// @pymethod int|PyCPropertyPage|OnKillActive|Calls the default MFC OnKillActive handler. +// @xref +PyObject *ui_proppage_on_kill_active( PyObject *self, PyObject *args ) +{ + CPythonPropertyPage *pPP; + if (!(pPP=GetPropPage(self))) + return NULL; + CHECK_NO_ARGS2(args, OnKillActive ); + // @xref + GUI_BGN_SAVE; + long rc = pPP->CPropertyPage::OnKillActive(); + GUI_END_SAVE; + return PyInt_FromLong(rc); + // @rdesc The result is true if the page should be deselected. + // Typically this result should be passed to the original OnSetActive handler. +} + + +// @object PyCPropertyPage|A class which encapsulates an MFC CPropertyPage object. Derived from a object. +static struct PyMethodDef ui_proppage_methods[] = { + {"CancelToClose", ui_proppage_cancel_to_close, 1}, // @pymeth CancelToClose|Changes the Cancel button to Close. + {"OnCancel", ui_proppage_on_cancel, 1}, // @pymeth OnCancel|Calls the default MFC OnCancel handler. + {"OnOK", ui_proppage_on_ok, 1}, // @pymeth OnOK|Calls the default MFC OnOK handler. + {"OnApply", ui_proppage_on_apply, 1}, // @pymeth OnApply|Calls the default MFC OnApply handler. + {"OnReset", ui_proppage_on_reset, 1}, // @pymeth OnReset|Calls the default MFC OnReset handler. + {"OnQueryCancel", ui_proppage_on_query_cancel, 1}, // @pymeth OnQueryCancel|Calls the default MFC OnQueryCancel handler. + {"OnWizardBack", ui_proppage_on_wizard_back, 1}, // @pymeth OnWizardBack|Calls the default MFC OnWizardBack handler. + {"OnWizardNext", ui_proppage_on_wizard_next, 1}, // @pymeth OnWizardNext|Calls the default MFC OnWizardNext handler. + {"OnWizardFinish", ui_proppage_on_wizard_finish, 1}, // @pymeth OnWizardFinish|Calls the default MFC OnWizardFinish handler. + {"OnSetActive", ui_proppage_on_set_active, 1}, // @pymeth OnSetActive|Calls the default MFC OnSetActive handler. + {"OnKillActive", ui_proppage_on_kill_active, 1}, // @pymeth OnKillActive|Calls the default MFC OnKillActive handler. + {"SetModified", ui_proppage_set_modified, 1}, // @pymeth SetModified|Sets the modified flag (for the Apply button). + {"SetPSPBit", ui_proppage_set_pspbit, 1}, // @pymeth SetPSPBit|Sets (or clears) a bit in m_psp.dwFlags. + {NULL, NULL} +}; +// derived from dialog. +ui_type_CObject PyCPropertyPage::type("PyCPropertyPage", + &PyCDialog::type, + RUNTIME_CLASS(CPropertyPage), + sizeof(PyCPropertyPage), + ui_proppage_methods, + GET_PY_CTOR(PyCPropertyPage)); + +///////////////////////////////////////////////////////////////////// +// +// Tab Control Object +// +// inherited from window +// +/////////////////////////////////////// +ui_tabctrl_object::ui_tabctrl_object() +{ +} + +ui_tabctrl_object::~ui_tabctrl_object() +{ +} + +// @pymethod int|PyCTabCtrl|SetCurSel|Sets the current selection of a tab control. +PyObject *ui_tabctrl_set_cur_sel( PyObject *self, PyObject *args ) +{ + CTabCtrl *pTab; + if (!(pTab=PyGetTabCtrlWithWnd(self))) + return NULL; + int tab; + // @pyparm int|index||The index of the tab to set current. + if (!PyArg_ParseTuple( args, "i", &tab)) + return NULL; + int rc = pTab->SetCurSel(tab); + if (rc==-1) + RETURN_ERR("SetCurSel failed"); + return Py_BuildValue("i", rc); + // @rdesc The zero-based index of the previously selected item. +} +// @pymethod int|PyCTabCtrl|GetCurSel|Gets the current selection of a tab control. +PyObject *ui_tabctrl_get_cur_sel( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CTabCtrl *pTab; + if ((pTab=PyGetTabCtrl(self))==NULL) + return NULL; + GUI_BGN_SAVE; + int rc = pTab->GetCurSel(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); + // @rdesc The zero-based index of the currently selected item, or -1 if no selection. + +} +// @pymethod int|PyCTabCtrl|GetItemCountl|Returns the number of tabs in the control. +PyObject *ui_tabctrl_get_item_count( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS(args); + CTabCtrl *pTab; + if ((pTab=PyGetTabCtrl(self))==NULL) + return NULL; + GUI_BGN_SAVE; + int rc = pTab->GetItemCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @object PyCTabCtrl|A class which encapsulates an MFC CTabCtrl object. Derived from a object. +static struct PyMethodDef ui_tabctrl_methods[] = { +/* {"AddTab", ui_tabctrl_add_tab, 1}, + {"NextTab", ui_tabctrl_next_tab, 1}, + {"RemoveTab", ui_tabctrl_remove_tab, 1},*/ + {"GetCurSel", ui_tabctrl_get_cur_sel, 1}, // @pymeth GetCurSel|Gets the current selection of a tab control. + {"GetItemCount", ui_tabctrl_get_item_count, 1}, // @pymeth GetItemCountl|Returns the number of tabs in the control. + {"SetCurSel", ui_tabctrl_set_cur_sel, 1}, // @pymeth SetCurSel|Sets the current selection of a tab control. + {NULL, NULL} +}; + +ui_type_CObject ui_tabctrl_object::type("PyCTabCtrl", + &PyCWnd::type, + RUNTIME_CLASS(CTabCtrl), + sizeof(ui_tabctrl_object), + ui_tabctrl_methods, + GET_PY_CTOR(ui_tabctrl_object)); diff --git a/Pythonwin/win32prop.h b/Pythonwin/win32prop.h new file mode 100644 index 0000000000..147a85a678 --- /dev/null +++ b/Pythonwin/win32prop.h @@ -0,0 +1,43 @@ +// property page/sheet objects + +// property sheets are a "clone" +// hack the associations a bit. When the dialog window closes, I dont +// destroy the association. + +///////////////////////////////////////////////////////// +// +// +class PyCPropertySheet : public PyCWnd { +public: + static PyObject *create( PyObject *self, PyObject *args ); + static ui_type_CObject type; + MAKE_PY_CTOR(PyCPropertySheet) +protected: + PyCPropertySheet(); + virtual ~PyCPropertySheet(); + +// virtual void DoKillAssoc( BOOL bDestructing=FALSE); +// virtual void SetAssocInvalid() {return; }//ignore +}; + +class PyCPropertyPage : public PyCDialog { +public: + static PyObject *create( PyObject *self, PyObject *args ); + static PyObject *createIndirect( PyObject *self, PyObject *args ); + static ui_type_CObject type; + MAKE_PY_CTOR(PyCPropertyPage) +protected: + PyCPropertyPage(); + virtual ~PyCPropertyPage(); +}; + +class ui_tabctrl_object : public PyCWnd { +public: + static PyObject *create( PyObject *self, PyObject *args ); +protected: + ui_tabctrl_object(); + virtual ~ui_tabctrl_object(); +public: + static ui_type_CObject type; + MAKE_PY_CTOR(ui_tabctrl_object) +}; diff --git a/Pythonwin/win32rgn.cpp b/Pythonwin/win32rgn.cpp new file mode 100644 index 0000000000..d573f4ec58 --- /dev/null +++ b/Pythonwin/win32rgn.cpp @@ -0,0 +1,165 @@ +// Contributed by: +// kk@epsilon.com.gr +// +// +// 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 + +// Purpose: It exports to Python the MFC GDI class CRgn. + +#include "stdafx.h" +#include "win32gdi.h" +#include "win32rgn.h" + +// this returns a pointer that should not be stored. +// Helper function that returns a CRgn object given a Python object +// Return Values: a CRgn object +CRgn *PyCRgn::GetRgn(PyObject *self) +{ + return (CRgn *)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|CreateRgn|Creates a new rgn object. +// Return Values: a PyCRgn object +PyObject * +PyCRgn::create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CRgn *pRgn = new CRgn; + return ui_assoc_object::make (PyCRgn::type, pRgn); +} + +// @pymethod int|PyCRgn|CreateRectRgn|Initializes a region to a rectangle +// Return Values: success or failure flag (BOOL) +PyObject * +PyCRgn::create_rect_rgn(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + + int x1,y1,x2,y2; + if (!PyArg_ParseTuple(args,"(iiii):CreateRectRgn", + &x1,&y1, &x2,&y2)) + return NULL; + + BOOL ok=pRgn->CreateRectRgn(x1,y1,x2,y2); + + return Py_BuildValue("i",ok); + } + +// @pymethod int|PyCRgn|CombineRgn|Creates a new GDI region by combining two existing regions. The regions are combined as specified by nCombineMode +// Return Values: success or failure flag (BOOL) +PyObject * +PyCRgn::combine_rgn(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + + PyObject *objRgn1 = Py_None; + PyObject *objRgn2 = Py_None; + int nCombineMode=RGN_AND; + + if (!PyArg_ParseTuple(args,"OOi:CombineRgn", + &objRgn1,&objRgn2,&nCombineMode)) + return NULL; + + CRgn *pRgn1 = PyCRgn::GetRgn(objRgn1); + if (!pRgn1) return NULL; + CRgn *pRgn2 = PyCRgn::GetRgn(objRgn2); + if (!pRgn2) return NULL; + + int result=pRgn->CombineRgn(pRgn1,pRgn2,nCombineMode); + + return Py_BuildValue("i",result); + } + +// @pymethod int|PyCRgn|CopyRgn|Copies the region defined by pRgnSrc into the CRgn object +// Return Values: success or failure flag (BOOL) +PyObject * +PyCRgn::copy_rgn(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + + PyObject *objRgnSrc = Py_None; + if (!PyArg_ParseTuple(args,"O:CopyRgn",&objRgnSrc)) + return NULL; + + CRgn *pRgnSrc = PyCRgn::GetRgn(objRgnSrc); + if (!pRgnSrc) return NULL; + + int result=pRgn->CopyRgn(pRgnSrc); + + return Py_BuildValue("i",result); + } + +// @pymethod int|PyCRgn|GetRgnBox|Retrieves the coordinates of the bounding rectangle of the CRgn object +// Return Values: the bounding rectangle as a tuple (l,t,r,b) +PyObject * +PyCRgn::get_rgn_box(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + + CHECK_NO_ARGS2(args,GetRgnBox); + + RECT rect = {0,0,0,0}; + int result=pRgn->GetRgnBox(&rect); + + return Py_BuildValue("i(iiii)", result, rect.left, rect.top, rect.right, rect.bottom); + } + +// @pymethod int|PyCRgn|DeleteObject|Deletes the attached Windows GDI Rgn object from memory by freeing all system storage associated with the Windows GDI object +// Return Values: None +PyObject * +PyCRgn::delete_object(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + + CHECK_NO_ARGS2(args,DeleteObject); + + BOOL ok=TRUE; + if(pRgn->GetSafeHandle()) + ok=pRgn->DeleteObject(); + if(!ok) + RETURN_ERR("DeleteObject failed"); + pRgn->m_hObject=0; // assert + + RETURN_NONE; + } + +// @pymethod int|PyCRgn|GetSafeHandle|A HANDLE to the attached Windows GDI object; otherwise NULL if no object is attached +// Return Values: the handle of the CRgn object +PyObject * +PyCRgn::get_safe_handle(PyObject *self, PyObject *args) + { + CRgn *pRgn = PyCRgn::GetRgn(self); + if (!pRgn) return NULL; + CHECK_NO_ARGS2(args,GetSafeHandle); + HGDIOBJ hgdiobj=pRgn->GetSafeHandle(); + return Py_BuildValue("l",hgdiobj); + } + + +// @object PyCRgn|An object encapsulating an MFC PyCRgn class. +static struct PyMethodDef PyCRgn_methods[] = { + {"CreateRectRgn",PyCRgn::create_rect_rgn,1}, + {"CombineRgn",PyCRgn::combine_rgn,1}, + {"CopyRgn",PyCRgn::copy_rgn,1}, + {"GetRgnBox",PyCRgn::get_rgn_box,1}, + {"DeleteObject",PyCRgn::delete_object,1}, + {"GetSafeHandle",PyCRgn::get_safe_handle,1}, + {NULL,NULL} +}; + +ui_type_CObject PyCRgn::type ("PyCRgn", + &PyCGdiObject::type, + RUNTIME_CLASS(CRgn), + sizeof(PyCRgn), + PyCRgn_methods, + GET_PY_CTOR(PyCRgn)); diff --git a/Pythonwin/win32rgn.h b/Pythonwin/win32rgn.h new file mode 100644 index 0000000000..f132ed5876 --- /dev/null +++ b/Pythonwin/win32rgn.h @@ -0,0 +1,25 @@ +#ifndef INC_PYCRGN +#define INC_PYCRGN + +#include "win32gdi.h" + +class PyCRgn : public PyCGdiObject + { + public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCRgn) + CRgn *GetRgn() {return GetRgn(this);} + static CRgn *GetRgn(PyObject *self); + + static PyObject *create(PyObject *self, PyObject *args); + + static PyObject *create_rect_rgn(PyObject *self, PyObject *args); + static PyObject *combine_rgn(PyObject *self, PyObject *args); + static PyObject *copy_rgn(PyObject *self, PyObject *args); + static PyObject *get_rgn_box(PyObject *self, PyObject *args); + static PyObject *delete_object(PyObject *self, PyObject *args); + static PyObject *get_safe_handle(PyObject *self, PyObject *args); + + }; + +#endif diff --git a/Pythonwin/win32splitter.cpp b/Pythonwin/win32splitter.cpp new file mode 100644 index 0000000000..6423668f2b --- /dev/null +++ b/Pythonwin/win32splitter.cpp @@ -0,0 +1,303 @@ +/* win32splitter : implementation file + + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32control.h" +#include "win32splitter.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////// +// +// splitter object +// + +PyCSplitterWnd::PyCSplitterWnd() +{ +} +PyCSplitterWnd::~PyCSplitterWnd() +{ +} + +// @pymethod |win32ui|CreateSplitter|Creates a splitter window object. +PyObject * +PyCSplitterWnd::create( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS2(args,CreateSplitter); + return ui_assoc_object::make(PyCSplitterWnd::type, new CPythonSplitter); +} + +// identical to ui_window, except no ASSERT_VALID checking if not initialised. +/*static*/ CPythonSplitter *PyCSplitterWnd::GetSplitterObject(PyObject *self) +{ + return (CPythonSplitter *)GetGoodCppObject( self, &type ); +} + +///////////////////////////////////////////////////////////////////// +// +// splitter methods +// +CWnd *GetWnd(PyObject *self) +{ + return (CWnd *)PyCWnd::GetPythonGenericWnd(self); +} +CView *GetView(PyObject *self) +{ + return (CView *)PyCWnd::GetPythonGenericWnd(self); +} + +// @pymethod |PyCSplitterWnd|CreateStatic|Creates a static splitter window. +// @comm A static splitter window is a splitter where the number of panes are +// fixed at window creation time. Currently this is the only splitter window +// supported by win32ui. +PyObject *ui_splitter_create_static( PyObject *self, PyObject *args ) +{ + CPythonSplitter *wnd = PyCSplitterWnd::GetSplitterObject(self); + if (!wnd) + return NULL; + int rows, cols; + DWORD dwStyle = WS_CHILD | WS_VISIBLE; + UINT nID = AFX_IDW_PANE_FIRST; + PyObject *ob; + if (!PyArg_ParseTuple(args,"Oii|ii", + &ob, // @pyparm or |parent||The parent window. + &rows, // @pyparm int|rows||The number of rows in the splitter. + &cols, // @pyparm int|cols||The number of columns in the splitter. + &dwStyle,// @pyparm int|style|WS_CHILD \| WS_VISIBLE|Specifies the window style + &nID)) // @pyparm int|id|AFX_IDW_PANE_FIRST|The child window ID of the window. The ID can be AFX_IDW_PANE_FIRST unless the splitter window is nested inside another splitter window. + return NULL; + if (!(ui_base_class::is_uiobject(ob, &PyCFrameWnd::type) || + ui_base_class::is_uiobject(ob, &PyCSplitterWnd::type))) + RETURN_TYPE_ERR("First argument must be a PyFrameWnd or PyCSplitter."); + + // these will cause assert failures in MFC. + if (!(rows >= 1 && rows <= 16) || !(cols >= 1 && cols <= 16) || !(cols > 1 || rows > 1)) + RETURN_TYPE_ERR("Row and column argument out of range"); + + CWnd *pParent = GetWnd( ob ); + if (pParent==NULL) + return NULL; + int rc; + GUI_BGN_SAVE; + rc=wnd->CreateStatic(pParent, rows, cols, dwStyle, nID); // @pyseemfc CSplitterWnd|CreateStatic + GUI_END_SAVE; + if (!rc) + RETURN_ERR("CSplitterWnd::CreateStatic failed"); + RETURN_NONE; +} + +// @pymethod |PyCSplitterWnd|CreateView|Creates a view in a splitter window +PyObject *ui_splitter_create_view( PyObject *self, PyObject *args ) +{ + CPythonSplitter *wnd = PyCSplitterWnd::GetSplitterObject(self); + if (!wnd) + return NULL; + int row, col, width, height; + PyObject *ob; + if (!PyArg_ParseTuple(args,"Oii(ii)", + &ob, // @pyparm |view||The view to place in the splitter pane. + &row, // @pyparm int|row||The row in the splitter to place the view. + &col, // @pyparm int|col||The column in the splitter to place the view. + &width, // @pyparm (int, int)|width, height||The initial size of the new view. + &height)) + return NULL; + if (!ui_base_class::is_uiobject(ob, &PyCView::type)) + RETURN_TYPE_ERR("Argument must be a PyCView or child"); + CView *pView = GetView( ob ); + if (pView==NULL) + return NULL; + CCreateContext context; + context.m_pLastView = pView; + context.m_pCurrentDoc = pView->GetDocument(); + if (!context.m_pCurrentDoc) + RETURN_ERR("There is no document attached to the view"); + + extern void PyWin_SetViewDocument(CView *pView, CDocument *pDoc); + PyWin_SetViewDocument(pView, NULL); + + int rc; + GUI_BGN_SAVE; + rc = wnd->CreateView(row, col, NULL, CSize(width, height), &context); // @pyseemfc CSplitterWnd|CreateView + GUI_END_SAVE; + if (!rc) + return NULL; // except set. + RETURN_NONE; +} + +// @pymethod |PyCSplitterWnd|GetPane|Returns the associated with the specified pane. +// @comm Theoretically the return value can be a object, but currently it +// will always be a or derived object. +PyObject *ui_splitter_get_pane( PyObject *self, PyObject *args ) +{ + CPythonSplitter *wnd = PyCSplitterWnd::GetSplitterObject(self); + if (!wnd) + return NULL; + int row, col; + if (!PyArg_ParseTuple(args,"ii", + &row, // @pyparm int|row||The row in the splitter. + &col)) // @pyparm int|col||The column in the splitter. + return NULL; + CWnd *pWnd; + GUI_BGN_SAVE; + pWnd=wnd->GetPane(row, col); + GUI_END_SAVE; + if (!pWnd) + return NULL; + return ui_assoc_object::make(UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); +} + +// @pymethod |PyCSplitterWnd|SetRowInfo|Sets a new minimum height and ideal height for a row. +PyObject *ui_splitter_set_row_info( PyObject *self, PyObject *args ) +{ + CPythonSplitter *wnd = PyCSplitterWnd::GetSplitterObject(self); + if (!wnd) + return NULL; + int row, min, ideal; + if (!PyArg_ParseTuple(args,"iii:SetRowInfo", + &row, // @pyparm int|row||The row in the splitter. + &ideal, // @pyparm int|ideal||Specifies an ideal height for the splitter window row in pixels. + &min)) // @pyparm int|min||Specifies a minimum height for the splitter window row in pixels. + return NULL; + + GUI_BGN_SAVE; + wnd->SetRowInfo(row, ideal, min); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCSplitterWnd|SetColumnInfo|Sets a new minimum height and ideal height for a column +PyObject *ui_splitter_set_column_info( PyObject *self, PyObject *args ) +{ + CPythonSplitter *wnd = PyCSplitterWnd::GetSplitterObject(self); + if (!wnd) + return NULL; + int row, min, ideal; + if (!PyArg_ParseTuple(args,"iii:SetColumnInfo", + &row, // @pyparm int|column||The column in the splitter. + &ideal, // @pyparm int|ideal||Specifies an ideal height for the splitter window column in pixels. + &min)) // @pyparm int|min||Specifies a minimum height for the splitter window column in pixels. + return NULL; + + GUI_BGN_SAVE; + wnd->SetColumnInfo(row, ideal, min); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCSplitterWnd|A class which encapsulates an MFC . Derived from a object. +static struct PyMethodDef ui_splitter_window_methods[] = { + {"GetPane", ui_splitter_get_pane, 1}, // @pymeth GetPane|Returns the object associated with a splitter window pane. + {"CreatePane", ui_splitter_create_view, 1}, + {"CreateView", ui_splitter_create_view, 1}, // @pymeth CreateView|Creates a view in a splitter window + {"CreateStatic", ui_splitter_create_static, 1}, // @pymeth CreateStatic|Creates a static splitter window. + {"SetColumnInfo", ui_splitter_set_column_info,1}, // @pymeth SetColumnInfo|Sets a new minimum height and ideal height for a column + {"SetRowInfo", ui_splitter_set_row_info, 1}, // @pymeth SetRowInfo|Sets a new minimum height and ideal height for a row. + {NULL, NULL} +}; + +ui_type_CObject PyCSplitterWnd::type("PyCSplitterWnd", + &PyCWnd::type, + RUNTIME_CLASS(CSplitterWnd), + sizeof(PyCSplitterWnd), + ui_splitter_window_methods, + GET_PY_CTOR(PyCSplitterWnd)); + +///////////////////////////////////////////////////////////////////// +// +// Python splitter +// +IMPLEMENT_DYNAMIC( CPythonSplitter, CSplitterWnd ); +CPythonSplitter::CPythonSplitter() +{ + bHaveAllChildren = FALSE; +} +BOOL CPythonSplitter::CreateView(int row, int col, + CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext) +{ + // NOTE NOTE NOTE + // This is basically cloned from MFC CSplitterWnd::CreateView (winsplit.cpp) + ASSERT_VALID(this); + ASSERT(row >= 0 && row < m_nRows); + ASSERT(col >= 0 && col < m_nCols); +// ASSERT(pViewClass != NULL); + if (GetDlgItem(IdFromRowCol(row, col)) != NULL) { + PyErr_SetString(ui_module_error, "CreateView - pane already exists"); + return FALSE; + } + // set the initial size for that pane + m_pColInfo[col].nIdealSize = sizeInit.cx; + m_pRowInfo[row].nIdealSize = sizeInit.cy; + + if (pContext==NULL || pContext->m_pLastView==NULL) { + PyErr_SetString(ui_module_error, "CreateView - Internal error - no valid context"); + return FALSE; + } + CWnd *pWnd = pContext->m_pLastView; + ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CWnd))); + ASSERT(pWnd->m_hWnd == NULL); // not yet created + + DWORD dwStyle = AFX_WS_DEFAULT_VIEW; +// if (afxData.bWin4) + dwStyle &= ~WS_BORDER; + + // Create with the right size (wrong position) + CRect rect(CPoint(0,0), sizeInit); + BOOL ok; + ok = pWnd->Create(NULL, NULL, dwStyle, + rect, this, IdFromRowCol(row, col), pContext); + if (!ok) + { + PyErr_SetString(ui_module_error, "CreateView: couldn't create client pane for splitter"); + // pWnd will be cleaned up by PostNcDestroy + return FALSE; + } +// ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col)); + + // send initial notification message +// if (bSendInitialUpdate) +// pWnd->SendMessage(WM_INITIALUPDATE); + + return TRUE; +} +void CPythonSplitter::PostNcDestroy() +{ + CSplitterWnd::PostNcDestroy(); + delete this; // clean up the pointer I created +} + +BEGIN_MESSAGE_MAP(CPythonSplitter, CSplitterWnd) + //{{AFX_MSG_MAP(CPythonSplitter) + ON_WM_SIZE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CPythonSplitter::OnSize( UINT nType, int cx, int cy ) +{ + // MFC will die, and this is not good for Python! + if (!bHaveAllChildren) { + // check them + BOOL bFailed = FALSE; + for (int col = 0; !bFailed && col < m_nCols; col++) + for (int row = 0; !bFailed && row < m_nRows; row++) + bFailed = (GetDlgItem(IdFromRowCol(row, col))==NULL); + bHaveAllChildren = !bFailed; + } + if (bHaveAllChildren) + CSplitterWnd::OnSize(nType, cx, cy); + else + OutputDebugString("Warning - Ignoring OnSize for splitter, due to missing children\n"); +} \ No newline at end of file diff --git a/Pythonwin/win32splitter.h b/Pythonwin/win32splitter.h new file mode 100644 index 0000000000..bfae906a38 --- /dev/null +++ b/Pythonwin/win32splitter.h @@ -0,0 +1,42 @@ +// win32splitter.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CPythonSplitter frame + +class CPythonSplitter : public CPythonWndFramework +{ + DECLARE_DYNAMIC( CPythonSplitter ); +public: + CPythonSplitter(); + virtual BOOL CPythonSplitter::CreateView(int row, int col, CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext); +protected: + BOOL bHaveAllChildren; + virtual void PostNcDestroy(); + // Generated message map functions + //{{AFX_MSG(CPythonSplitter) + afx_msg void OnSize( UINT nType, int cx, int cy ); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void AssertValid() const { // MFCs version wont allow us to call it before created, and our framework want's to! + CWnd::AssertValid(); + } +}; + +///////////////////////////////////////////////////////// +// +// splitter_window +class PyCSplitterWnd : public PyCWnd { +public: + static PyObject *create(PyObject *self, PyObject *args); + static CPythonSplitter *GetSplitterObject(PyObject *self); +protected: + PyCSplitterWnd(); + virtual ~PyCSplitterWnd(); +public: + static ui_type_CObject type; + MAKE_PY_CTOR(PyCSplitterWnd) +}; + +///////////////////////////////////////////////////////////////////////////// diff --git a/Pythonwin/win32template.cpp b/Pythonwin/win32template.cpp new file mode 100644 index 0000000000..506d60492c --- /dev/null +++ b/Pythonwin/win32template.cpp @@ -0,0 +1,542 @@ +/* + + win32 template data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32template.h" +#include "win32doc.h" +#include "win32menu.h" + +extern CFrameWnd *GetFramePtr(PyObject *self); + +class CProtectedDocument : public CDocument +{ +public: + void SetPathName( const char *pathName ) {m_strPathName = pathName;} +}; + +///////////////////////////////////////////////////////////////////// +// +// Document object +// +////////////////////////////////////////////////////////////////////// +PyCDocTemplate::PyCDocTemplate() +{ +} + +PyCDocTemplate::~PyCDocTemplate() +{ + CPythonDocTemplate *pTemp = GetTemplate(this); + if (pTemp==NULL) + return; // no more to do. + RemoveDocTemplateFromApp(pTemp); + delete pTemp; + SetAssocInvalid(); +} + +void PyCDocTemplate::cleanup() +{ + PyCCmdTarget::cleanup(); + CPythonDocTemplate *pTempl = GetTemplate(this); + if (pTempl==NULL) + OutputDebugString("PyCDocTemplate::cleanup could not cleanup template!\n"); + else { + RemoveDocTemplateFromApp( pTempl ); + delete pTempl; + } +} + +BOOL PyCDocTemplate::RemoveDocTemplateFromApp( CDocTemplate *pTemplate ) +{ + // (Must keep templates in the same order (I think!) + // Loop over each item, putting it at the end of the list. When I get back to the first one, I know I am finished. + + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + CProtectedDocManager *pDocMgr = pApp->GetDocManager(); + if (pDocMgr==NULL) + return FALSE; + CPtrList &templateList = pDocMgr->GetTemplateList(); + ASSERT_VALID(pTemplate); + if (templateList.IsEmpty()) return FALSE; + CDocTemplate *headItem = (CDocTemplate *)templateList.RemoveHead(); + CDocTemplate *item; + if (headItem==pTemplate) return TRUE; + BOOL ret = FALSE; + templateList.AddTail(headItem); + while (templateList.GetHead()!=headItem) { + item = (CDocTemplate *)templateList.RemoveHead(); + if (item==pTemplate) + ret = TRUE; + else + templateList.AddTail(item); + } + return ret; +} + +CPythonDocTemplate *PyCDocTemplate::GetTemplate(PyObject *self) +{ + return (CPythonDocTemplate*)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|CreateDocTemplate|Creates a document template object. +PyObject * +PyCDocTemplate::create(PyObject *self, PyObject *args) +{ + UINT idResource; + // @pyparm int|idRes||The ID for resources for documents of this type. + if (!PyArg_ParseTuple(args,"i:CreateDocTemplate", &idResource)) + return NULL; + + CPythonDocTemplate *pMFCTemplate = new CPythonDocTemplate(idResource); + return ui_assoc_object::make(PyCDocTemplate::type, pMFCTemplate); +} + +PyObject * +PyCDocTemplate::DoCreateDocHelper(PyObject *self, PyObject *args, CRuntimeClass *pClass, ui_type_CObject &pydoc_type) +{ + char *fileName = NULL; // default, untitled document + if (!PyArg_ParseTuple(args,"|z", &fileName)) + return NULL; + CDocument *pDoc = NULL; + if (fileName) { + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + // need to look for an open doc of same name, and return that object. + // Let MFC framework search for a filename for us. + pDoc=pApp->FindOpenDocument(fileName); + } + // no name given, or no open document of that name + if (pDoc==NULL) { + CPythonDocTemplate *pMFCTemplate = GetTemplate(self); + if (pMFCTemplate==NULL) + return NULL; + CObject *pOb; + GUI_BGN_SAVE; + pOb = pClass->CreateObject(); + GUI_END_SAVE; + if (pOb==NULL) + RETURN_MEM_ERR("error creating document object"); + if (!pOb->IsKindOf( RUNTIME_CLASS(CDocument))) + RETURN_ERR("Internal error: Unknown created instead of a document"); + pDoc = (CDocument *)pOb; + pMFCTemplate->AddDocument(pDoc); + ASSERT_VALID(pDoc); + ASSERT(pDoc->IsKindOf(RUNTIME_CLASS(CDocument))); + if (fileName) + ((CProtectedDocument *)pDoc)->SetPathName(fileName); +// else { +// CString strDocName; +// VERIFY(strDocName.LoadString(AFX_IDS_UNTITLED)); +// pDoc->SetTitle(strDocName); +// } + } + return ui_assoc_object::make(pydoc_type, pDoc); +} + +// @pymethod |PyCDocTemplate|DoCreateDoc|Creates an underlying document object. +PyObject * +PyCDocTemplate::DoCreateDoc(PyObject *self, PyObject *args) +{ + // @pyparm string|fileName|None|The name of the file to load. + return DoCreateDocHelper(self, args, RUNTIME_CLASS(CPythonDoc), PyCDocument::type); +} + + +// CreateNewFrame should no longer be used. +PyObject * +PyCDocTemplate::CreateNewFrame(PyObject *self, PyObject *args) +{ + PyObject *obDoc; + PyObject *obWndOther; + + if (!PyArg_ParseTuple(args,"|OO:CreateNewFrame", + &obDoc, // |doc|None|A document for the frame. + &obWndOther))// |wndOther|None|A window to base the new one on. + return NULL; + // If the doc parameter is None, a new document will be created. + // Otherwise, the new view will be associated with the existing document specified by pDoc. + // The wndOther parameter is used to implement the Window New command. + // It provides a frame window on which to model the new frame window. + // The new frame window is usually created invisible. + CDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + GUI_BGN_SAVE; + CFrameWnd *pFrame = new CPythonMDIChildWnd(); + GUI_END_SAVE; + return ui_assoc_object::make(PyCMDIChildWnd::type, pFrame ); +} + +static BOOL AFXAPI PyAfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2) +{ +#ifndef _MAC + // it is necessary to convert the paths first + TCHAR szTemp1[_MAX_PATH]; + AfxFullPath(szTemp1, lpszPath1); + TCHAR szTemp2[_MAX_PATH]; + AfxFullPath(szTemp2, lpszPath2); + return lstrcmpi(szTemp1, szTemp2) == 0; +#else + FSSpec fssTemp1; + FSSpec fssTemp2; + if (!UnwrapFile(lpszPath1, &fssTemp1) || !UnwrapFile(lpszPath2, &fssTemp2)) + return FALSE; + return fssTemp1.vRefNum == fssTemp2.vRefNum && + fssTemp1.parID == fssTemp2.parID && + EqualString(fssTemp1.name, fssTemp2.name, false, true); +#endif +} + +// @pymethod list|PyCDocTemplate|GetDocumentList|Return a list of all open documents. +PyObject * +PyCDocTemplate::GetDocumentList(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":GetDocumentList")) + return NULL; + CPythonDocTemplate *pTemplate = GetTemplate(self); + PyObject *newOb = PyList_New(0); + POSITION posDoc = pTemplate->GetFirstDocPosition(); + while (posDoc) { + CDocument* pDoc = pTemplate->GetNextDoc(posDoc); + PyList_Append(newOb, ui_assoc_object::make(PyCDocument::type, pDoc)->GetGoodRet()); + } + return newOb; +} + +// @pymethod |PyCDocTemplate|FindOpenDocument|Returns an existing document with the specified file name. +PyObject * +PyCDocTemplate::FindOpenDocument(PyObject *self, PyObject *args) +{ + char *fileName; + // @pyparm string|fileName||The fully qualified filename to search for. + if (!PyArg_ParseTuple(args,"s:FindOpenDocument", &fileName)) + return NULL; + CPythonDocTemplate *pTemplate = GetTemplate(self); + POSITION posDoc = pTemplate->GetFirstDocPosition(); + while (posDoc) { + CDocument* pDoc = pTemplate->GetNextDoc(posDoc); + if (PyAfxComparePath(pDoc->GetPathName(), fileName)) + return ui_assoc_object::make(PyCDocument::type, pDoc)->GetGoodRet(); + } + RETURN_NONE; +} +// @pymethod string|PyCDocTemplate|GetDocString|Retrieves a specific substring describing the document type. +PyObject * +PyCDocTemplate::GetDocString(PyObject *self, PyObject *args) +{ + CDocTemplate::DocStringIndex docIndex; + // @pyparm int|docIndex||The document index. Must be one of the win32ui.CDocTemplate_* constants. + if (!PyArg_ParseTuple(args, "i:GetDocString", &docIndex)) + return NULL; + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + CString csRet; + GUI_BGN_SAVE; + BOOL ok = pTempl->GetDocString(csRet, docIndex); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("PyCDocTemplate::GetDocString failed"); + // @comm For more information on the doc strings, please see + return Py_BuildValue("s", (const char *)csRet); +} + +// @pymethod |PyCDocTemplate|GetResourceID|Returns the resource ID in use. +PyObject * +PyCDocTemplate::GetResourceID(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetResourceID); + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + return Py_BuildValue("i", pTempl->GetResourceID()); +} + +// @pymethod |PyCDocTemplate|GetSharedMenu|Returns the shared menu object for all frames using this template. +PyObject * +PyCDocTemplate::GetSharedMenu(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + // @pyseemfc CWnd|m_hMenuShared + HMENU hMenu = pTempl->m_hMenuShared; + if (hMenu==NULL) + RETURN_ERR("The template has no menu"); + return ui_assoc_object::make(PyCMenu::type, hMenu)->GetGoodRet(); +} + +// @pymethod |PyCDocTemplate|InitialUpdateFrame|Calls the default OnInitialFrame handler. +PyObject * +PyCDocTemplate::InitialUpdateFrame(PyObject *self, PyObject *args) +{ + PyObject *obDoc; + PyObject *obFrame; + int bMakeVisible = TRUE; + + if (!PyArg_ParseTuple(args,"OO|i:InitialUpdateFrame", + &obFrame,// @pyparm |frame|None|The frame window. + &obDoc, // @pyparm |doc|None|A document for the frame. + &bMakeVisible))// @pyparm int|bMakeVisible|1|Indicates of the frame should be shown. + return NULL; + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + CDocument *pDoc = PyCDocument::GetDoc(obDoc); + CFrameWnd *pFrame = GetFramePtr(obFrame); + if (pFrame==NULL || pDoc==NULL) return NULL; + // @xref + GUI_BGN_SAVE; + pTempl->CMultiDocTemplate::InitialUpdateFrame(pFrame, pDoc, bMakeVisible); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCDocTemplate|OpenDocumentFile|Opens a document file, creating a view and frame. +PyObject * +PyCDocTemplate::OpenDocumentFile(PyObject *self, PyObject *args) +{ + char *fileName = NULL; + BOOL bMakeVisible = TRUE; + if (!PyArg_ParseTuple(args,"|zi:OpenDocumentFile", + &fileName, // @pyparm string|filename||Name of file to open, or None + &bMakeVisible))// @pyparm int|bMakeVisible|1|Indicates if the document should be created visible. + return NULL; + CMultiDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + GUI_BGN_SAVE; + CDocument *pDocument = pTempl->CMultiDocTemplate::OpenDocumentFile(fileName, bMakeVisible); + GUI_END_SAVE; + if (PyErr_Occurred()) + return NULL; + if (pDocument==NULL) + RETURN_NONE; + return ui_assoc_object::make(PyCDocument::type, pDocument )->GetGoodRet(); +} + +// @pymethod |PyCDocTemplate|SetDocStrings|Assigns the document strings for the template. +PyObject * +PyCDocTemplate::SetDocStrings(PyObject *self, PyObject *args) +{ + char *docStrings; + if (!PyArg_ParseTuple(args,"s:SetDocStrings", + &docStrings)) // @pyparm string|docStrings||The document strings. + return NULL; + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + pTempl->m_strDocStrings = docStrings; + // @comm The string must be a \n seperated list of docstrings. + // The elements are: + // @flagh elementName|Description + // @flag windowTitle|Title used for the window (only for SDI applications) + // @flag docName|Root for the default document name. + // @flag fileNewName|Name of the document type, as displayed in the "File/New" dialog + // @flag filterName|Description of the document type and a wildcard spec for the file open dialog. + // @flag filterExt|Extension for documents of this file type. + // @flag regFileTypeId|Internal Id of the document as registered in the registry. Used to associate the extension with the file type. + // @flag regFileTypeName|Name of the document, as stored in the reigstry. This is the name presented to the user. + RETURN_NONE; +} +// @pymethod |PyCDocTemplate|SetContainerInfo|Sets the resources to be used when an OLE 2 object is in-place activated. +PyObject * +PyCDocTemplate::SetContainerInfo(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args,"i:SetContainerInfo", + &id)) // @pyparm int|id||The resource ID. + return NULL; + CPythonDocTemplate *pTempl = GetTemplate(self); + if (pTempl==NULL) + return NULL; + GUI_BGN_SAVE; + pTempl->SetContainerInfo(id); + GUI_END_SAVE; + RETURN_NONE; +} + + +// @object PyCDocTemplate|A document template class. Encapsulates an MFC class +static struct PyMethodDef PyCDocTemplate_methods[] = { + {"CreateNewFrame", PyCDocTemplate::CreateNewFrame, 1}, + {"DoCreateDoc", PyCDocTemplate::DoCreateDoc, 1}, // @pymeth DoCreateDoc|Creates an underlying document object. + {"FindOpenDocument", PyCDocTemplate::FindOpenDocument, 1}, // @pymeth FindOpenDocument|Returns an existing document with the specified file name. + {"GetDocString", PyCDocTemplate::GetDocString, 1}, // @pymeth GetDocString|Retrieves a specific substring describing the document type. + {"GetDocumentList", PyCDocTemplate::GetDocumentList, 1}, // @pymeth GetDocumentList|Return a list of all open documents. + {"GetResourceID", PyCDocTemplate::GetResourceID, 1}, // @pymeth GetResourceID|Returns the resource ID in use. + {"GetSharedMenu", PyCDocTemplate::GetSharedMenu, 1}, // @pymeth GetSharedMenu|Returns the shared menu object for all frames using this template. + {"InitialUpdateFrame",PyCDocTemplate::InitialUpdateFrame, 1}, // @pymeth InitialUpdateFrame|Calls the default OnInitialFrame handler. + {"SetContainerInfo", PyCDocTemplate::SetContainerInfo, 1}, // @pymeth SetContainerInfo|Sets the resources to be used when an OLE 2 object is in-place activated. + {"SetDocStrings", PyCDocTemplate::SetDocStrings, 1}, // @pymeth SetDocStrings|Assigns the document strings for the template. + {"OpenDocumentFile", PyCDocTemplate::OpenDocumentFile, 1}, // @pymeth OpenDocumentFile|Opens a document file, creating a view and frame. + {NULL, NULL} +}; +ui_type_CObject PyCDocTemplate::type("PyCDocTemplate", + &PyCCmdTarget::type, + RUNTIME_CLASS(CDocTemplate), + sizeof(PyCDocTemplate), + PyCDocTemplate_methods, + GET_PY_CTOR(PyCDocTemplate) ); + +// The MFC class. +CPythonDocTemplate::CPythonDocTemplate(UINT idResource) : + CMultiDocTemplate(idResource, NULL, NULL, NULL) +{ +} + +// The MFC class. +CPythonDocTemplate::~CPythonDocTemplate() +{ + Python_delete_assoc( this ); // notify Python of my death. +} + +void CPythonDocTemplate::InitialUpdateFrame( CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible) +{ + // @pyvirtual |PyCDocTemplate|InitialUpdateFrame|Called to perform the initial frame update. + // The default behaviour is to call OnInitialUpdate on all views. + CVirtualHelper helper("InitialUpdateFrame", this); + if (!helper.HaveHandler()) { + CMultiDocTemplate::InitialUpdateFrame(pFrame, pDoc, bMakeVisible); + return; + } + PyObject *frame = (PyObject *)ui_assoc_object::make (PyCFrameWnd::type, + pFrame)->GetGoodRet(); + PyObject *doc = (PyObject *) ui_assoc_object::make (PyCDocument::type, + pDoc)->GetGoodRet(); + + // @pyparm |frame||The frame window. + // @pyparm |frame||The document attached to the frame. + // @pyparm int|bMakeVisible||Indicates if the frame should be made visible. + PyObject *arglst = Py_BuildValue("(OOi)",frame, doc, bMakeVisible); + XDODECREF(frame); + XDODECREF(doc); + helper.call_args(arglst); + return; +} + +CFrameWnd* CPythonDocTemplate::CreateNewFrame( CDocument* pDoc, CFrameWnd* pOther ) +{ + BOOL ok; + // @pyvirtual |PyCDocTemplate|CreateNewFrame|Called to create a new frame window. + CVirtualHelper helper("CreateNewFrame", this); + ok = helper.call(pDoc); + PyObject *retObject=NULL; + ok = ok && helper.retval( retObject ); + ok = ok && ui_base_class::is_uiobject( retObject, &PyCFrameWnd::type ); + if (!ok) { + CEnterLeavePython _celp; + if (PyErr_Occurred()) + gui_print_error(); + PyErr_SetString(PyExc_TypeError,"PyCTemplate::CreateNewFrame must return a PyCFrameWnd object."); + gui_print_error(); + return NULL; + } + CFrameWnd *pWnd = GetFramePtr( retObject ); + return pWnd; +} + +CDocument* CPythonDocTemplate::CreateNewDocument() +{ + // @pyvirtual |PyCDocTemplate|CreateNewDocument|Called to create a new document object. + CVirtualHelper helper("CreateNewDocument", this); + BOOL ok = helper.HaveHandler(); + if (!ok) { + PyErr_SetString(ui_module_error, "PyCTemplate::CreateNewDocument handler does not exist."); + TRACE0("CPythonDocTemplate::CreateNewDocument fails due to no handler\n"); + return NULL; + } + + ok = ok && helper.call(); + PyObject *retObject=NULL; + ok = ok && helper.retval( retObject ); + ok = ok && ui_base_class::is_uiobject( retObject, &PyCDocument::type ); + + if (!ok) { + CEnterLeavePython _celp; + if (PyErr_Occurred()) + gui_print_error(); + PyErr_SetString(PyExc_TypeError,"PyCTemplate::CreateNewDocument must return a PyCDocument object."); + TRACE0("CPythonDocTemplate::CreateNewDocument fails due to return type error\n"); + return NULL; + } + CDocument *pDoc = PyCDocument::GetDoc( retObject ); + return pDoc; +} + +CDocument* CPythonDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible /*= TRUE*/ ) +{ + // @pyvirtual |PyCDocTemplate|OpenDocumentFile|Called when a document file is to be opened. + CVirtualHelper helper("OpenDocumentFile", this); + BOOL ok = helper.HaveHandler(); + if (!ok) + return CMultiDocTemplate::OpenDocumentFile(lpszPathName, bMakeVisible); + + ok = ok && helper.call(lpszPathName, bMakeVisible); + PyObject *retObject=NULL; + ok = ok && helper.retval( retObject ); + if (retObject==Py_None) // If we failed, get out. + return NULL; + ok = ok && ui_base_class::is_uiobject( retObject, &PyCDocument::type ); + + if (!ok) { + CEnterLeavePython _celp; + if (PyErr_Occurred()) + gui_print_error(); + PyErr_SetString(PyExc_TypeError,"PyCTemplate::OpenDocumentFile must return a PyCDocument object."); + TRACE0("CPythonDocTemplate::CreateNewDocument fails due to return type error\n"); + return NULL; + } + return PyCDocument::GetDoc( retObject ); +} + +#ifndef _MAC +CDocTemplate::Confidence CPythonDocTemplate::MatchDocType(LPCTSTR lpszPathName, + CDocument*& rpDocMatch) +{ + DWORD dwFileType=0; +#else +CDocTemplate::Confidence CPythonDocTemplate::MatchDocType(LPCTSTR lpszPathName, + DWORD dwFileType, CDocument*& rpDocMatch) +{ +#endif + // @pyvirtual int\||PyCDocTemplate|MatchDocType|Queries if the template can open the specified file name. + // @comm This method should call PyCDocTemplate.FindOpenDocument to return an already open + // document if one exists, else it should return one of the win32ui.CDocTemplate_Confidence_* constants. + CVirtualHelper helper("MatchDocType", this); + if (helper.HaveHandler()) { + // @pyparm string|fileName||The name of the file to open. + // @pyparm int|fileType||Only used on the mac. + helper.call(lpszPathName, dwFileType); + PyObject *ret; + if (!helper.retval(ret)) + return CDocTemplate::noAttempt; + if (PyInt_Check(ret)) + return (CDocTemplate::Confidence)PyInt_AsLong(ret); + if (ui_base_class::is_uiobject(ret, &PyCDocument::type)) { + CDocument *pDoc = PyCDocument::GetDoc(ret); + rpDocMatch = pDoc; + return yesAlreadyOpen; + } + CEnterLeavePython _celp; + PyErr_SetString(PyExc_TypeError,"PyCTemplate::MatchDocType must return an integer or PyCDocument object."); + gui_print_error(); + return CDocTemplate::noAttempt; + } else { + return CDocTemplate::noAttempt; + } +} diff --git a/Pythonwin/win32template.h b/Pythonwin/win32template.h new file mode 100644 index 0000000000..7c2fe5644e --- /dev/null +++ b/Pythonwin/win32template.h @@ -0,0 +1,51 @@ +class CPythonDocTemplate; +// +// Document Template Object. +// + +class PYW_EXPORT PyCDocTemplate : public PyCCmdTarget { +protected: + virtual void cleanup(); + PyCDocTemplate(); + ~PyCDocTemplate(); +public: + static CPythonDocTemplate *GetTemplate(PyObject *self); + static BOOL RemoveDocTemplateFromApp( CDocTemplate *pTemplate ); + + static PyObject *create(PyObject *self, PyObject *args); + static PyObject *DoCreateDocHelper(PyObject *self, PyObject *args, CRuntimeClass *pClass, ui_type_CObject &new_type); + static PyObject *DoCreateDoc(PyObject *self, PyObject *args); + static PyObject *AddDocTemplate(PyObject *self, PyObject *args); + static PyObject *SetDocStrings(PyObject *self, PyObject *args); + static PyObject *SetContainerInfo(PyObject *self, PyObject *args); + static PyObject *CreateNewFrame(PyObject *self, PyObject *args); + static PyObject *OpenDocumentFile(PyObject *self, PyObject *args); + static PyObject *GetResourceID(PyObject *self, PyObject *args); + static PyObject *GetSharedMenu(PyObject *self, PyObject *args); + static PyObject *GetDocumentList(PyObject *self, PyObject *args); + static PyObject *FindOpenDocument(PyObject *self, PyObject *args); + static PyObject *GetDocString(PyObject *self, PyObject *args); + static PyObject *InitialUpdateFrame(PyObject *self, PyObject *args); + + static ui_type_CObject type; + MAKE_PY_CTOR(PyCDocTemplate) +}; + +// The MFC derived class. +class PYW_EXPORT CPythonDocTemplate : public CMultiDocTemplate { +friend class PyCDocTemplate; +public: + CPythonDocTemplate(UINT idResource); + virtual ~CPythonDocTemplate(); + virtual CDocument* CreateNewDocument(); + virtual CDocument* OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE ); + + virtual CFrameWnd* CreateNewFrame( CDocument* pDoc, CFrameWnd* pOther ); + virtual void InitialUpdateFrame( CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible = TRUE); +#ifndef _MAC + virtual CDocTemplate::Confidence MatchDocType(LPCTSTR lpszPathName,CDocument*& rpDocMatch); +#else + virtual CDocTemplate::Confidence MatchDocType(LPCTSTR lpszPathName,DWORD dwFileType, CDocument*& rpDocMatch); +#endif + UINT GetResourceID() {return m_nIDResource;} +}; diff --git a/Pythonwin/win32thread.cpp b/Pythonwin/win32thread.cpp new file mode 100644 index 0000000000..1e48e0d3d3 --- /dev/null +++ b/Pythonwin/win32thread.cpp @@ -0,0 +1,324 @@ +/* + + MFC Thread data type + + Created Jan 1998, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32ui.h" +#include "win32win.h" + +extern BOOL HookWindowsMessages(); +extern BOOL Win32uiPreTranslateMessage( MSG *pMsg ); + +class CPythonWinThread : public CWinThread +{ +public: + CPythonWinThread() { + obFunc = NULL; + obArgs = NULL; + } + CPythonWinThread(AFX_THREADPROC pfnThreadProc) : + CWinThread(pfnThreadProc, NULL) + { + m_pThreadParams = this; + obFunc = NULL; + obArgs = NULL; + } + + ~CPythonWinThread() { + Py_XDECREF(obFunc); + Py_XDECREF(obArgs); + } + virtual BOOL PreTranslateMessage( MSG *pMsg ) { + if (Win32uiPreTranslateMessage(pMsg)) + return TRUE; + else + return CWinThread::PreTranslateMessage(pMsg); + } + + virtual BOOL InitInstance() { + HookWindowsMessages(); + CVirtualHelper helper("InitInstance", this); + if (helper.HaveHandler() && helper.call()) { + BOOL ret; + helper.retval(ret); + return ret; + } else + return CWinThread::InitInstance(); + } + virtual int ExitInstance() { + CVirtualHelper helper("ExitInstance", this); + if (helper.HaveHandler() && helper.call()) { + int ret; + helper.retval(ret); + return ret; + } else + return CWinThread::InitInstance(); + } + PyObject *obFunc; + PyObject *obArgs; +}; + +void CProtectedWinThread::PumpIdle() +{ + long lIdleCount = 0; + while (OnIdle(lIdleCount++)) + ; + return; +} + +void CProtectedWinThread::PumpMessages() +{ + ASSERT_VALID(this); + + // for tracking the idle time state + BOOL bIdle = TRUE; + LONG lIdleCount = 0; + + // acquire and dispatch messages until a WM_QUIT message is received. + for (;;) + { + // phase1: check to see if we can do idle work + while (bIdle && + !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) + { + // call OnIdle while in bIdle state + if (!OnIdle(lIdleCount++)) + bIdle = FALSE; // assume "no idle" state + } + // phase2: pump messages while available + do + { + // pump message, but quit on WM_QUIT + if (!PumpMessage()) { +#ifdef _DEBUG + m_nDisablePumpCount--; // application must NOT die +#endif + return; + } + + // reset "no idle" state after pumping "normal" message + if (IsIdleMessage(&m_msgCur)) + { + bIdle = TRUE; + lIdleCount = 0; + } + + } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); + } + + ASSERT(FALSE); // not reachable +} + +void CProtectedWinThread::PumpWaitingMessages(UINT firstMsg, UINT lastMsg) +{ + MSG msg; + if (::PeekMessage(&msg, NULL, firstMsg, lastMsg, PM_REMOVE)) + { + ::DispatchMessage(&msg); + } +/**************88 + while(::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) + if (!PumpMessage()) { + // if got a close message, must send it back down. +#ifdef _DEBUG + m_nDisablePumpCount--; // hack! +#endif + PostQuitMessage(0); + return; + } + // let MFC do its idle processing + LONG lIdle = 0; + while ( AfxGetApp()->OnIdle(lIdle++ ) ) + ; +********/ +} +unsigned int ThreadWorkerEntryPoint( LPVOID lpvoid ) +{ + CPythonWinThread *pThis = (CPythonWinThread *)lpvoid; + CEnterLeavePython _celp; + PyObject *result = PyEval_CallObject( pThis->obFunc, pThis->obArgs ); + if (result == NULL) { + if (PyErr_Occurred() == PyExc_SystemExit) + PyErr_Clear(); + else { + fprintf(stderr, "Unhandled exception in thread:\n"); + PyErr_Print(); + } + } + else + Py_DECREF(result); + // Cleanup thread state? + return 0; +} + +PyCWinThread::PyCWinThread() +{ +} + +PyCWinThread::~PyCWinThread() +{ +} + + +CWinThread *GetCWinThreadPtr(PyObject *self) +{ + return (CWinThread *)ui_assoc_object::GetGoodCppObject( self, &PyCWinThread::type); +} +CProtectedWinThread *GetCProtectedWinThreadPtr(PyObject *self) +{ + return (CProtectedWinThread *)ui_assoc_object::GetGoodCppObject( self, &PyCWinThread::type); +} + +// @pymethod win32ui|win32ui|CreateThread|Creates a new object +PyObject *PyCWinThread::create(PyObject *self, PyObject *args) +{ + CPythonWinThread *pThread; + PyObject *obFunc, *obArgs = Py_None; + if (PyArg_ParseTuple(args, "|:CreateThread")) { + pThread = new CPythonWinThread(); + } else if (PyArg_ParseTuple(args, "O|O:CreateThread", &obFunc, &obArgs)) { + PyErr_Clear(); + if (!PyCallable_Check(obFunc)) { + PyErr_SetString(PyExc_TypeError, "First argument must be a callable object"); + return NULL; + } + pThread = new CPythonWinThread(ThreadWorkerEntryPoint); + pThread->obFunc = obFunc; + pThread->obArgs = obArgs; + Py_INCREF(obFunc); + Py_INCREF(obArgs); + } else { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "Must pass no arguments, or a function and optional arguments"); + return NULL; + } + return ui_assoc_object::make( PyCWinThread::type, pThread ); +} + + +// @pymethod |PyCWinThread|PumpIdle|Pumps all idle messages. +static PyObject * +ui_thread_pump_idle(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,PumpIdle); + CProtectedWinThread *pThread = GetCProtectedWinThreadPtr(self); + if (!pThread) return NULL; + GUI_BGN_SAVE; + pThread->PumpIdle(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWinThread|SetMainFrame|Sets the threads main frame +static PyObject * +ui_thread_set_main_frame(PyObject *self, PyObject *args) +{ + PyObject *wndObject; + if (!PyArg_ParseTuple(args, "O:SetMainFrame", + &wndObject )) // @pyparm |mainFrame||The applications main frame. + return NULL; + CWinThread *pThread = GetCWinThreadPtr(self); + if (!pThread) return NULL; + + if (wndObject==Py_None) { + // @comm You can pass None to this function to reset the main frame. + pThread->m_pMainWnd = NULL; // Should I free this? I dont think so! + } else { + CWnd *pMainWnd = GetWndPtr(wndObject); + if (!pMainWnd) return NULL; + pThread->m_pMainWnd = pMainWnd; + } + RETURN_NONE; +} + +// @pymethod |PyCWinThread|SetThreadPriority|Sets the threads priority. Returns TRUE if successful. +static PyObject * +ui_thread_set_thread_priority(PyObject *self, PyObject *args) +{ + int priority; + if (!PyArg_ParseTuple(args, "i:SetMainFrame", + &priority )) // @pyparm |priority||The threads priority. + return NULL; + CWinThread *pThread = GetCWinThreadPtr(self); + if (!pThread) return NULL; + + long rc = pThread->SetThreadPriority(priority); + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWinThread|Run|Starts the message pump. Advanced users only +static PyObject * +ui_thread_run(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "Run"); + CWinThread *pThread = GetCWinThreadPtr(self); + if (!pThread) return NULL; + GUI_BGN_SAVE; + long rc = pThread->CWinThread::Run(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod |PyCWinThread|CreateThread|Creates the actual thread behind the thread object. +static PyObject * +ui_thread_create_thread(PyObject *self, PyObject *args) +{ + DWORD createFlags = 0; + UINT stackSize = 0; + if (!PyArg_ParseTuple(args, "|li:CreateThread", &createFlags, &stackSize)) + return NULL; + CWinThread *pThread = GetCWinThreadPtr(self); + if (!pThread) return NULL; + PyEval_InitThreads(); + GUI_BGN_SAVE; + BOOL ok = pThread->CreateThread(createFlags, stackSize); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CreateThread failed"); + RETURN_NONE; +} + +// @pymethod |PyCWinThread|PumpMessages|Pumps all messages to the application until a WM_QUIT message is received. +static PyObject * +ui_thread_pump_messages(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple (args, ":PumpMessages")) + return NULL; + CProtectedWinThread *pThread = GetProtectedThread(); + if (!pThread) return NULL; + GUI_BGN_SAVE; + pThread->PumpMessages(); + GUI_END_SAVE; + RETURN_NONE; + // @comm This allows an application which is performing a long operation to dispatch paint messages during the operation. +} + + +// @object PyCWinThread|An application class. Encapsulates an MFC class +static struct PyMethodDef PyCWinThread_methods[] = { + {"CreateThread", ui_thread_create_thread, 1}, // @pymeth CreateThread|Creates the actual thread behind the thread object. + {"PumpIdle", ui_thread_pump_idle, 1}, // @pymeth PumpIdle|Pumps idle messages. + {"PumpMessages", ui_thread_pump_messages, 1}, // @pymeth PumpMessages|Pumps all messages to the application until a WM_QUIT message is received. + {"Run", ui_thread_run, 1}, // @pymeth Run|Starts the main application message pump. + {"SetMainFrame", ui_thread_set_main_frame, 1}, // @pymeth SetMainFrame|Sets the C++ applications main frame + {"SetThreadPriority", ui_thread_set_thread_priority, 1}, // @pymeth SetThreadPriority|Sets the threads priority + {NULL, NULL} +}; +ui_type_CObject PyCWinThread::type("PyCWinThread", + &PyCCmdTarget::type, + RUNTIME_CLASS(CWinThread), + sizeof(PyCWinThread), + PyCWinThread_methods, + GET_PY_CTOR(PyCWinThread) ); + diff --git a/Pythonwin/win32toolbar.cpp b/Pythonwin/win32toolbar.cpp new file mode 100644 index 0000000000..5154a7b5ac --- /dev/null +++ b/Pythonwin/win32toolbar.cpp @@ -0,0 +1,1553 @@ +/* + long awaited python toolbar class + + by Dave Brennan (brennan@hal.com) + + (Actually, is now all the win32ui supported PyCControlBar + derived classes!!) + + Portions contributed by Kleanthis Kleanthous (kk@epsilon.com.gr) + + ToolbarCtrl contributed by Scott Deerwester (scott@HK.Super.NET) + +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 "win32win.h" +#include "win32toolbar.h" + +#include "win32control.h" // For tooltips. +#include "win32gdi.h" +#include "win32bitmap.h" +#include "pythoncbar.h" + +class PyCDockContext : public ui_assoc_object{ +public: + static ui_type type; + PyCDockContext() {;} + static CDockContext *GetDockContext(PyObject *); + MAKE_PY_CTOR(PyCDockContext); + virtual PyObject *getattr(char *name); + virtual int setattr(char *name, PyObject *v); +protected: + virtual ~PyCDockContext() {;} +}; + +enum MyTypes { + MT_INT, + MT_RECT, + MT_SIZE, + MT_POINT, +}; +struct MyMemberList { + const char *name; + MyTypes type; + size_t off; +}; + +CDockContext *PyCDockContext::GetDockContext (PyObject *self) +{ + return (CDockContext *)GetGoodCppObject( self, &type); +} + +// @pymethod int|PyCDockContext|StartDrag| +PyObject *PyCDockContext_StartDrag( PyObject *self, PyObject *args ) +{ + CDockContext *pC = PyCDockContext::GetDockContext(self); + if (!pC) return NULL; + CPoint pt; + // @pyparm int, int|pt|| + if (!PyArg_ParseTuple( args, "(ii)", &pt.x, &pt.y)) + return NULL; + GUI_BGN_SAVE; + pC->StartDrag(pt); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCDockContext|EndDrag| +PyObject *PyCDockContext_EndDrag( PyObject *self, PyObject *args ) +{ + CDockContext *pC = PyCDockContext::GetDockContext(self); + if (!pC) return NULL; + CHECK_NO_ARGS2(args, "EndDrag"); + GUI_BGN_SAVE; + pC->EndDrag(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCDockContext|StartResize| +PyObject *PyCDockContext_StartResize( PyObject *self, PyObject *args ) +{ + CDockContext *pC = PyCDockContext::GetDockContext(self); + if (!pC) return NULL; + CPoint pt; + int hittest; + // @pyparm int|hittest|| + // @pyparm int, int|pt|| + if (!PyArg_ParseTuple( args, "i(ii)", &hittest, &pt.x, &pt.y)) + return NULL; + GUI_BGN_SAVE; + pC->StartResize(hittest, pt); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCDockContext|EndResize| +PyObject *PyCDockContext_EndResize( PyObject *self, PyObject *args ) +{ + CDockContext *pC = PyCDockContext::GetDockContext(self); + if (!pC) return NULL; + CHECK_NO_ARGS2(args, EndResize); + GUI_BGN_SAVE; + pC->EndResize(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCDockContext|ToggleDocking| +PyObject *PyCDockContext_ToggleDocking( PyObject *self, PyObject *args ) +{ + CDockContext *pC = PyCDockContext::GetDockContext(self); + if (!pC) return NULL; + CHECK_NO_ARGS2(args, ToggleDocking); + GUI_BGN_SAVE; + pC->ToggleDocking(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCDockContext|A class which encapsulates an MFC CDockContext object +static struct PyMethodDef +PyCDockContext_methods[] = +{ + {"EndDrag", PyCDockContext_EndDrag, 1}, // @pymeth EndDrag| + {"StartDrag", PyCDockContext_StartDrag, 1}, // @pymeth StartDrag| + {"EndResize", PyCDockContext_EndResize, 1}, // @pymeth EndResize| + {"StartResize", PyCDockContext_StartResize, 1}, // @pymeth StartResize| + {"ToggleDocking",PyCDockContext_ToggleDocking, 1}, // @pymeth ToggleDocking| + { NULL, NULL } +}; + +#define OFF(e) offsetof(CDockContext, e) +struct MyMemberList dcmembers[] = { + {"ptLast", MT_POINT, OFF(m_ptLast)}, // @prop x,y|ptLast| + {"rectLast",MT_RECT, OFF(m_rectLast)}, // @prop left, top, right, bottom|rectLast| + {"sizeLast",MT_SIZE, OFF(m_sizeLast)}, // @prop cx, cy|sizeLast| + {"bDitherLast", MT_INT, OFF(m_bDitherLast)}, // @prop int|bDitherLast| + {"rectDragHorz",MT_RECT, OFF(m_rectDragHorz)}, // @prop left, top, right, bottom|rectDragHorz| + {"rectDragVert",MT_RECT, OFF(m_rectDragVert)}, // @prop left, top, right, bottom|rectDragVert| + {"rectFrameDragHorz",MT_RECT, OFF(m_rectFrameDragHorz)}, // @prop left, top, right, bottom|rectFrameDragHorz| + {"rectFrameDragVert",MT_RECT, OFF(m_rectFrameDragVert)}, // @prop left, top, right, bottom|rectFrameDragVert| + {"dwDockStyle", MT_INT, OFF(m_dwDockStyle)}, // @prop int|dwDockStyle|allowable dock styles for bar + {"dwOverDockStyle", MT_INT, OFF(m_dwOverDockStyle)}, // @prop int|dwOverDockStyle|style of dock that rect is over + {"dwStyle", MT_INT, OFF(m_dwStyle)}, // @prop int|dwStyle|style of control bar + {"bFlip", MT_INT, OFF(m_bFlip)}, // @prop int|bFlip|if shift key is down + {"bForceFrame", MT_INT, OFF(m_bForceFrame)}, // @prop int|bForceFrame|if ctrl key is down +// CDC* m_pDC; // where to draw during drag + {"bDragging", MT_INT, OFF(m_bDragging)}, // @prop int|bDragging| + {"nHitTest", MT_INT, OFF(m_nHitTest)}, // @prop int|nHitTest| + {"uMRUDockID", MT_INT, OFF(m_uMRUDockID)}, // @prop int|uMRUDockID| + {"rectMRUDockPos",MT_RECT, OFF(m_rectMRUDockPos)}, // @prop left, top, right, bottom|rectMRUDockPos| + {"dwMRUFloatStyle", MT_INT, OFF(m_dwMRUFloatStyle)}, // @prop int|dwMRUFloatStyle| + {"ptMRUFloatPos", MT_POINT, OFF(m_ptMRUFloatPos)}, // @prop x,y|ptMRUFloatPos| + {NULL} /* Sentinel */ +}; + +PyObject *PyCDockContext::getattr(char *name) +{ + CDockContext *pC = GetDockContext(this); + if (!pC) return NULL; + + for (MyMemberList *pm=dcmembers;pm->name;pm++) { + if (strcmp(name, pm->name)==0) { + void *pv = &((BYTE *)pC)[pm->off]; + switch (pm->type) { + case MT_INT: + return PyInt_FromLong(*((int *)pv)); + break; + case MT_RECT: { + CRect *p = (CRect *)pv; + return Py_BuildValue("iiii", p->left, p->top, p->right, p->bottom); + } + case MT_POINT: { + CPoint *p = (CPoint *)pv; + return Py_BuildValue("ii", p->x, p->y); + } + case MT_SIZE: { + CSize *p = (CSize *)pv; + return Py_BuildValue("ii", p->cx, p->cy); + } + default: + Py_FatalError("Bad type for CDockContext"); + } + } + } + return ui_assoc_object::getattr(name); +} + +int PyCDockContext::setattr(char *name, PyObject *value) +{ + CDockContext *pC = GetDockContext(this); + if (!pC) return NULL; + + for (MyMemberList *pm=dcmembers;pm->name;pm++) { + if (strcmp(name, pm->name)==0) { + void *pv = &((BYTE *)pC)[pm->off]; + switch (pm->type) { + case MT_INT: + *((int *)pv) = PyInt_AsLong(value); + return 0; + case MT_RECT: { + CRect *p = (CRect *)pv; + if (!PyArg_ParseTuple(value, "iiii", &p->left, &p->top, &p->right, &p->bottom)) + return -1; + } + return 0; + case MT_POINT: { + CPoint *p = (CPoint *)pv; + if (!PyArg_ParseTuple(value, "ii", &p->x, &p->y)) + return -1; + return 0; + } + case MT_SIZE: { + CSize *p = (CSize *)pv; + if (!PyArg_ParseTuple(value, "ii", &p->cx, &p->cy)) + return -1; + } + return 0; + default: + Py_FatalError("Bad type for CDockContext"); + } + } + } + return ui_assoc_object::setattr(name, value); +} + + +ui_type PyCDockContext::type("PyCDockContext", + &ui_assoc_object::type, + sizeof(PyCDockContext), + PyCDockContext_methods, + GET_PY_CTOR(PyCDockContext)); + + +// @pymethod |win32ui|CreateControlBar|Creates a control bar object. +PyObject * +PyCControlBar::create (PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple (args,":CreateControlBar")) + return NULL; + CControlBar *cb = new CPythonControlBar(); + return ui_assoc_object::make (PyCControlBar::type, cb)->GetGoodRet(); +} + +CControlBar *PyCControlBar::GetControlBar (PyObject *self) +{ + return (CControlBar *)GetGoodCppObject( self, &type); +} + +// @pymethod int|PyCControlBar|CalcDynamicLayout|The framework calls this member function to calculate the dimensions of a dynamic toolbar. +PyObject *PyCControlBar_CalcDynamicLayout( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + int length, dwMode; + // @pyparm int|length||The requested dimension of the control bar, either horizontal or vertical, depending on dwMode. + // @pyparm int|dwMode||A combination of flags. + if (!PyArg_ParseTuple( args, "ii:CalcDynamicLayout", &length, &dwMode)) return NULL; + GUI_BGN_SAVE; + CSize sz = pCtlBar->CControlBar::CalcDynamicLayout(length, dwMode); + GUI_END_SAVE; + return Py_BuildValue("ii", sz.cx, sz.cy); +} + +// @pymethod int|PyCControlBar|CalcFixedLayout|Calculates the horizontal size of a control bar +PyObject *PyCControlBar_CalcFixedLayout( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + int stretch, horz; + // @pyparm int|bStretch||Indicates whether the bar should be stretched to the size of the frame. The bStretch parameter is nonzero when the bar is not a docking bar (not available for docking) and is 0 when it is docked or floating (available for docking). + // @pyparm int|bHorz||Indicates that the bar is horizontally or vertically oriented. + if (!PyArg_ParseTuple( args, "ii:CalcFixedLayout", &stretch, &horz)) return NULL; + GUI_BGN_SAVE; + CSize sz = pCtlBar->CControlBar::CalcFixedLayout(stretch, horz); + GUI_END_SAVE; + return Py_BuildValue("ii", sz.cx, sz.cy); +} + +// @pymethod |PyCControlBar|EraseNonClient| +PyObject *PyCControlBar_EraseNonClient( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + if (!PyArg_ParseTuple( args, ":EraseNonClient")) return NULL; + GUI_BGN_SAVE; + pCtlBar->EraseNonClient(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCControlBar|GetDockingFrame|Returns the frame window to which a control bar is docked. +PyObject *PyCControlBar_GetDockingFrame( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + if (!PyArg_ParseTuple( args, ":GetDockingFrame")) return NULL; + GUI_BGN_SAVE; + CWnd *pWnd = pCtlBar->GetDockingFrame(); + GUI_END_SAVE; + if (pWnd==NULL) + RETURN_ERR("There is no docking frame window"); + return ui_assoc_object::make(PyCFrameWnd::type, pWnd)->GetGoodRet(); +} + +// @pymethod int|PyCControlBar|GetCount|Returns the number of non-HWND elements in the control bar. +PyObject *PyCControlBar_GetCount( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + if (!PyArg_ParseTuple( args, ":GetCount")) return NULL; + GUI_BGN_SAVE; + int rc = pCtlBar->GetCount(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} +// @pymethod int|PyCControlBar|IsFloating|Returns a nonzero value if the control bar in question is a floating control bar. +PyObject *PyCControlBar_IsFloating( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + if (!PyArg_ParseTuple( args, ":IsFloating")) return NULL; + GUI_BGN_SAVE; + int rc = pCtlBar->IsFloating(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + + +// @pymethod int|PyCControlBar|GetBarStyle|Retrieves the control bar style settings. +PyObject *PyCControlBar_GetBarStyle( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + if (!PyArg_ParseTuple( args, ":GetBarStyle")) return NULL; + GUI_BGN_SAVE; + int rc = pCtlBar->GetBarStyle(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod |PyCControlBar|SetBarStyle|Modifies the control bar style settings. +PyObject *PyCControlBar_SetBarStyle( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + int style; + // @pyparm int|style||The new style + if (!PyArg_ParseTuple( args, "i:SetBarStyle", &style)) + return NULL; + GUI_BGN_SAVE; + pCtlBar->SetBarStyle(style); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCControlBar|EnableDocking|pecifies whether the control bar supports docking and the sides of its parent window. +PyObject *PyCControlBar_EnableDocking( PyObject *self, PyObject *args ) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(self); + if (!pCtlBar) return NULL; + int style; + // @pyparm int|style||Enables a control bar to be docked. + if (!PyArg_ParseTuple( args, "i:EnableDocking", &style)) + return NULL; + GUI_BGN_SAVE; + pCtlBar->EnableDocking(style); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCControlBar|ShowWindow|Shows the toolbar, and recalculates the button layout. +static PyObject * +PyCControlBar_ShowWindow (PyObject *self, PyObject *args) +{ + // proto the base class method here + extern PyObject *ui_window_show_window(PyObject *self, PyObject *args); + + // @comm This method is provided for convenience. For further details, see + // and + + PyObject *ret = ui_window_show_window(self, args); + // call base first + CWnd *pWnd = GetWndPtr (self); + if (!pWnd) + return NULL; + + CWnd *parent = pWnd->GetParent(); + if (parent && parent->IsKindOf (RUNTIME_CLASS(CFrameWnd))) { + GUI_BGN_SAVE; + ((CFrameWnd *) parent)->RecalcLayout(); + GUI_END_SAVE; + } + return ret; + // @rdesc The return value is that returned from +} + +// @object PyCControlBar|A class which encapsulates an MFC . Derived from a object. +static struct PyMethodDef +PyCControlBar_methods[] = +{ + {"CalcDynamicLayout",PyCControlBar_CalcDynamicLayout,1}, // @pymeth CalcDynamicLayout|The framework calls this member function to calculate the dimensions of a dynamic toolbar. + {"CalcFixedLayout", PyCControlBar_CalcFixedLayout,1},// @pymeth CalcFixedLayout|Calculates the horizontal size of a control bar + {"EnableDocking", PyCControlBar_EnableDocking,1}, // @pymeth EnableDocking|Specifies whether the control bar supports docking and the sides of its parent window. + {"EraseNonClient", PyCControlBar_EraseNonClient,1}, // @pymeth EraseNonClient| + {"GetBarStyle", PyCControlBar_GetBarStyle,1}, // @pymeth GetBarStyle|Retrieves the control bar style settings. + {"GetCount", PyCControlBar_GetCount,1}, // @pymeth GetCount|Returns the number of non-HWND elements in the control bar. + {"GetDockingFrame", PyCControlBar_GetDockingFrame,1}, // @pymeth GetDockingFrame|Returns the frame window to which a control bar is docked. + {"IsFloating", PyCControlBar_IsFloating,1}, // @pymeth IsFloating|Returns a nonzero value if the control bar in question is a floating control bar. + {"SetBarStyle", PyCControlBar_SetBarStyle,1}, // @pymeth SetBarStyle|Modifies the control bar style settings. + {"ShowWindow", PyCControlBar_ShowWindow,1}, // @pymeth ShowWindow|Shows the window, and recalculates the toolbar layout. + { NULL, NULL } +}; + +PyObject *PyCControlBar::getattr(char *name) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(this); + if (!pCtlBar) return NULL; + if (strcmp(name, "dockSite")==0) { // @prop |dockSite|Current dock site, if dockable + if (pCtlBar->m_pDockSite==NULL) + RETURN_NONE; + return ui_assoc_object::make(UITypeFromCObject(pCtlBar->m_pDockSite), pCtlBar->m_pDockSite); + } + if (strcmp(name, "dockBar")==0) { // @prop |dockBar|Current dock bar, if dockable + if (pCtlBar->m_pDockBar==NULL) + RETURN_NONE; + return ui_assoc_object::make(UITypeFromCObject(pCtlBar->m_pDockBar), pCtlBar->m_pDockBar); + } + if (strcmp(name, "dockContext")==0) { // @prop |dockContext|Used during dragging + if (pCtlBar->m_pDockBar==NULL) + RETURN_NONE; + return ui_assoc_object::make(PyCDockContext::type, pCtlBar->m_pDockContext); + } + if (strcmp(name, "dwStyle")==0) // @prop int|dwStyle|creation style (used for layout) + return PyInt_FromLong(pCtlBar->m_dwStyle); + if (strcmp(name, "dwDockStyle")==0) // @prop int|dwDockStyle|indicates how bar can be docked + return PyInt_FromLong(pCtlBar->m_dwStyle); + + return PyCWnd::getattr(name); +} + +int PyCControlBar::setattr(char *name, PyObject *v) +{ + CControlBar *pCtlBar = PyCControlBar::GetControlBar(this); + if (!pCtlBar) return NULL; + if (strcmp(name, "dwStyle")==0) { + if (!PyInt_Check(v)) + RETURN_TYPE_ERR("dwStyle must be an integer"); + pCtlBar->m_dwStyle = PyInt_AsLong(v); + return 0; + } + if (strcmp(name, "dwDockStyle")==0) { + if (!PyInt_Check(v)) + RETURN_TYPE_ERR("dwDockStyle must be an integer"); + pCtlBar->m_dwDockStyle = PyInt_AsLong(v); + return 0; + } + return PyCWnd::setattr(name, v); +} + +ui_type_CObject PyCControlBar::type ("PyCControlBar", + &PyCWnd::type, + RUNTIME_CLASS(CControlBar), + sizeof(PyCControlBar), + PyCControlBar_methods, + GET_PY_CTOR(PyCControlBar)); + +/* ToolBar Wish, er, ToDo List: + + change toolbar button styles (SetButtonInfo) + +*/ +#define MAKE_GET_INT_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); \ + if (!pToolBar) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + return Py_BuildValue("i", pToolBar->mfcName(val)); \ +} + +#define MAKE_SETVOID_INT_METH(fnname, mfcName) \ +PyObject *fnname( PyObject *self, PyObject *args ) { \ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); \ + if (!pToolBar) return NULL; \ + int val; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &val)) \ + return NULL; \ + pToolBar->mfcName(val); \ + RETURN_NONE; \ +} + + +/* static */ CToolBar * +PyCToolBar::GetToolBar (PyObject *self) +{ + return (CToolBar *)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|CreateToolBar|Creates a toolbar object. +PyObject * +PyCToolBar::create (PyObject *self, PyObject *args) +{ + PyObject *parent; + int style; + int id = AFX_IDW_TOOLBAR; + if (!PyArg_ParseTuple (args,"Oi|i:CreateToolBar", + &parent, // @pyparm |parent||The parent window for the toolbar. + &style, // @pyparm int|style||The style for the toolbar. + &id)) // @pyparm int|windowId|afxres.AFX_IDW_TOOLBAR|The child window ID. + return NULL; + if (!ui_base_class::is_uiobject (parent, &PyCWnd::type)) + { + RETURN_ERR("The parent param must be a window object."); + } + + // @comm You must ensure no 2 toolbars share the same ID. + CString error; + CToolBar *tb = new CPythonToolBar(); + CFrameWnd *frame = (CFrameWnd *) + PyCWnd::GetPythonGenericWnd (parent, &PyCFrameWnd::type); + if (frame == NULL) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = tb->Create (frame, style, id); + GUI_END_SAVE; + if (!ok) { + delete tb; + RETURN_API_ERR("PyCToolBar.Create"); + } + tb->m_bAutoDelete = TRUE; // let MFC handle deletion + return ui_assoc_object::make (PyCToolBar::type, tb)->GetGoodRet(); +} + +// @pymethod |PyCToolBar|SetButtons|Sets button styles and an index of button images within the bitmap. +PyObject * +PyCToolBar_SetButtons(PyObject *self, PyObject *args) +{ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + PyObject *buttons; + if (!PyArg_ParseTuple (args,"O:SetButtons", + &buttons)) // @pyparm tuple|buttons||A tuple containing the ID's of the buttons. + return NULL; + + if (PyInt_Check(buttons)) { + // @pyparmalt1 int|numButtons||The number of buttons to pre-allocate. If this option is used, then must be used. + BOOL rc = pToolBar->SetButtons(NULL, PyInt_AsLong(buttons) ); + if (!rc) RETURN_API_ERR("PyCToolBar.SetButtons"); + RETURN_NONE; + } + // Not an integer - normal tuple of buttons. + if (!PyTuple_Check (buttons)) + RETURN_TYPE_ERR("SetButtons requires a tuple of IDs"); + + // convert button tuple to array + int num_buttons = PyTuple_Size (buttons); + UINT *button_list = new UINT[num_buttons]; + PyObject *o; + for (int i = 0; i < num_buttons; i++) { + o = PyTuple_GetItem (buttons, i); + if (!PyInt_Check(o)) { + delete button_list; + RETURN_ERR ("SetButtons expected integer button ids."); + } + button_list[i] = PyInt_AsLong (o); + } + BOOL rc = pToolBar->SetButtons(button_list, num_buttons); + delete button_list; + if (!rc) RETURN_API_ERR("PyCToolBar.SetButtons"); + RETURN_NONE; +} + +// @pymethod |PyCToolBar|SetButtonInfo|Sets the button's command ID, style, and image number. +static PyObject * +PyCToolBar_SetButtonInfo(PyObject *self, PyObject *args) +{ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar)return NULL; + + int nIndex; + UINT nID; + UINT nStyle; + int iImage; + if(!PyArg_ParseTuple(args,"iiii", + &nIndex, // @pyparm int|index||Index of the button or separator whose information is to be set. + &nID, // @pyparm int|ID||The value to which the button's command ID is set. + &nStyle, // @pyparm int|style||The new button style + &iImage)) // @pyparm int|imageIx||New index for the button's image within the bitmap + return NULL; + + // since the info is set through msgs we must protect state + GUI_BGN_SAVE; + pToolBar->SetButtonInfo(nIndex,nID,nStyle,iImage); + GUI_END_SAVE; + + RETURN_NONE; +} + +// @pymethod |PyCToolBar|GetToolTips|Returns the associated tooltips control +static PyObject * +PyCToolBar_GetToolTips(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetToolTips); + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar)return NULL; + CToolBarCtrl& toolBarCtrl=pToolBar->GetToolBarCtrl(); + CToolTipCtrl* pTTC=toolBarCtrl.GetToolTips(); + if(pTTC==NULL) + RETURN_ERR("Toolbar hasn't a tool tip control"); + return ui_assoc_object::make(PyCToolTipCtrl::type,pTTC); +} + +// @pymethod |PyCToolBar|SetToolTips|Sets the tooltips control +static PyObject * +PyCToolBar_SetToolTips(PyObject *self, PyObject *args) +{ + PyObject *obTTC; + if (!PyArg_ParseTuple(args,"O", + &obTTC)) // @pyparm |obTTC||The ToolTipCtrl ctrl to be set. + return NULL; + + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar)return NULL; + + CToolTipCtrl* pTTC=(CToolTipCtrl*)GetWndPtr(obTTC); + if(!pTTC) return NULL; + + + CToolBarCtrl& toolBarCtrl=pToolBar->GetToolBarCtrl(); + toolBarCtrl.SetToolTips(pTTC); + RETURN_NONE; +} + + +// @pymethod |PyCToolBar|SetBarStyle|Sets the toolbar part of style +static PyObject * +PyCToolBar_SetBarStyle(PyObject *self, PyObject *args) +{ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar)return NULL; + DWORD dwStyle; + if (!PyArg_ParseTuple(args,"l", + &dwStyle)) // @pyparm long|style||The toolbar style to set. + return NULL; + pToolBar->SetBarStyle(dwStyle); + RETURN_NONE; +} + +// @pymethod |PyCToolBar|LoadBitmap|Loads the bitmap containing bitmap-button images. +static PyObject * +PyCToolBar_LoadBitmap (PyObject *self, PyObject *args) +{ + BOOL rc; + int id; + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + if (PyArg_ParseTuple(args,"i", + &id )) // @pyparm int|id||The bitmap ID. + rc = pToolBar->LoadBitmap(id); + else { + char *szId; + PyErr_Clear(); + if (PyArg_ParseTuple(args,"i:LoadBitmap", + &szId )) // @pyparmalt1 string|id||The bitmap ID. + rc = pToolBar->LoadBitmap(szId); + else + RETURN_ERR("LoadBitmap requires an integer or string argument"); + } + + if (!rc) + RETURN_ERR("LoadBitmap failed"); + // @comm The bitmap should contain one image for each toolbar button. If the + // images are not of the standard size (16 pixels wide and 15 pixels high), + // call to set the button sizes and their images. + RETURN_NONE; +} +// @pymethod |PyCToolBar|LoadToolBar|Loads a toolbar from a toolbar resource. +static PyObject * +PyCToolBar_LoadToolBar (PyObject *self, PyObject *args) +{ + BOOL rc; + int id; + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + if (PyArg_ParseTuple(args,"i", + &id )) // @pyparm int|id||The bitmap ID. + rc = pToolBar->LoadToolBar(id); + else { + char *szId; + PyErr_Clear(); + if (PyArg_ParseTuple(args,"i:LoadBitmap", + &szId )) // @pyparmalt1 string|id||The bitmap ID. + rc = pToolBar->LoadToolBar(szId); + else + RETURN_ERR("LoadToolBar requires an integer or string argument"); + } + + if (!rc) + RETURN_ERR("LoadBitmap failed"); + // @comm The bitmap should contain one image for each toolbar button. If the + // images are not of the standard size (16 pixels wide and 15 pixels high), + // call to set the button sizes and their images. + RETURN_NONE; +} + + +// @pymethod |PyCToolBar|SetHeight|Sets the height of the toolbar. +// @pyparm int|height||The height in pixels of the toolbar. +MAKE_SETVOID_INT_METH(PyCToolBar_SetHeight, SetHeight ) + +// @pymethod |PyCToolBar|GetItemID|Returns the command ID of a button or separator at the given index. +// @pyparm int|index||Index of the item whose ID is to be retrieved. +MAKE_GET_INT_INT_METH(PyCToolBar_GetItemID, GetItemID ) + +// @pymethod |PyCToolBar|GetButtonStyle|Retrieves the style for a button. +// @pyparm int|index||Index of the item whose style is to be retrieved. +MAKE_GET_INT_INT_METH(PyCToolBar_GetButtonStyle, GetButtonStyle ) + +// @pymethod |PyCToolBar|SetBitmap|Sets a bitmapped image. +PyObject *PyCToolBar_SetBitmap( PyObject *self, PyObject *args ) +{ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) return NULL; + int val; + // @pyparm int|hBitmap||The handle to a bitmap resource. + // @comm Call this method to set the bitmap image for the toolbar. For example, + // call SetBitmap to change the bitmapped image after the user takes an action on + // a document that changes the action of a button. + if (!PyArg_ParseTuple( args, "i:SetBitmap", &val)) + return NULL; + if (!IsWin32s() && ::GetObjectType((HBITMAP)val) != OBJ_BITMAP) + RETURN_ERR("The bitmap handle is invalid"); + if (!pToolBar->SetBitmap((HBITMAP)val)) + RETURN_ERR("SetBitmap failed"); + RETURN_NONE; +} + + +// @pymethod |PyCToolBar|SetSizes|Sets the size of each button. +static PyObject * +PyCToolBar_SetSizes (PyObject *self, PyObject *args) +{ + SIZE sizeBut, sizeBmp; + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + + if (!PyArg_ParseTuple(args,"(ii)(ii)", + &sizeBut.cx, &sizeBut.cy, // @pyparm (cx, cy)|sizeButton||The size of each button. + &sizeBmp.cx, &sizeBmp.cy)) // @pyparm (cx, cy)|sizeButton||The size of each bitmap. + return NULL; + pToolBar->SetSizes(sizeBut, sizeBmp); + RETURN_NONE; +} + +// @pymethod |PyCToolBar|SetButtonStyle|Sets the style for a button. +static PyObject * +PyCToolBar_SetButtonStyle (PyObject *self, PyObject *args) +{ + int index, style; + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + if (!PyArg_ParseTuple(args,"ii", + &index, // @pyparm int|index||Index of the item whose style is to be set + &style))// @pyparm int|style||The new style + return NULL; + pToolBar->SetButtonStyle(index, style); + RETURN_NONE; +} + +// @pymethod string|PyCToolBar|GetButtonText|Gets the text for a button. +PyObject *PyCToolBar_GetButtonText( PyObject *self, PyObject *args ) +{ + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) return NULL; + int index; + // @pyparm int|index||Index of the item whose text is to be retrieved. + if (!PyArg_ParseTuple( args, "i:GetButtonText", &index)) + return NULL; + return Py_BuildValue("s", (const char *)pToolBar->GetButtonText(index)); +} + +// @pymethod |PyCToolBar|SetButtonText|Sets the text for a button. +static PyObject * +PyCToolBar_SetButtonText(PyObject *self, PyObject *args) +{ + int index; + char *text; + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) + return NULL; + if (!PyArg_ParseTuple(args,"is", + &index, // @pyparm int|index||Index of the item whose style is to be set + &text))// @pyparm string|text||The new text + return NULL; + pToolBar->SetButtonText(index, text); + RETURN_NONE; +} + +// @pymethod |PyCToolBar|GetToolBarCtrl|Gets the toolbar control object for the toolbar +PyObject *PyCToolBar_GetToolBarCtrl( PyObject *self, PyObject *args ) +{ + CHECK_NO_ARGS (args); + CToolBar *pToolBar = PyCToolBar::GetToolBar(self); + if (!pToolBar) return NULL; + + CToolBarCtrl &pTBC = pToolBar->GetToolBarCtrl(); + return ui_assoc_object::make (PyCToolBarCtrl::type, pTBC)->GetGoodRet(); +} + +// @object PyCToolBar|A class which encapsulates an MFC . Derived from a object. +static struct PyMethodDef +PyCToolBar_methods[] = +{ + {"GetButtonStyle", PyCToolBar_GetButtonStyle,1}, // @pymeth GetButtonStyle|Retrieves the style for a button. + {"GetButtonText", PyCToolBar_GetButtonText,1}, // @pymeth GetButtonText|Gets the text for a button. + {"GetItemID", PyCToolBar_GetItemID,1}, // @pymeth GetItemID|Returns the command ID of a button or separator at the given index. + {"GetToolTips", PyCToolBar_GetToolTips,1},// @pymeth SetButtonInfo|Gets the associated tooltip control + {"GetToolBarCtrl", PyCToolBar_GetToolBarCtrl,1}, // @pymeth GetToolBarCtrl|Returns the tool bar control object associated with the tool bar + {"LoadBitmap", PyCToolBar_LoadBitmap,1}, // @pymeth LoadBitmap|Loads the bitmap containing bitmap-button images. + {"LoadToolBar", PyCToolBar_LoadToolBar,1}, // @pymeth LoadToolBar|Loads a toolbar from a Toolbar resource. + {"SetBarStyle", PyCToolBar_SetBarStyle,1},// @pymeth SetBarStyle|Sets toolbar's (CBRS_xxx) part of style + {"SetBitmap", PyCToolBar_SetBitmap,1}, // @pymeth SetBitmap|Sets a bitmapped image. + {"SetButtonInfo", PyCToolBar_SetButtonInfo,1},// @pymeth SetButtonInfo|Sets the button's command ID, style, and image number. + {"SetButtons", PyCToolBar_SetButtons,1}, // @pymeth SetButtons|Sets button styles and an index of button images within the bitmap. + {"SetButtonStyle", PyCToolBar_SetButtonStyle,1}, // @pymeth SetButtonStyle|Sets the style for a button + {"SetHeight", PyCToolBar_SetHeight,1}, // @pymeth SetHeight|Sets the height of the toolbar. + {"SetSizes", PyCToolBar_SetSizes,1}, // @pymeth SetSizes|Sets the sizes for the toolbar items. + {"SetToolTips", PyCToolBar_SetToolTips,1},// @pymeth SetButtonInfo|Sets the tooltips control + { NULL, NULL } +}; + +ui_type_CObject PyCToolBar::type ("PyCToolBar", + &PyCControlBar::type, + RUNTIME_CLASS(CToolBar), + sizeof(PyCToolBar), + PyCToolBar_methods, + GET_PY_CTOR(PyCToolBar)); + +/////////////////////////////////////////////////////////////////////////////// +// +// PyCToolBarCtrl object +// + +#define MAKE_GET_BOOL_INT_METHOD(mfcName) \ +PyObject *PyCToolBarCtrl_##mfcName (PyObject *self, PyObject *args) { \ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); \ + if (!pTBC) return NULL; \ + int nID; \ + if (!PyArg_ParseTuple( args, "i:" #mfcName, &nID)) \ + return NULL; \ + GUI_BGN_SAVE; \ + int rc = pTBC->mfcName (nID); \ + GUI_END_SAVE; \ + return Py_BuildValue ("i", rc); \ +} + +#define MAKE_SET_INT_BOOL_METHOD(mfcName) \ +PyObject *PyCToolBarCtrl_##mfcName (PyObject *self, PyObject *args) { \ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); \ + if (!pTBC) return NULL; \ + int nID; int bSet;\ + if (!PyArg_ParseTuple( args, "ii:" #mfcName, &nID, &bSet)) \ + return NULL; \ + GUI_BGN_SAVE; \ + BOOL rc = pTBC->mfcName (nID, bSet); \ + GUI_END_SAVE; \ + if (!rc) \ + RETURN_ERR("CToolBarCtrl::" #mfcName); \ + RETURN_NONE; \ +} + +//#define MAKE_SET_INT_BOOL_METHOD(mfcName) MAKE_SET_INT_INT_METHOD(mfcName) + +PyCToolBarCtrl::PyCToolBarCtrl() +{ + bmplist = new CPtrArray(); + strlist = new CPtrArray(); +} + +PyCToolBarCtrl::~PyCToolBarCtrl() +{ + int i, n; + n = bmplist->GetSize(); + for (i = 0; i < n; i++) { + PyObject *o = (PyObject *) bmplist->GetAt (i); + Py_DECREF (o); + } + delete bmplist; + + n = strlist->GetSize(); + for (i = 0; i < n; i++) + delete strlist->GetAt (i); + delete strlist; +} + +/* static */ CToolBarCtrl *GetToolBarCtrl (PyObject *self) +{ + return (CToolBarCtrl *)ui_assoc_CObject::GetGoodCppObject( self, &PyCToolBarCtrl::type); +} + +// @pymethod |win32ui|CreateToolBarCtrl|Creates a toolbar control object. creates the actual control. +PyObject * +PyCToolBarCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = new CToolBarCtrl(); + return ui_assoc_object::make( PyCToolBarCtrl::type, pTBC ); +} + +// @pymethod |PyCToolBarCtrl|TBUTTON tuple|Describes a TBUTTON tuple, used by the PyCToolBarCtrl AddButtons method +// @pyparm int|iBitmap||Zero-based index of button image +// @pyparm int|idCommand||Command to be sent when button pressed +// @pyparm int|fsState||Button state. Can be any of the TBSTATE values defined in win32con +// @pyparm int|fsStyle||Button style. Can be any of the TBSTYLE values defined in win32con +// @pyparm object|userob||Arbitrary Python object +// @pyparm int|iString||Zero-based index of button label string +// @comm Userob is any Python object at all, but no reference count is kept, so you must ensure the object remains referenced throughout. + +// @pymethod int|PyCToolBarCtrl|AddBitmap|Add one or more button images to the list of button images + +PyObject *PyCToolBarCtrl_AddBitmap (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (!pTBC) return NULL; + int numButtons; + PyObject *pBitmap = NULL; + if (!PyArg_ParseTuple(args,"iO", + &numButtons, // @pyparm int|numButtons||Number of button images in the bitmap. + &pBitmap))// @pyparm |bitmap||Bitmap containing button or buttons to be added + return NULL; + + Py_INCREF (pBitmap); + ((PyCToolBarCtrl *) self)->bmplist->Add ((void *) pBitmap); + GUI_BGN_SAVE; + int rc = pTBC->AddBitmap (numButtons, ui_bitmap::GetBitmap (pBitmap)); + GUI_END_SAVE; + + // @pyseemfc CToolBarCtrl|AddBitmap + return Py_BuildValue ("i", rc); +} + +// @pymethod int|PyCToolBarCtrl|AddButtons|Add one or more buttons to the toolbar + +PyObject *PyCToolBarCtrl_AddButtons (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int numButtons; + + numButtons = PyTuple_Size (args); + + TBBUTTON *btn = new TBBUTTON[numButtons]; + + if (btn == NULL) + return NULL; + + int i; + PyObject *pButtonTuple; + + for (i = 0; i < numButtons; i++) { + pButtonTuple = PySequence_GetItem (args, i); + if (!PyArg_ParseTuple (pButtonTuple, "iibbOi", + &btn[i].iBitmap, + &btn[i].idCommand, + &btn[i].fsState, + &btn[i].fsStyle, + &btn[i].dwData, + &btn[i].iString)) { + delete btn; + return NULL; + } + } + + // @pyseemfc CToolBarCtrl|AddButtons + GUI_BGN_SAVE; + int rc = pTBC->AddButtons (numButtons, btn); + GUI_END_SAVE; + PyObject *ret = Py_BuildValue ("i",rc); + delete btn; + return ret; +} + +// @pymethod int|PyCToolBarCtrl|AddStrings|Add one or more strings to the toolbar + +PyObject *PyCToolBarCtrl_AddStrings (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + PyObject *pStringList = NULL; + PyObject *pString = NULL; + + int i; + int n = PyObject_Length(args); + int nchars = 0; + + for (i = 0; i < n; i++) { + nchars += PyObject_Length (PyTuple_GetItem (args, i)); + } + + char *buf = new char[nchars + n + 1]; + char *pbuf = buf; + ((PyCToolBarCtrl *) self)->strlist->Add (buf); + + // @pyparm string...|strings||Strings to add. Can give more than one string. + + /* + * Okay, so this is REALLY ugly... Blame Microsoft in this case. + * They actually require that you pass in a bunch of strings, + * separated by NULL bytes, and terminated by TWO NULL bytes. + * I'm going to go wash my hands now... + */ + + for (i = 0; i < n; i++) { + PyObject *o; + + o = PyTuple_GetItem (args, i); + + if (!PyArg_Parse (o, "s", &pbuf)) { + delete buf; + return NULL; + } + + pbuf += strlen (pbuf); + pbuf++; // Tack on another NULL byte + } + + *pbuf = '\0'; // Tack on ANOTHER NULL byte + + // @pyseemfc CToolBarCtrl|AddStrings + GUI_BGN_SAVE; + int rc = pTBC->AddStrings (buf); + GUI_END_SAVE; + + PyObject *ret = Py_BuildValue ("i", rc); + delete buf; + return ret; +} + +// @pymethod |PyCToolBarCtrl|AutoSize|Resize the entire toolbar control + +PyObject *PyCToolBarCtrl_AutoSize (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + GUI_BGN_SAVE; + pTBC->AutoSize(); // @pyseemfc CToolBarCtrl|AutoSize + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCToolBarCtrl|CheckButton|Check or clear a given button in a toolbar control + +PyObject *PyCToolBarCtrl_CheckButton (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID, bCheck = 1; + if (!PyArg_ParseTuple( args, "i|i:CheckButton", + &nID, // @pyparm int|nID||Command identifier of the button to check or clear. + &bCheck)) // @pyparm int|bCheck|1|1 to check, 0 to clear the button + return NULL; + GUI_BGN_SAVE; + int rc = pTBC->CheckButton (nID, bCheck); + GUI_END_SAVE; + + return Py_BuildValue ("i", rc); // @pyseemfc CToolBarCtrl|CheckButton +} + +// @pymethod int|PyCToolBarCtrl|CommandToIndex|Retrieve the zero-based index for the button associated with the specified command identifier. + +PyObject *PyCToolBarCtrl_CommandToIndex (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID; + if (!PyArg_ParseTuple( args, "i:CommandToIndex", + &nID)) // @pyparm int|nID||Command identifier of the button you want to find. + return NULL; + + GUI_BGN_SAVE; + int rc = pTBC->CommandToIndex (nID); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); // @pyseemfc CToolBarCtrl|CommandToIndex +} + +// @pymethod |PyCToolBarCtrl|CreateWindow|Creates the window for a new toolbar object +static PyObject * +PyCToolBarCtrl_CreateWindow(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + if (!PyArg_ParseTuple(args, "i(iiii)Oi:CreateWindow", + &style, // @pyparm int|style||The style for the button. Use any of the win32con.BS_* constants. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the button. + &obParent, // @pyparm |parent||The parent window of the button. Usually a . + &id )) // @pyparm int|id||The buttons control ID. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CToolBarCtrl *pTBC = GetToolBarCtrl (self); + if (!pTBC) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pTBC->Create(style, rect, pParent, id ); + GUI_END_SAVE; + if (!ok) // @pyseemfc CToolBarCtrl|Create + RETURN_ERR("CToolBarCtrl::Create"); + RETURN_NONE; +} + +// @pymethod |PyCToolBarCtrl|Customize|Display the Customize Toolbar dialog box. + +PyObject *PyCToolBarCtrl_Customize (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + GUI_BGN_SAVE; + pTBC->Customize(); // @pyseemfc CToolBarCtrl|Customize + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCToolBarCtrl|DeleteButton|Delete a button from the toolbar control. + +PyObject *PyCToolBarCtrl_DeleteButton (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID; + if (!PyArg_ParseTuple( args, "i:DeleteButton", + &nID)) // @pyparm int|nID||ID of the button to delete. + return NULL; + GUI_BGN_SAVE; + BOOL ok = pTBC->DeleteButton(nID); + GUI_END_SAVE; + + if (!ok) // @pyseemfc CToolBarCtrl|DeleteButton + RETURN_ERR ("CToolBarCtrl::DeleteButton"); + + RETURN_NONE; +} + +// @pymethod |PyCToolBarCtrl|EnableButton|Enable or disable a toolbar control button. +// @pyparm int|nID||ID of the button to enable or disable. +// @pyparm int|bEnable|1|1 to enable, 0 to disable +// @pyseemfc CToolBarCtrl|EnableButton +MAKE_SET_INT_BOOL_METHOD (EnableButton) + +// @pymethod int|PyCToolBarCtrl|GetBitmapFlags|retrieve the bitmap flags from the toolbar. + +PyObject *PyCToolBarCtrl_GetBitmapFlags (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + + GUI_BGN_SAVE; + int rc = pTBC->GetBitmapFlags (); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); // @pyseemfc CToolBarCtrl|GetBitmapFlags +} + +// @pymethod |PyCToolBarCtrl|GetButton|Retrieve information about the specified button in a toolbar control. + +PyObject *PyCToolBarCtrl_GetButton (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID; + if (!PyArg_ParseTuple( args, "i:GetButton", + &nID)) // @pyparm int|nID||ID of the button to retrieve. + return NULL; + TBBUTTON tbb; + GUI_BGN_SAVE; + BOOL ok = pTBC->GetButton (nID, &tbb); + GUI_END_SAVE; + if (!ok) // @pyseemfc CToolBarCtrl|GetButton + RETURN_ERR ("CToolBarCtrl::GetButton"); + return Py_BuildValue ("iibbli", + tbb.iBitmap, + tbb.idCommand, + tbb.fsState, + tbb.fsStyle, + tbb.dwData, + tbb.iString); +} + +// @pymethod int|PyCToolBarCtrl|GetButtonCount|Retrieve a count of the buttons currently in the toolbar control. + +PyObject *PyCToolBarCtrl_GetButtonCount (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + + GUI_BGN_SAVE; + int rc = pTBC->GetButtonCount (); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); // @pyseemfc CToolBarCtrl|GetButtonCount +} + +// @pymethod left, top, right, bottom|PyCToolBarCtrl|GetItemRect|Retrieve the bounding rectangle of a button in a toolbar control. + +PyObject *PyCToolBarCtrl_GetItemRect (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID; + if (!PyArg_ParseTuple( args, "i:GetItemRect", + &nID)) // @pyparm int|nID||ID of the button. + return NULL; + + RECT r; + GUI_BGN_SAVE; + BOOL ok = pTBC->GetItemRect (nID, &r); + GUI_END_SAVE; + if (!ok) // @pyseemfc CToolBarCtrl|GetItemRect + RETURN_ERR ("CToolBarCtrl::GetItemRect"); + return Py_BuildValue ("(iiii)", r.left, r.top, r.right, r.bottom); +} + +// @pymethod left, top, right, bottom|PyCToolBarCtrl|GetRows|Retrieve the number of rows of buttons currently displayed + +PyObject *PyCToolBarCtrl_GetRows (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + GUI_BGN_SAVE; + int rc = pTBC->GetRows(); + GUI_END_SAVE; + return Py_BuildValue ("i", rc); // @pyseemfc CToolBarCtrl|GetRows +} + +// @pymethod |PyCToolBarCtrl|HideButton|Hide or show the specified button in a toolbar control. +// @pyparm int|nID||ID of the button to hide. +// @pyparm int|bEnable|1|1 to hide, 0 to show. +// @pyseemfc CToolBarCtrl|HideButton + +MAKE_SET_INT_BOOL_METHOD(HideButton) + +// @pymethod |PyCToolBarCtrl|Indeterminate|Mark or unmark the specified button as indeterminate +// @pyparm int|nID||ID of the button to mark. +// @pyparm int|bEnable|1|1 to hide, 0 to show. +// @pyseemfc CToolBarCtrl|Indeterminate + +MAKE_SET_INT_BOOL_METHOD(Indeterminate) + +// @pymethod int|PyCToolBarCtrl|InsertButton|Insert a button in a toolbar control. +// @comm The image and/or string whose index you provide must have +// previously been added to the toolbar control's list using +// , , +// and/or . + +PyObject *PyCToolBarCtrl_InsertButton (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nID; + TBBUTTON btn; + + if (!PyArg_ParseTuple(args,"i(iibbOi)", + &nID, // @pyparm int|nID||Zero-based index of a button. This function inserts the new button to the left of this button. + &btn.iBitmap, // @pyparm |button||Bitmap containing button to be inserted + &btn.idCommand, + &btn.fsState, + &btn.fsStyle, + (PyObject *) &btn.dwData, + &btn.iString)) + return NULL; + + // @pyseemfc CToolBarCtrl|InsertButton + + GUI_BGN_SAVE; + BOOL ok = pTBC->InsertButton (nID, &btn); + GUI_END_SAVE; + if (!ok) + RETURN_ERR ("CToolBarCtrl::InsertButton"); + RETURN_NONE; +} + +// @pymethod int|PyCToolBarCtrl|IsButtonChecked|Determine whether the specified button in a toolbar control is checked. +// @pyparm int|nID||ID of the button to check. +// @pyseemfc CToolBarCtrl|IsButtonChecked +MAKE_GET_BOOL_INT_METHOD(IsButtonChecked) + +// @pymethod int|PyCToolBarCtrl|IsButtonEnabled|Determine whether the specified button in a toolbar control is enabled. +// @pyparm int|nID||ID of the button to check. +// @pyseemfc CToolBarCtrl|IsButtonEnabled +MAKE_GET_BOOL_INT_METHOD(IsButtonEnabled) + +// @pymethod int|PyCToolBarCtrl|IsButtonHidden|Determine whether the specified button in a toolbar control is hidden. +// @pyparm int|nID||ID of the button to check. +// @pyseemfc CToolBarCtrl|IsButtonHidden +MAKE_GET_BOOL_INT_METHOD(IsButtonHidden) + +// @pymethod int|PyCToolBarCtrl|IsButtonIndeterminate|Determine whether the specified button in a toolbar control is indeterminate. +// @pyparm int|nID||ID of the button to check. +// @pyseemfc CToolBarCtrl|IsButtonIndeterminate +MAKE_GET_BOOL_INT_METHOD(IsButtonIndeterminate) + +// @pymethod int|PyCToolBarCtrl|IsButtonPressed|Determine whether the specified button in a toolbar control is pressed. +// @pyparm int|nID||ID of the button to check. +// @pyseemfc CToolBarCtrl|IsButtonPressed +MAKE_GET_BOOL_INT_METHOD(IsButtonPressed) + +// @pymethod |PyCToolBarCtrl|PressButton|Mark or unmark the specified button as pressed. +// @pyparm int|nID||ID of the button to mark. +// @pyparm int|bEnable|1|1 to mark, 0 to unmark. +// @pyseemfc CToolBarCtrl|PressButton + +MAKE_SET_INT_BOOL_METHOD(PressButton) + +// @pymethod |PyCToolBarCtrl|SetBitmapSize|Set the size of the actual bitmapped images to be added to a toolbar control. +PyObject *PyCToolBarCtrl_SetBitmapSize (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + SIZE sz = {16, 15}; + if (!PyArg_ParseTuple( args, "|ii:SetBitmapSize", + &sz.cx, // @pyparm int|width|16|Width of bitmap images. + &sz.cy)) { // @pyparm int|height|15|Height of bitmap images. + PyErr_Clear(); + if (!PyArg_ParseTuple( args, "|(ii):SetBitmapSize", + &sz.cx, // @pyparmalt1 int|width|16|Width of bitmap images. + &sz.cy)) // @pyparmalt1 int|height|15|Height of bitmap images. + return NULL; + return NULL; + } + + GUI_BGN_SAVE; + BOOL ok = pTBC->SetBitmapSize (sz); + GUI_END_SAVE; + if (!ok) // @pyseemfc CToolBarCtrl|SetBitmapSize + RETURN_ERR ("CToolBarCtrl::SetBitmapSize"); + RETURN_NONE; +} + +// @pymethod |PyCToolBarCtrl|SetButtonSize|Set the size of the buttons to be added to a toolbar control. +PyObject *PyCToolBarCtrl_SetButtonSize (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + SIZE sz = {16, 15}; + if (!PyArg_ParseTuple( args, "|ii:SetButtonSize", + &sz.cx, // @pyparm int|width|16|Width of buttons + &sz.cy)) { // @pyparm int|height|15|Height of buttons + PyErr_Clear(); + if (!PyArg_ParseTuple( args, "|(ii):SetButtonSize", + &sz.cx, // @pyparmalt1 int|width|16|Width of bitmap images. + &sz.cy)) // @pyparmalt1 int|height|15|Height of bitmap images. + return NULL; + } + + GUI_BGN_SAVE; + BOOL ok = pTBC->SetButtonSize (sz); + GUI_END_SAVE; + if (!ok) // @pyseemfc CToolBarCtrl|SetButtonSize + RETURN_ERR ("CToolBarCtrl::SetButtonSize"); + RETURN_NONE; +} + +// @pymethod |PyCToolBarCtrl|SetCmdID|Set the command identifier which will be sent to the owner window when the specified button is pressed. +// @pyparm int|nIndex||The zero-based index of the button whose command ID is to be set. +// @pyparm int|nID||The command ID to set the selected button to. +// @pyseemfc CToolBarCtrl|SetCmdID + +MAKE_SET_INT_BOOL_METHOD(SetCmdID) + +// @pymethod left, top, right, bottom|PyCToolBarCtrl|SetRows|Ask the toolbar control to resize itself to the requested number of rows. +PyObject *PyCToolBarCtrl_SetRows (PyObject *self, PyObject *args) +{ + CToolBarCtrl *pTBC = GetToolBarCtrl(self); + if (pTBC == NULL) return NULL; + int nRows; + int bLarger; + RECT r; + + if (!PyArg_ParseTuple( args, "ii:SetRows", + &nRows, // @pyparm int|nRows||Requested number of rows. + &bLarger)) // @pyparm int|bLarger||Tells whether to use more rows or fewer rows if the toolbar cannot be resized to the requested number of rows. + return NULL; + + GUI_BGN_SAVE; + pTBC->SetRows (nRows, bLarger, &r); // @pyseemfc CToolBarCtrl|SetRows + GUI_END_SAVE; + return Py_BuildValue ("(iiii)", r.left, r.top, r.right, r.bottom); +} + + +// @object PyCToolBarCtrl|A class which encapsulates an MFC . Derived from a object. Created using +static struct PyMethodDef +PyCToolBarCtrl_methods[] = +{ + {"AddBitmap", PyCToolBarCtrl_AddBitmap, 1}, // @pymeth AddBitmap|Add one or more button images to the list of button images + {"AddButtons", PyCToolBarCtrl_AddButtons, 1}, // @pymeth AddButtons|Add one or more buttons + {"AddStrings", PyCToolBarCtrl_AddStrings, 1}, // @pymeth AddStrings|Add one or more strings + {"AutoSize", PyCToolBarCtrl_AutoSize, 1}, // @pymeth AutoSize|Resize the entire toolbar + {"CheckButton", PyCToolBarCtrl_CheckButton, 1}, // @pymeth CheckButton|Check or clear a button + {"CommandToIndex", PyCToolBarCtrl_CommandToIndex, 1}, // @pymeth CommandToIndex|Retrieve the zero-based index for the button associated with the specified command identifier. + {"CreateWindow", PyCToolBarCtrl_CreateWindow, 1}, // @pymeth CreateWindow|Create the actual control + {"Customize", PyCToolBarCtrl_Customize, 1}, // @pymeth Customize|Display the customize toolbar dialog box + {"DeleteButton", PyCToolBarCtrl_DeleteButton, 1}, // @pymeth DeleteButton|Delete a button from the toolbar control + {"EnableButton", PyCToolBarCtrl_EnableButton, 1}, // @pymeth EnableButton|Enable or disable a toolbar control button. + {"GetBitmapFlags", PyCToolBarCtrl_GetBitmapFlags, 1}, // @pymeth GetBitmapFlags|Retrieve the bitmap flags from the toolbar. + {"GetButton", PyCToolBarCtrl_GetButton, 1}, // @pymeth GetButton|Retrieve information about the specified button in a toolbar control. + {"GetButtonCount", PyCToolBarCtrl_GetButtonCount, 1}, // @pymeth GetButtonCount|Retrieve a count of the buttons currently in the toolbar control. + {"GetItemRect", PyCToolBarCtrl_GetItemRect, 1}, // @pymeth GetItemRect|Retrieve the bounding rectangle of a button in a toolbar control. + {"GetRows", PyCToolBarCtrl_GetRows, 1}, // @pymeth GetRows|Retrieve the number of rows of buttons currently displayed + {"HideButton", PyCToolBarCtrl_HideButton, 1}, // @pymeth HideButton|Hide or show the specified button in a toolbar control. + {"Indeterminate", PyCToolBarCtrl_Indeterminate, 1}, // @pymeth Indeterminate|Hide or show the specified button in a toolbar control. + {"InsertButton", PyCToolBarCtrl_InsertButton, 1}, // @pymeth InsertButton|Insert a button into a toolbar control. + {"IsButtonChecked", PyCToolBarCtrl_IsButtonChecked, 1}, // @pymeth IsButtonChecked|See if a button is checked. + {"IsButtonEnabled", PyCToolBarCtrl_IsButtonEnabled, 1}, // @pymeth IsButtonEnabled|See if a button is enabled. + {"IsButtonHidden", PyCToolBarCtrl_IsButtonHidden, 1}, // @pymeth IsButtonHidden|See if a button is checked. + {"IsButtonIndeterminate", PyCToolBarCtrl_IsButtonIndeterminate, 1}, // @pymeth IsButtonIndeterminate|See if a button is Indeterminate. + {"IsButtonPressed", PyCToolBarCtrl_IsButtonPressed, 1}, // @pymeth IsButtonPressed|See if a button is pressed. + {"PressButton", PyCToolBarCtrl_PressButton, 1}, // @pymeth PressButton|Mark or unmark the specified button as pressed. + {"SetBitmapSize", PyCToolBarCtrl_SetBitmapSize, 1}, // @pymeth SetBitmapSize|Set the size of the actual bitmapped images to be added to a toolbar control. + {"SetButtonSize", PyCToolBarCtrl_SetButtonSize, 1}, // @pymeth SetButtonSize|Set the size of the actual buttons to be added to a toolbar control. + {"SetCmdID", PyCToolBarCtrl_SetCmdID, 1}, // @pymeth SetCmdID|Set the command identifier which will be sent to the owner window when the specified button is pressed. + {"SetRows", PyCToolBarCtrl_SetRows, 1}, // @pymeth SetRows|Ask the toolbar control to resize itself to the requested number of rows. + { NULL, NULL } +}; + +ui_type_CObject PyCToolBarCtrl::type ("PyCToolBarCtrl", + &PyCWnd::type, + RUNTIME_CLASS(CToolBarCtrl), + sizeof(PyCToolBarCtrl), + PyCToolBarCtrl_methods, + GET_PY_CTOR(PyCToolBarCtrl)); + +/////////////////////////////////////////////////////////////////////////////// +// +// CStatusBar object +// +/* static */ CStatusBar * +PyCStatusBar::GetStatusBar (PyObject *self) +{ + return (CStatusBar *)GetGoodCppObject( self, &type); +} + +// @pymethod |win32ui|CreateStatusBar|Creates a statusbar object. +PyObject * +PyCStatusBar::create (PyObject *self, PyObject *args) +{ + PyObject *parent; + int style = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM; + int id = AFX_IDW_STATUS_BAR; + if (!PyArg_ParseTuple (args,"O|ii:CreateStatusBar", + &parent, // @pyparm |parent||The parent window for the status bar. + &style, // @pyparm int|style|afxres.WS_CHILD \| afxres.WS_VISIBLE \| afxres.CBRS_BOTTOM|The style for the status bar. + &id)) // @pyparm int|windowId|afxres.AFX_IDW_STATUS_BAR|The child window ID. + return NULL; + if (!ui_base_class::is_uiobject (parent, &PyCWnd::type)) + { + RETURN_ERR("The parent param must be a window object."); + } + + // @comm You must ensure no 2 status bars share the same ID. + CString error; + CStatusBar *sb = new CPythonStatusBar(); + CFrameWnd *frame = (CFrameWnd *) + PyCWnd::GetPythonGenericWnd (parent, &PyCFrameWnd::type); + if (frame == NULL) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = sb->Create (frame, style, id); + GUI_END_SAVE; + if (!ok) { + delete sb; + RETURN_API_ERR("CStatusBar.Create"); + } + sb->m_bAutoDelete = TRUE; // let MFC handle deletion??? really?? Cloned from toolbar - not so sure about status bar!! + return ui_assoc_object::make (PyCStatusBar::type, sb)->GetGoodRet(); +} + +// @pymethod |PyCStatusBar|SetIndicators|Sets each indicator's ID. +PyObject * +PyCStatusBar_SetIndicators(PyObject *self, PyObject *args) +{ + PyObject *buttons; + if (!PyArg_ParseTuple (args,"O:SetIndicators", + &buttons) // @pyparm tuple|indicators||A tuple containing the ID's of the indicators. + || + !PySequence_Check (buttons)) + RETURN_ERR("SetIndicators requires a tuple of IDs"); + CStatusBar *pSB = PyCStatusBar::GetStatusBar(self); + if (!pSB) + return NULL; + // convert indicator sequence to array + int num_buttons = PySequence_Length (buttons); + UINT *button_list = new UINT[num_buttons]; + PyObject *o; + for (int i = 0; i < num_buttons; i++) { + o = PySequence_GetItem (buttons, i); + if (!PyInt_Check(o)) { + Py_XDECREF(o); + delete button_list; + RETURN_ERR ("SetIndicators expected integer button ids."); + } + button_list[i] = PyInt_AsLong (o); + Py_DECREF(o); + } + BOOL rc = pSB->SetIndicators(button_list, num_buttons); + delete button_list; + if (!rc) RETURN_API_ERR("PyCStatusBar.SetIndicators"); + RETURN_NONE; +} + +// @object PyCStatusBar|A class which encapsulates an MFC . Derived from a object. +static struct PyMethodDef +PyCStatusBar_methods[] = +{ + {"SetIndicators", PyCStatusBar_SetIndicators, 1}, // @pymeth SetIndicators|Sets each indicator's ID. + { NULL, NULL } +}; + +ui_type_CObject PyCStatusBar::type ("PyCStatusBar", + &PyCControlBar::type, + RUNTIME_CLASS(CStatusBar), + sizeof(PyCStatusBar), + PyCStatusBar_methods, + GET_PY_CTOR(PyCStatusBar)); + diff --git a/Pythonwin/win32toolbar.h b/Pythonwin/win32toolbar.h new file mode 100644 index 0000000000..5800d1aa93 --- /dev/null +++ b/Pythonwin/win32toolbar.h @@ -0,0 +1,47 @@ +// long awaited toolbar class + +class PyCControlBar : public PyCWnd +{ +public: + MAKE_PY_CTOR(PyCControlBar) + static ui_type_CObject type; + static CControlBar *GetControlBar (PyObject *self); + virtual PyObject *getattr(char *name); + virtual int setattr(char *name, PyObject *v); + static PyObject *create (PyObject *self, PyObject *args); + +protected: + // virtual CString repr(); maybe add later to show id? +private: +}; + +class PyCToolBar : public PyCControlBar +{ +public: + MAKE_PY_CTOR(PyCToolBar) + static ui_type_CObject type; + static PyObject *create (PyObject *self, PyObject *args); + static CToolBar *GetToolBar (PyObject *self); +}; + +class PyCStatusBar : public PyCControlBar +{ +public: + MAKE_PY_CTOR(PyCStatusBar) + static ui_type_CObject type; + static PyObject *create (PyObject *self, PyObject *args); + static CStatusBar *GetStatusBar (PyObject *self); +}; + +class PyCToolBarCtrl : public PyCWnd +{ +public: + MAKE_PY_CTOR(PyCToolBarCtrl) + static ui_type_CObject type; + CPtrArray *bmplist; + CPtrArray *strlist; +protected: + PyCToolBarCtrl(); + ~PyCToolBarCtrl(); +private: +}; \ No newline at end of file diff --git a/Pythonwin/win32tooltip.cpp b/Pythonwin/win32tooltip.cpp new file mode 100644 index 0000000000..6c9d6ffc61 --- /dev/null +++ b/Pythonwin/win32tooltip.cpp @@ -0,0 +1,177 @@ +// Contributed by kk@epsilon.com.gr +// +// +// 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 "win32win.h" +#include "win32control.h" + +#include "win32gdi.h" +#include "win32bitmap.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + + +// +// PyCToolTipCtrl +// +static CToolTipCtrl *GetToolTipCtrl(PyObject *self) +{ + return (CToolTipCtrl *)PyCWnd::GetPythonGenericWnd(self); +} +PyCToolTipCtrl::PyCToolTipCtrl() +{ +} +PyCToolTipCtrl::~PyCToolTipCtrl() +{ +} + +// @pymethod |win32ui|CreateToolTipCtrl|Creates a progress control object. creates the actual control. +PyObject * +PyCToolTipCtrl_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CToolTipCtrl *pTTC = new CToolTipCtrl(); + return ui_assoc_object::make( PyCToolTipCtrl::type, pTTC ); +} + +// @pymethod |PyCToolTipCtrl|CreateWindow|Creates the actual control. +static PyObject * +PyCToolTipCtrl_create_window(PyObject *self, PyObject *args) +{ + int style; + PyObject *obParent; + if (!PyArg_ParseTuple(args, "Oi:CreateWindow", + &obParent, // @pyparm |parent||The parent window of the control. + &style)) // @pyparm int|style||The style for the control. + return NULL; + + if (!ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + RETURN_TYPE_ERR("parent argument must be a window object"); + CWnd *pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + CToolTipCtrl *pTTC = GetToolTipCtrl(self); + if (!pTTC) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pTTC->Create(pParent,style); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CToolTipCtrl::Create"); + RETURN_NONE; +} + + +// @pymethod |PyCToolTipCtrl|UpdateTipText|Update the tool tip text for a control's tools +static PyObject * +PyCToolTipCtrl_update_tip_text(PyObject *self, PyObject *args) + { + PyObject *obWnd; + char *pszText; + UINT nIDTool; + if (!PyArg_ParseTuple(args, "sOi:UpdateTipText", + &pszText,// @pyparm string|text||The text for the tool. + &obWnd, // @pyparm |wnd||The window of the tool. + &nIDTool// @pyparm int|id||The id of the tool + )) + return NULL; + + CWnd *pWndToolOwner = NULL; + if (obWnd != Py_None) + { + if (!ui_base_class::is_uiobject(obWnd,&PyCWnd::type)) + RETURN_TYPE_ERR("wnd argument must be a window object"); + pWndToolOwner = GetWndPtr(obWnd); + if (pWndToolOwner==NULL) + RETURN_TYPE_ERR("The window is not a valid PyCWnd"); + } + + CToolTipCtrl *pTTC = GetToolTipCtrl(self); + if (!pTTC)return NULL; + + GUI_BGN_SAVE; + pTTC->UpdateTipText(pszText,pWndToolOwner,nIDTool); + GUI_END_SAVE; + RETURN_NONE; + } + + + +// @pymethod |PyCToolTipCtrl|AddTool|Adds a tool to tooltip control. +static PyObject * +PyCToolTipCtrl_add_tool(PyObject *self, PyObject *args) + { + PyObject *obWnd,*obRect; + char *pszText; + UINT nIDTool; + if (!PyArg_ParseTuple(args, "OsOi:CreateWindow", + &obWnd, // @pyparm |wnd||The window of the tool. + &pszText,// @pyparm string|text||The text for the tool. + &obRect, // @pyparm int, int, int, int|rect|None|The default rectangle + &nIDTool// @pyparm int|id||The id of the tool + )) + return NULL; + + CWnd *pWnd = NULL; + if (obWnd != Py_None) + { + if (!ui_base_class::is_uiobject(obWnd,&PyCWnd::type)) + RETURN_TYPE_ERR("wnd argument must be a window object"); + pWnd = GetWndPtr(obWnd); + if (pWnd==NULL) + RETURN_TYPE_ERR("The window is not a valid PyCWnd"); + } + + RECT rect; + RECT *pRectTool=NULL; + if (obRect != Py_None) + { + if (!PyArg_ParseTuple(obRect, "iiii", &rect.left, &rect.top, &rect.right,&rect.bottom)) + { + PyErr_Clear(); + RETURN_TYPE_ERR("Rect must be None or a tuple of (iiii)"); + } + pRectTool=▭ + } + + + CToolTipCtrl *pTTC = GetToolTipCtrl(self); + if (!pTTC)return NULL; + + GUI_BGN_SAVE; + BOOL ok=pTTC->AddTool(pWnd,pszText,pRectTool,nIDTool); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CToolTipCtrl::AddTool"); + RETURN_NONE; + } + + + +// @object PyCToolTipCtrl|A windows tooltip control. Encapsulates an MFC class. Derived from . +static struct PyMethodDef PyCToolTipCtrl_methods[] = { + {"CreateWindow", PyCToolTipCtrl_create_window,1}, // @pymeth CreateWindow|Creates the window for a new progress bar object. + {"UpdateTipText", PyCToolTipCtrl_update_tip_text, 1}, // @pymeth UpdateTipText|Update the tool tip text for a control's tools + {"AddTool", PyCToolTipCtrl_add_tool, 1}, // @pymeth AddTool|Adds a tool to tooltip control. + {NULL, NULL} +}; + +ui_type_CObject PyCToolTipCtrl::type("PyCToolTipCtrl", + &ui_control_object::type, + RUNTIME_CLASS(CToolTipCtrl), + sizeof(PyCToolTipCtrl), + PyCToolTipCtrl_methods, + GET_PY_CTOR(PyCToolTipCtrl)); diff --git a/Pythonwin/win32ui.h b/Pythonwin/win32ui.h new file mode 100644 index 0000000000..7c5a2fbc9e --- /dev/null +++ b/Pythonwin/win32ui.h @@ -0,0 +1,360 @@ +// -*- Mode: C++; tab-width: 4 -*- +// +// win32ui.h +// +// external declarations for the application. +// +// +#ifndef __WIN32UI_H__ +#define __WIN32UI_H__ + +#ifdef FREEZE_WIN32UI +# define PYW_EXPORT +#else +# ifdef BUILD_PYW +# define PYW_EXPORT __declspec(dllexport) +# else +# define PYW_EXPORT __declspec(dllimport) +# ifdef _DEBUG +# pragma comment(lib,"win32ui_d.lib") +# else +# pragma comment(lib,"win32ui.lib") +# endif +# endif +#endif + +#include // Bit of an unusual MFC header. +#include // Also unusual - needed for CCreateContext. + +#define DOINCREF(o) Py_INCREF(o) +#define DODECREF(o) Py_DECREF(o) +#define XDODECREF(o) Py_XDECREF(o) + +// we cant use these memory operators - must use make and python handles delete +#undef NEWOBJ +#undef DEL + + +// implement a few byte overhead for type checking. +static char SIG[] = "py4w"; + +// Identical to Py_BEGIN_ALLOW_THREADS except no script "{" !!! +// means variables can be declared between the blocks +#define GUI_BGN_SAVE PyThreadState *_save = PyEval_SaveThread() +#define GUI_END_SAVE PyEval_RestoreThread(_save) +#define GUI_BLOCK_THREADS Py_BLOCK_THREADS + +inline BOOL IsWin32s() {return FALSE;} + +inline BOOL IsGdiHandleValid(HANDLE hobject) \ + {return hobject == NULL || IsWin32s() || ::GetObjectType(hobject) != 0;} + + +CString GetAPIErrorString(char *fnName); +CString GetAPIErrorString(DWORD dwCode); + +// The do/while clauses wrapped around these macro bodies are a cpp +// idiom - they allow you to unambiguously treat a macro 'call' - even +// one consisting of multiple statements - as a single statement, +// terminated by a semicolon. (SMR 960129) +extern PyObject *ReturnAPIError(const char *fn); +#define RETURN_NONE do {Py_INCREF(Py_None);return Py_None;} while (0) +#define RETURN_ERR(err) do {PyErr_SetString(ui_module_error,err);return NULL;} while (0) +#define RETURN_MEM_ERR(err) do {PyErr_SetString(PyExc_MemoryError,err);return NULL;} while (0) +#define RETURN_TYPE_ERR(err) do {PyErr_SetString(PyExc_TypeError,err);return NULL;} while (0) +#define RETURN_VALUE_ERR(err) do {PyErr_SetString(PyExc_ValueError,err);return NULL;} while (0) +#define RETURN_API_ERR(fn) return ReturnAPIError(fn) + +#define CHECK_NO_ARGS(args) do {if (!PyArg_ParseTuple(args,"")) return NULL;} while (0) +#define CHECK_NO_ARGS2(args, fnName) do {if (!PyArg_ParseTuple(args,":"#fnName)) return NULL;} while (0) + +extern PYW_EXPORT PyObject *ui_module_error; + +// Note: design rules to be aware of when looking/coding/etc +// (Im making these up after most is coded already, and just about to implement!) +// +// All object creation must now be via ui_base_class::make +// +// For any object derived from ui_cmd_target, it is important there +// is exactly one c++ object per python object. to support this, +// ui_cmd_target has a make that has an ASSOC object passed. This will +// return a reference to an existing object if one already exists. +/* + The general class hierarchy is: + ui_base_class Mainly Python helpers. + | + + ui_assoc All objects that maintain a mapping between + | an external C++ object and a Python object. + | (ie, all non trivial!) Inherits all ui_base classes. + | + + ui_assoc_CObject - base of all CObject partnered classes + | + + ui_menu Association is to the hMenu + | + + ui_cmd_target Does not define assoc. + | | + | + ui_window Assoc is hWnd + | | + | + ui_edit_window Inherits Window methods and Assoc. + | | + | + ui_mdi_frame Ditto. + | ... + | + + ui_dc Assoc is hDC + | + + ui_document Assoc is CDocument pointer. + + +*/ +// +// object types +// +// to make life convenient, I derive from PyObject, rather than "include" +// the structure at the start. As PyObject has no virtual members, casts +// will offset the pointer. +// It is important that the functions which handle python methods +// only have self declared as "PyObject *", not "class *", as the +// vfptr stuffs things up. + +class ui_base_class; +//////////////////// + +// helper typeobject class. +class PYW_EXPORT ui_type : public PyTypeObject { +public: + ui_type( const char *name, ui_type *pBaseType, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ); + ~ui_type(); +public: + ui_type *base; + struct PyMethodDef* methods; + ui_base_class * (* ctor)(); +}; + +// helper typeCObject class. +class PYW_EXPORT ui_type_CObject : public ui_type { +public: + ui_type_CObject( const char *name, ui_type *pBaseType, CRuntimeClass *pRT, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ); + ~ui_type_CObject(); +public: + ui_type *base; + CRuntimeClass *pCObjectClass; + // A map of CRuntimeClass to these objects. Populated by the ctor. + // Allows us to convert from an arbitary CObject to the best Python type. + typedef CMap CRuntimeClassTypeMap; + static CRuntimeClassTypeMap* typemap; +}; + +PYW_EXPORT ui_type_CObject &UITypeFromCObject( CObject *ob ); +PYW_EXPORT ui_type_CObject &UITypeFromHWnd( HWND hwnd ); + +CString GetReprText( PyObject *objectUse ); + +#ifdef _DEBUG +void DumpAssocPyObject( CDumpContext &dc , void *object ); + +#ifdef TRACK_PYTHON_OBJECTS +#define _DEBUG_TRACK_PYTHON_OBJECTS +#endif +#endif + +#if defined(_DEBUG) && defined(TRACK_PYTHON_OBJECTS) +#define MAKE_PY_CTOR(classname) static ui_base_class * classname::PyObConstruct(void) {return new classname;} +#else +#define MAKE_PY_CTOR(classname) static ui_base_class * classname::PyObConstruct(void) { \ + BOOL bOld = AfxEnableMemoryTracking(FALSE); \ + ui_base_class * ret = new classname; \ + AfxEnableMemoryTracking(bOld); \ + return ret; } +#endif + +#define GET_PY_CTOR(classname) classname::PyObConstruct + +// general purpose base class for my C++ objects. +// +// Note that Python itself cannot create these data types itself - the program +// must call a module method to do so, so it is totally C++'s responsibility +// to enforce this. To this end, all constructors are protected. + +class PYW_EXPORT ui_base_class : +#ifdef _DEBUG + // In debug mode, we use MI!! This gives us the ability + // to dump these objects as MFC objects, aiding in leak detection + // (now all we need do is track all those leaks :-) + public CObject, +#endif + public PyObject +{ +public: + static ui_base_class *make( ui_type &type ); + + // virtuals for Python support + virtual CString repr(); + virtual PyObject *getattr(char *name); + virtual int setattr(char *name, PyObject *v); + virtual void cleanup(); + + static struct PyMethodDef ui_base_class::empty_methods[]; + static ui_type type; // my type. +protected: + ui_base_class(); + virtual ~ui_base_class(); + +public: + static BOOL is_uiobject( PyObject *&, ui_type *which); + static BOOL ui_base_class::is_nativeuiobject(PyObject *ob, ui_type *which); + + BOOL is_uiobject(ui_type *which); + static void sui_dealloc(PyObject *ob); + static PyObject *sui_repr(PyObject *ob); + static PyObject *sui_getattr(PyObject *self, char *name); + static int sui_setattr(PyObject *op, char *name, PyObject *v); +#ifdef _DEBUG + DECLARE_DYNAMIC(ui_base_class) + virtual void Dump( CDumpContext &dc ) const; +#endif +private: + char sig[sizeof(SIG)]; +}; + +// for threading, must use GUI versions of these calls +PYW_EXPORT PyObject *gui_call_object(PyObject *themeth, PyObject *thearglist); +PYW_EXPORT void gui_print_error(void); +void gui_decref(PyObject *o); + + +//#endif // Py_ALLOBJECTS_H +// +// CreateContext used when creating frames etc. +// +class PYW_EXPORT PythonCreateContext : public CCreateContext { +public: + PythonCreateContext(); + ~PythonCreateContext(); + void SetPythonObject(PyObject *ob); + void ReleasePythonObject(); + PyObject *GetPythonObject() {return m_PythonObject;} +private: + PyObject *m_PythonObject; +}; + +// A helper class for calling "virtual methods" - ie, given a C++ object +// call a Python method of that name on the attached Python object. + +// The type of error handling we want... +enum EnumVirtualErrorHandling { + VEH_PRINT_ERROR, + VEH_DISPLAY_DIALOG +}; + +class PYW_EXPORT CVirtualHelper +{ +public: + CVirtualHelper(const char *iname, const void *iassoc, EnumVirtualErrorHandling veh = VEH_PRINT_ERROR); + ~CVirtualHelper(); + + BOOL HaveHandler() {return handler!=NULL;} + // All the "call" functions return FALSE if the call failed, or no handler exists. + BOOL call(); + BOOL call(int); + BOOL call(int, int); + BOOL call(int, int, int); + BOOL call(long); + BOOL call(const char *); + BOOL call(const char *, int); + BOOL call(CDC *, CPrintInfo *); + BOOL call(CPrintInfo *); + BOOL call(CDC *); + BOOL call(CDocument *); + BOOL call(CWnd *); + BOOL call(CWnd *, int); + BOOL call(CWnd *, int, int); + BOOL call(BOOL, CWnd *, CWnd *); + BOOL call(LPCREATESTRUCT); + BOOL call(LPCREATESTRUCT, PyObject *); + BOOL call(PyObject *); + BOOL call(PyObject *, PyObject *); + BOOL call(PyObject *, PyObject *, int); + BOOL call(CView *pWnd, PyObject *ob); + BOOL call(CDC *pDC, CWnd *pWnd, int i); + BOOL call(const MSG *); + BOOL call(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo); + BOOL call_args(PyObject *arglst); + // All the retval functions will ASSERT if the call failed! + BOOL retval( int &ret ); + BOOL retval( long &ret ); + BOOL retval( PyObject* &ret ); + BOOL retval( CREATESTRUCT &cs ); + BOOL retval( char * &ret ); + BOOL retval( CString &ret ); + BOOL retval( MSG *msg); + BOOL retnone(); + PyObject *GetHandler(); +private: + BOOL do_call(PyObject *args); + PyObject *handler; + PyObject *retVal; + PyObject *py_ob; + CString csHandlerName; + EnumVirtualErrorHandling vehErrorHandling; +}; + +// These error functions are designed to be used "asynchronously" - ie, where +// there is no Python call to return NULL from. These force an exception to +// be printed. +PYW_EXPORT PyObject *Python_do_callback(PyObject *themeth, PyObject *thearglst); +PYW_EXPORT int Python_callback(PyObject *); +PYW_EXPORT int Python_callback(PyObject *, int); +PYW_EXPORT int Python_callback(PyObject *, int, int); +PYW_EXPORT int Python_callback(PyObject *, const MSG *); +PYW_EXPORT int Python_callback(PyObject *method, PyObject *object); +int Python_run_command_with_log(const char *command, const char * logFileName); +PYW_EXPORT BOOL Python_check_message(const MSG *pMsg); // TRUE if fully processed. +PYW_EXPORT BOOL Python_check_key_message(const MSG *pMsg); // TRUE if fully processed. +PYW_EXPORT BOOL Python_OnCmdMsg(CCmdTarget *, UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo );// TRUE if fully processed. +PYW_EXPORT BOOL Python_OnNotify (CWnd *pFrom, WPARAM, LPARAM lParam, LRESULT *pResult); + +// Conversion routines +PYW_EXPORT BOOL CreateStructFromPyObject(LPCREATESTRUCT lpcs, PyObject *ob, const char *fnName = NULL, BOOL bFromTuple = FALSE); +PYW_EXPORT PyObject *PyObjectFromCreateStruct(LPCREATESTRUCT lpcs); + +PYW_EXPORT BOOL DictToLogFont(PyObject *font_props, LOGFONT *pLF); +PYW_EXPORT PyObject *LogFontToDict(const LOGFONT &lf); + +PYW_EXPORT BOOL ParseCharFormatTuple( PyObject *args, CHARFORMAT *pFmt); +PYW_EXPORT PyObject *MakeCharFormatTuple(CHARFORMAT *pFmt); +PYW_EXPORT BOOL ParseParaFormatTuple( PyObject *args, PARAFORMAT *pFmt); +PYW_EXPORT PyObject *MakeParaFormatTuple(PARAFORMAT *pFmt); + +PYW_EXPORT PyObject *MakeLV_ITEMTuple(LV_ITEM *item); +PYW_EXPORT BOOL ParseLV_ITEMTuple( PyObject *args, LV_ITEM *pItem); + +PYW_EXPORT PyObject *MakeLV_COLUMNTuple(LV_COLUMN *item); +PYW_EXPORT BOOL ParseLV_COLUMNTuple( PyObject *args, LV_COLUMN *pItem); + +PYW_EXPORT BOOL ParseTV_ITEMTuple( PyObject *args, TV_ITEM *pItem); +PYW_EXPORT PyObject *MakeTV_ITEMTuple(TV_ITEM *item); + +PyObject *PyWin_GetPythonObjectFromLong(long val); + +PYW_EXPORT PyObject *PyWinObject_FromRECT(RECT *p, bool bTakeCopy); +PYW_EXPORT PyObject *PyWinObject_FromRECT(const RECT &r); + +PYW_EXPORT PyObject *PyWinObject_FromCWnd(CWnd *); + +PYW_EXPORT void Python_do_exchange(CDialog *pDlg, CDataExchange *pDX); + +// call when an external object dies. +PYW_EXPORT void Python_delete_assoc( void *ob ); + +PYW_EXPORT void Python_addpath( const char *paths ); + +// Use an internal MFC function. Pretty easy to remove should the need arise. +extern BOOL PASCAL AfxFullPath(LPSTR lpszPathOut, LPCSTR lpszFileIn); +// but make it easier to! +inline BOOL GetFullPath(LPSTR lpszPathOut, LPCSTR lpszFileIn) + { return AfxFullPath(lpszPathOut, lpszFileIn);} + +BOOL AFXAPI PyAfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2); + +#endif // __filename_h__ + diff --git a/Pythonwin/win32ui.rc b/Pythonwin/win32ui.rc new file mode 100644 index 0000000000..01960d0751 --- /dev/null +++ b/Pythonwin/win32ui.rc @@ -0,0 +1,1257 @@ +//Microsoft Developer Studio generated resource script. +// +#include "reswin32ui.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 + "reswin32ui.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""afxprint.rc"" // printing/print preview resources\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "RES\\IDR_MAIN.ICO" +IDR_TEXTTYPE ICON DISCARDABLE "RES\\PADDOC.ICO" +IDR_PYTHONTYPE ICON DISCARDABLE "RES\\IDR_PYTH.ICO" +IDR_DEBUGGER ICON DISCARDABLE "RES\\DEBUGGER.ICO" +IDR_PYTHONCONTYPE ICON DISCARDABLE "RES\\pycon.ico" +IDR_PYTHONREMOVE ICON DISCARDABLE "RES\\ICO00002.ICO" +IDR_PYC ICON DISCARDABLE "res\\pyc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_TEXTTYPE BITMAP MOVEABLE PURE "RES\\temp.BMP" +IDB_HIERFOLDERS BITMAP DISCARDABLE "RES\\HIERFOLD.BMP" +IDB_BROWSER_HIER BITMAP DISCARDABLE "RES\\BROWSER.BMP" +IDR_MAINFRAME BITMAP DISCARDABLE "res\\toolbar.bmp" +IDR_DEBUGGER BITMAP DISCARDABLE "RES\\toolbar_debugger.bmp" +IDB_DEBUGGER_HIER BITMAP DISCARDABLE "RES\\debugger_stack.BMP" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Locate...\tCtrl+L", ID_FILE_LOCATE + MENUITEM "&Run...\tCtrl+R", ID_FILE_RUN + POPUP "&Debug" + BEGIN + MENUITEM "&Go", IDC_DBG_GO + MENUITEM "Step &in", IDC_DBG_STEP + MENUITEM "Step &out", IDC_DBG_STEPOUT + MENUITEM "&Stop", IDC_DBG_CLOSE + END + MENUITEM SEPARATOR + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&View" + BEGIN + POPUP "&Toolbars" + BEGIN + MENUITEM "&Standard", ID_VIEW_TOOLBAR + MENUITEM "&Debugging", ID_VIEW_TOOLBAR_DBG + END + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Interactive Window", ID_VIEW_INTERACTIVE + MENUITEM SEPARATOR + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Help" + BEGIN + MENUITEM "&Python Manuals", ID_HELP_PYTHON + MENUITEM "Python&Win Reference", ID_HELP_GUI_REF + POPUP "&Other" + BEGIN + MENUITEM "Other help files", ID_HELP_OTHER + END + MENUITEM SEPARATOR + MENUITEM "&About PythonWin...", ID_APP_ABOUT + END +END + +IDR_TEXTTYPE MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Locate...\tCtrl+L", ID_FILE_LOCATE + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM "Sa&ve All\tCtrl+Shift+S", ID_FILE_SAVE_ALL + MENUITEM SEPARATOR + MENUITEM "&Run...\tCtrl+R", ID_FILE_RUN + POPUP "&Debug" + BEGIN + MENUITEM "&Go", IDC_DBG_GO + MENUITEM "Step &in", IDC_DBG_STEP + MENUITEM "Step &out", IDC_DBG_STEPOUT + MENUITEM "&Stop", IDC_DBG_CLOSE + END + MENUITEM "&Import..\tCtrl+I", ID_FILE_IMPORT + MENUITEM "Chec&k\tCtrl+Shift+C", ID_FILE_CHECK + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "Page Se&tup...", ID_PAGE_SETUP + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit\tAlt+F4", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + MENUITEM "&Delete\tDel", ID_EDIT_CLEAR + MENUITEM SEPARATOR + MENUITEM "&Find...\tAlt+F3", ID_EDIT_FIND + MENUITEM "Find &Next\tF3", ID_EDIT_REPEAT + MENUITEM "&Replace...", ID_EDIT_REPLACE + MENUITEM SEPARATOR + MENUITEM "&Goto Line...\tCtrl+G", ID_EDIT_GOTO_LINE + MENUITEM "Select &All", ID_EDIT_SELECT_ALL + END + POPUP "&View" + BEGIN + POPUP "&Toolbars" + BEGIN + MENUITEM "&Standard", ID_VIEW_TOOLBAR + MENUITEM "&Debugging", ID_VIEW_TOOLBAR_DBG + END + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Interactive Window", ID_VIEW_INTERACTIVE + MENUITEM SEPARATOR + MENUITEM "&Whitespace", ID_VIEW_WHITESPACE + MENUITEM "&Fixed Font", ID_VIEW_FIXED_FONT + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Window" + BEGIN + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + END + POPUP "&Help" + BEGIN + MENUITEM "&Python Manuals", ID_HELP_PYTHON + MENUITEM "Python&Win Reference", ID_HELP_GUI_REF + POPUP "&Other" + BEGIN + MENUITEM "Other help files", ID_HELP_OTHER + END + MENUITEM SEPARATOR + MENUITEM "&About PythonWin...", ID_APP_ABOUT + END +END + +IDR_PYTHONTYPE MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Locate...\tCtrl+L", ID_FILE_LOCATE + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM "Sa&ve All\tCtrl+Shift+S", ID_FILE_SAVE_ALL + MENUITEM SEPARATOR + MENUITEM "&Run...\tCtrl+R", ID_FILE_RUN + POPUP "&Debug" + BEGIN + MENUITEM "&Go", IDC_DBG_GO + MENUITEM "Step &in", IDC_DBG_STEP + MENUITEM "Step &out", IDC_DBG_STEPOUT + MENUITEM "&Stop", IDC_DBG_CLOSE + END + MENUITEM "&Import..\tCtrl+I", ID_FILE_IMPORT + MENUITEM "Chec&k\tCtrl+Shift+C", ID_FILE_CHECK + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "Page Se&tup...", ID_PAGE_SETUP + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit\tAlt+F4", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + MENUITEM "&Delete\tDel", ID_EDIT_CLEAR + MENUITEM SEPARATOR + MENUITEM "&Find...\tAlt+F3", ID_EDIT_FIND + MENUITEM "Find &Next\tF3", ID_EDIT_REPEAT + MENUITEM "&Replace...", ID_EDIT_REPLACE + MENUITEM SEPARATOR + MENUITEM "&Goto Line...\tCtrl+G", ID_EDIT_GOTO_LINE + MENUITEM "Select &All", ID_EDIT_SELECT_ALL + MENUITEM "Select &block", ID_EDIT_SELECT_BLOCK + END + POPUP "&View" + BEGIN + POPUP "&Toolbars" + BEGIN + MENUITEM "&Standard", ID_VIEW_TOOLBAR + MENUITEM "&Debugging", ID_VIEW_TOOLBAR_DBG + END + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Interactive Window", ID_VIEW_INTERACTIVE + MENUITEM SEPARATOR + MENUITEM "&Whitespace", ID_VIEW_WHITESPACE + MENUITEM "&Fixed Font", ID_VIEW_FIXED_FONT + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Window" + BEGIN + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + END + POPUP "&Help" + BEGIN + MENUITEM "&Python Manuals", ID_HELP_PYTHON + MENUITEM "Python&Win Reference", ID_HELP_GUI_REF + POPUP "&Other" + BEGIN + MENUITEM "Other help files", ID_HELP_OTHER + END + MENUITEM SEPARATOR + MENUITEM "&About PythonWin...", ID_APP_ABOUT + END +END + +IDR_PYTHONTYPE_CNTR_IP MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Locate...\tCtrl+L", ID_FILE_LOCATE + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "&Run...\tCtrl+R", ID_FILE_RUN + MENUITEM "&Import..\tCtrl+I", ID_FILE_IMPORT + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "Page Se&tup...", ID_PAGE_SETUP + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit\tAlt+F4", ID_APP_EXIT + END + MENUITEM SEPARATOR + MENUITEM SEPARATOR + POPUP "&Window" + BEGIN + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + END +END + +IDR_SHELLTRAY MENU DISCARDABLE +BEGIN + POPUP "Shell Icon" + BEGIN + MENUITEM "Activate", ID_SHELL_ACTIVATE + MENUITEM "Break into running code", ID_SHELL_BREAK + END +END + +IDR_CNTR_INPLACE MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + MENUITEM SEPARATOR + MENUITEM SEPARATOR +END + +IDR_DEBUGGER MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Locate...\tCtrl+L", ID_FILE_LOCATE + MENUITEM "&Run...\tCtrl+R", ID_FILE_RUN + POPUP "&Debug" + BEGIN + MENUITEM "&Go", IDC_DBG_GO + MENUITEM "Step &in", IDC_DBG_STEP + MENUITEM "Step &out", IDC_DBG_STEPOUT + MENUITEM "&Stop", IDC_DBG_CLOSE + END + MENUITEM SEPARATOR + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&View" + BEGIN + POPUP "&Toolbars" + BEGIN + MENUITEM "&Standard", ID_VIEW_TOOLBAR + MENUITEM "&Debugging", ID_VIEW_TOOLBAR_DBG + END + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Interactive Window", ID_VIEW_INTERACTIVE + MENUITEM SEPARATOR + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Help" + BEGIN + MENUITEM "&Python Manuals", ID_HELP_PYTHON + MENUITEM "Python&Win Reference", ID_HELP_GUI_REF + POPUP "&Other" + BEGIN + MENUITEM "Other help files", ID_HELP_OTHER + END + MENUITEM SEPARATOR + MENUITEM "&About PythonWin...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "B", ID_VIEW_BROWSE, VIRTKEY, CONTROL, NOINVERT + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + "I", ID_FILE_IMPORT, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_LOCATE, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_EDIT_REPEAT, VIRTKEY, NOINVERT + VK_F3, ID_EDIT_FIND, VIRTKEY, ALT, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + +IDR_PYTHONTYPE ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "B", ID_VIEW_BROWSE, VIRTKEY, CONTROL, NOINVERT + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "C", ID_FILE_CHECK, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + "I", ID_FILE_IMPORT, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_LOCATE, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE_ALL, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_EDIT_REPEAT, VIRTKEY, NOINVERT + VK_F3, ID_EDIT_FIND, VIRTKEY, ALT, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + +IDR_TEXTTYPE ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "B", ID_VIEW_BROWSE, VIRTKEY, CONTROL, NOINVERT + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "C", ID_FILE_CHECK, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + "I", ID_FILE_IMPORT, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_LOCATE, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE_ALL, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_EDIT_REPEAT, VIRTKEY, NOINVERT + VK_F3, ID_EDIT_FIND, VIRTKEY, ALT, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + +IDR_DEBUGGER ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "B", ID_VIEW_BROWSE, VIRTKEY, CONTROL, NOINVERT + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT + "I", ID_FILE_IMPORT, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_LOCATE, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RUN, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F3, ID_EDIT_REPEAT, VIRTKEY, NOINVERT + VK_F3, ID_EDIT_FIND, VIRTKEY, ALT, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PAGE_SETUP DIALOG DISCARDABLE 0, 0, 195, 73 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Page Setup" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Header:",IDC_STATIC,7,8,30,9 + EDITTEXT IDC_HEADER,37,7,106,13,ES_AUTOHSCROLL + CONTROL "File Time",IDC_HEADER_FILE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,6,23,49,10 + CONTROL "System Time",IDC_HEADER_SYSTEM,"Button", + BS_AUTORADIOBUTTON,58,23,61,10 + LTEXT "&Footer:",IDC_STATIC,7,41,29,9 + EDITTEXT IDC_FOOTER,37,40,106,13,ES_AUTOHSCROLL + CONTROL "File Time",IDC_FOOTER_FILE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,6,56,49,10 + CONTROL "System Time",IDC_FOOTER_SYSTEM,"Button", + BS_AUTORADIOBUTTON,58,56,61,10 + DEFPUSHBUTTON "OK",IDOK,147,5,42,14 + PUSHBUTTON "Cancel",IDCANCEL,147,23,42,14 +END + +IDD_SET_TABSTOPS DIALOG DISCARDABLE 3, 4, 161, 46 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Set Tab Stops" +FONT 8, "Helv" +BEGIN + LTEXT "&Tab Stops:",IDC_PROMPT_TABS,6,14,37,8 + EDITTEXT IDC_EDIT_TABS,50,12,49,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,111,4,40,15 + PUSHBUTTON "Cancel",IDCANCEL,111,23,40,14 +END + +IDD_TREE_MB DIALOG DISCARDABLE 0, 0, 226, 103 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "title" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,170,5,50,14 + PUSHBUTTON "Cancel",IDCANCEL,170,25,50,14 + PUSHBUTTON "Button 1",IDC_BUTTON1,170,45,50,14 + PUSHBUTTON "Button 2",IDC_BUTTON2,170,65,50,14 + PUSHBUTTON "Button 3",IDC_BUTTON3,170,85,50,14 + CONTROL "Tree1",IDC_LIST1,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | + WS_BORDER | WS_TABSTOP,3,3,160,94 +END + +IDD_TREE DIALOG DISCARDABLE 0, 0, 226, 93 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "title" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,170,5,50,14 + PUSHBUTTON "Cancel",IDCANCEL,170,25,50,14 + CONTROL "Tree1",IDC_LIST1,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | + WS_BORDER | WS_TABSTOP,3,3,160,88 +END + +IDD_SIMPLE_INPUT DIALOG DISCARDABLE 3, 4, 219, 46 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Title" +FONT 8, "Helv" +BEGIN + CTEXT "&Prompt:",IDC_PROMPT1,0,14,70,26 + EDITTEXT IDC_EDIT1,70,12,92,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,171,5,40,15 + PUSHBUTTON "Cancel",IDCANCEL,171,23,40,14 +END + +IDD_PROPDEMO2 DIALOG DISCARDABLE 0, 0, 164, 96 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Page 2" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Option I",IDC_DEMO1,"Button",BS_AUTORADIOBUTTON | + BS_NOTIFY | WS_GROUP,10,10,59,11 + CONTROL "Option II",IDC_DEMO2,"Button",BS_AUTORADIOBUTTON | + BS_NOTIFY,10,24,68,10 + CONTROL "Option III",IDC_DEMO3,"Button",BS_AUTORADIOBUTTON | + BS_NOTIFY,10,39,60,10 +END + +IDD_ABOUTBOX DIALOGEX 34, 22, 228, 116 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Pythonwin" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + ICON IDR_PYTHONTYPE,ID_HELP_PYTHON,154,93,20,20 + DEFPUSHBUTTON "OK",IDOK,184,94,37,14,WS_GROUP + LTEXT "Version",IDC_ABOUT_VERSION,7,96,130,8 + CONTROL "",IDC_EDIT1,"RICHEDIT",ES_MULTILINE | WS_BORDER | + WS_VSCROLL | WS_TABSTOP,4,4,219,85,WS_EX_TRANSPARENT + LTEXT "There is an icon here, it just wont show :-(", + IDC_STATIC,114,89,49,25 +END + +IDD_DUMMYPROPPAGE DIALOG DISCARDABLE 0, 0, 185, 92 +STYLE WS_CHILD | WS_VISIBLE | WS_CAPTION +FONT 8, "MS Sans Serif" +BEGIN +END + +IDD_RUN_SCRIPT DIALOG DISCARDABLE 3, 4, 251, 63 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Run Script" +FONT 8, "Helv" +BEGIN + LTEXT "&Script File",IDC_PROMPT1,5,7,45,10 + EDITTEXT IDC_EDIT1,55,5,140,12,ES_AUTOHSCROLL + LTEXT "&Arguments",IDC_PROMPT2,5,25,45,10 + EDITTEXT IDC_EDIT2,55,23,140,12,ES_AUTOHSCROLL + LTEXT "&Debugging",IDC_STATIC,5,44,45,10 + COMBOBOX IDC_COMBO1,55,41,140,59,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Browse...",IDC_BUTTON2,205,5,40,12 + DEFPUSHBUTTON "OK",IDOK,205,23,40,15 + PUSHBUTTON "Cancel",IDCANCEL,205,45,40,14 +END + +IDD_LARGE_EDIT DIALOG DISCARDABLE 0, 0, 257, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Title" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT1,5,10,245,120,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "Cancel",IDCANCEL,140,135,50,14,NOT WS_VISIBLE + DEFPUSHBUTTON "OK",IDOK,200,135,50,14 +END + +IDD_GENERAL_STATUS DIALOG DISCARDABLE 0, 0, 237, 49 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,180,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,180,24,50,14 + CONTROL "",IDC_PROMPT1,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,7,7, + 169,10 + CONTROL "",IDC_PROMPT2,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,7, + 18,169,10 + CONTROL "",IDC_PROMPT3,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,7, + 30,169,10 +END + +IDD_PROPDEMO1 DIALOG DISCARDABLE 0, 0, 193, 71 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Page 1" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT1,84,4,104,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT2,84,20,104,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT3,84,36,104,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT4,84,52,104,12,ES_AUTOHSCROLL + LTEXT "Item 1",IDC_PROMPT1,7,6,68,8 + LTEXT "Item 2",IDC_PROMPT2,7,22,68,8,NOT WS_GROUP + LTEXT "Item 3",IDC_PROMPT3,7,38,68,8,NOT WS_GROUP + LTEXT "Item 4",IDC_PROMPT4,7,54,68,8,NOT WS_GROUP +END + +IDD_PP_EDITOR DIALOG DISCARDABLE 0, 0, 255, 121 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Editor" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Python",IDC_STATIC,7,7,123,41 + CONTROL "View &Whitespace",IDC_VIEW_WHITESPACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,15,105,9 + CONTROL "&Auto-complete attributes",IDC_AUTOCOMPLETE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,26,105,9 + CONTROL "Show &calltips",IDC_CALLTIPS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,37,105,9 + GROUPBOX "Indenting",IDC_STATIC,7,52,123,62 + CONTROL "&Use TAB characters?",IDC_USE_TABS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,60,100,10 + CONTROL "Use &Python smart tabs?",IDC_USE_SMART_TABS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,70,100,10 + LTEXT "&Tab size",IDC_STATIC,14,82,42,13 + EDITTEXT IDC_TAB_SIZE,72,82,42,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_HOTTRACK,114,80,11,14 + LTEXT "&Indent Size",IDC_STATIC,14,98,42,10 + EDITTEXT IDC_INDENT_SIZE,72,97,42,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | + UDS_HOTTRACK,114,97,11,14 + GROUPBOX "Editor",IDC_STATIC,135,7,113,41 + GROUPBOX "External File Support",IDC_STATIC,135,52,113,62 + CONTROL "Auto &reload changed file",IDC_AUTO_RELOAD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,142,59,98,11 + CONTROL "&VSS Integration",IDC_VSS_INTEGRATE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,142,70,98,11 + LTEXT "&Backups",IDC_STATIC,142,83,32,10 + COMBOBOX IDC_COMBO1,191,83,49,41,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + CONTROL "&Color Editor",IDC_EDITOR_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,142,17,51,10 + COMBOBOX IDC_KEYBOARD_CONFIG,193,33,47,37,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Keyboard",IDC_STATIC,142,33,32,10 +END + +IDD_PP_IDE DIALOG DISCARDABLE 0, 0, 255, 117 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Size of recent file list",IDC_STATIC,7,14,73,12 + EDITTEXT IDC_EDIT4,87,12,29,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,119,15,10, + 14 + GROUPBOX "Interactive Window",IDC_STATIC,134,7,114,103 + CONTROL "&Show at startup",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,141,16,72,10 + CONTROL "",IDC_EDIT1,"RICHEDIT",ES_AUTOHSCROLL | WS_BORDER | + WS_TABSTOP,141,54,57,16 + PUSHBUTTON "Change...",IDC_BUTTON1,203,56,39,13 + CONTROL "",IDC_EDIT2,"RICHEDIT",ES_AUTOHSCROLL | WS_BORDER | + WS_TABSTOP,141,70,57,16 + PUSHBUTTON "Change...",IDC_BUTTON2,203,72,39,13 + CONTROL "",IDC_EDIT3,"RICHEDIT",ES_AUTOHSCROLL | WS_BORDER | + WS_TABSTOP,141,88,58,16 + PUSHBUTTON "Change...",IDC_BUTTON3,203,89,39,13 + CONTROL "&Dockable window",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,141,27,72,10 +END + +1538 DIALOG DISCARDABLE 36, 52, 225, 148 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "My Print" +FONT 8, "Helv" +BEGIN + LTEXT "Printer:",1093,4,4,40,8 + LTEXT "System Default",1088,44,4,120,18 + GROUPBOX "Print Range",1072,4,27,132,67 + RADIOBUTTON "&All",1056,10,39,76,12,WS_GROUP | WS_TABSTOP + RADIOBUTTON "S&election",1057,10,52,76,12 + RADIOBUTTON "&Pages",1058,10,65,76,12 + RTEXT "&From:",1089,24,80,24,9 + EDITTEXT 1152,52,80,26,12,ES_RIGHT + RTEXT "&To:",1090,82,80,16,9 + EDITTEXT 1153,102,78,26,12,ES_RIGHT + LTEXT "Print &Quality:",1091,4,100,50,9 + COMBOBOX 1136,55,98,81,36,CBS_DROPDOWNLIST | WS_BORDER | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + LTEXT "&Copies:",1092,153,100,29,9 + EDITTEXT 1154,184,98,26,12,ES_RIGHT + CONTROL "Print to Fi&le",1040,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,4,113,120,12 + CONTROL "Collate Cop&ies",1041,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,153,113,67,12 + DEFPUSHBUTTON "OK",IDOK,170,4,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,170,21,50,14,WS_GROUP + PUSHBUTTON "&Setup...",1024,170,41,50,14,WS_GROUP + PUSHBUTTON "&Help",1038,170,75,50,14,WS_GROUP + LTEXT "Magnification:",IDC_STATIC,4,132,45,8 + EDITTEXT IDC_ZZ_PRINT_MAG_EDIT,52,130,26,12,ES_AUTOHSCROLL +END + +IDD_PP_FORMAT DIALOG DISCARDABLE 0, 0, 255, 125 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Format" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST1,130,5,120,75,LBS_SORT | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Format",IDC_STATIC,130,81,120,41 + CONTROL "Fixed Width",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,15,91,55,10 + GROUPBOX "Default Font",IDC_STATIC,7,81,120,41 + PUSHBUTTON "Define...",IDC_BUTTON1,78,90,44,12 + CONTROL "Proportional",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,15, + 107,55,8 + PUSHBUTTON "Define...",IDC_BUTTON2,78,107,44,12 + PUSHBUTTON "Define...",IDC_BUTTON3,200,107,44,12 + CONTROL "Use default",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,134,107,55,8 + COMBOBOX IDC_COMBO1,134,90,50,63,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_COMBO2,194,90,50,63,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_PP_DEBUGGER DIALOG DISCARDABLE 0, 0, 251, 102 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Debugger" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "General Options",-1,5,2,122,31 + CONTROL "Hide debugger in non-GUI apps",IDC_CHECK1,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,9,114,11 + CONTROL "Stop at exceptions",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,12,20,75,11 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + BUTTON ID_FILE_SAVE_ALL + SEPARATOR + BUTTON ID_FILE_IMPORT + BUTTON ID_FILE_RUN + BUTTON ID_FILE_CHECK + BUTTON ID_VIEW_INTERACTIVE + SEPARATOR + BUTTON ID_EDIT_UNDO + BUTTON ID_EDIT_REDO + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + SEPARATOR + BUTTON ID_HELP_INDEX + SEPARATOR + BUTTON ID_APP_ABOUT +END + +IDR_DEBUGGER TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON IDC_DBG_WATCH + BUTTON IDC_DBG_STACK + BUTTON IDC_DBG_BREAKPOINTS + SEPARATOR + BUTTON IDC_DBG_ADD + BUTTON IDC_DBG_CLEAR + SEPARATOR + BUTTON IDC_DBG_STEP + BUTTON IDC_DBG_STEPOVER + BUTTON IDC_DBG_STEPOUT + SEPARATOR + BUTTON IDC_DBG_GO + BUTTON IDC_DBG_CLOSE +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + BOTTOMMARGIN, 113 + END + + IDD_GENERAL_STATUS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 230 + TOPMARGIN, 7 + BOTTOMMARGIN, 42 + END + + IDD_PP_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 14 + VERTGUIDE, 114 + VERTGUIDE, 130 + VERTGUIDE, 135 + VERTGUIDE, 142 + VERTGUIDE, 193 + VERTGUIDE, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 114 + HORZGUIDE, 17 + HORZGUIDE, 33 + HORZGUIDE, 48 + HORZGUIDE, 52 + HORZGUIDE, 70 + HORZGUIDE, 83 + HORZGUIDE, 97 + END + + IDD_PP_IDE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 110 + END + + IDD_PP_FORMAT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 250 + VERTGUIDE, 244 + TOPMARGIN, 5 + BOTTOMMARGIN, 122 + HORZGUIDE, 90 + HORZGUIDE, 107 + END + + IDD_PP_DEBUGGER, DIALOG + BEGIN + RIGHTMARGIN, 249 + VERTGUIDE, 5 + VERTGUIDE, 12 + VERTGUIDE, 127 + VERTGUIDE, 205 + HORZGUIDE, 100 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_PP_EDITOR DLGINIT +BEGIN + IDC_COMBO1, 0x403, 8, 0 +0x774f, 0x206e, 0x6964, 0x0072, + IDC_COMBO1, 0x403, 10, 0 +0x422e, 0x4b41, 0x6620, 0x6c69, 0x0065, + IDC_COMBO1, 0x403, 9, 0 +0x4554, 0x504d, 0x6420, 0x7269, "\000" + IDC_COMBO1, 0x403, 5, 0 +0x6f4e, 0x656e, "\000" + 0 +END + +IDD_PP_FORMAT DLGINIT +BEGIN + IDC_COMBO2, 0x403, 8, 0 +0x6552, 0x7567, 0x616c, 0x0072, + IDC_COMBO2, 0x403, 7, 0 +0x7449, 0x6c61, 0x6369, "\000" + IDC_COMBO2, 0x403, 5, 0 +0x6f42, 0x646c, "\000" + IDC_COMBO2, 0x403, 12, 0 +0x6f42, 0x646c, 0x4920, 0x6174, 0x696c, 0x0063, + 0 +END + +IDD_RUN_SCRIPT DLGINIT +BEGIN + IDC_COMBO1, 0x403, 13, 0 +0x6f4e, 0x6420, 0x6265, 0x6775, 0x6967, 0x676e, "\000" + IDC_COMBO1, 0x403, 29, 0 +0x7453, 0x7065, 0x742d, 0x7268, 0x756f, 0x6867, 0x6920, 0x206e, 0x6874, +0x2065, 0x6564, 0x7562, 0x6767, 0x7265, "\000" + IDC_COMBO1, 0x403, 20, 0 +0x7552, 0x206e, 0x6e69, 0x7420, 0x6568, 0x6420, 0x6265, 0x6775, 0x6567, +0x0072, + IDC_COMBO1, 0x403, 29, 0 +0x6544, 0x7562, 0x2067, 0x6669, 0x7520, 0x686e, 0x6e61, 0x6c64, 0x6465, +0x6520, 0x6378, 0x7065, 0x6974, 0x6e6f, "\000" + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "Pythonwin" + IDR_PYTHONTYPE "\nScript\nPython Script\nPython scripts (*.py)\n.py\nPythonScript\nPython Script" + IDR_TEXTTYPE "\nScript\nPython Script\nPython Script (*.py)\n.py\nTEXT Document\nText Files (*.txt)\n.txt\nTextFileType\nTEXT File Type" + IDR_DEBUGGER "Pythonwin Debugger" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "Python for Win32" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new file\nNew" + ID_FILE_OPEN "Open an existing file\nOpen" + ID_FILE_CLOSE "Close the active file\nClose" + ID_FILE_SAVE "Save the active file\nSave" + ID_FILE_SAVE_AS "Save the active file with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save files\nExit" + ID_HELP_INDEX "Display an index of all the help files.\nHelp Index" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this file" + ID_FILE_MRU_FILE2 "Open this file" + ID_FILE_MRU_FILE3 "Open this file" + ID_FILE_MRU_FILE4 "Open this file" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Window" + ID_PREV_PANE "Switch back to the previous window pane\nPrev Window" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_NEW "Open another window for the active file\nNew Window" + ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" + ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade" + ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile" + ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nClear" + ID_EDIT_CLEAR_ALL "Erase everything\nClear All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_GOTO_LINE "Goto a specific line in the file\nGoto line" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire file\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_RUN "Run a python script.\nRun" + ID_PAGE_SETUP "Set headers, footers, and margins\nPage Setup" + ID_CHOOSE_PRINT_FONT "Choose a font for printing\nPrinter Font" + ID_FILE_IMPORT "Import/reload a Python module\nImport/Reload" + ID_FILE_LOCATE "Locate a Python script (search down the Python path)\nLocate Script" + ID_VIEW_BROWSE "Browse a Python object.\nBrowse" + ID_HELP_GUI_REF "PythonWin Reference Guide/PythonWin Ref." + ID_MIRROR_DISPLAY_FONT "Use same font for printing as for display" + ID_HELP_PYTHON "Open the Python documentation.\nPython Manuals" + ID_VIEW_INTERACTIVE "Show or hide the interactive window.\nInteractive window" + ID_VIEW_WHITESPACE "Show or hide whitespace\nView whitespace" + ID_EDIT_SELECT_BLOCK "Select the current code or output block\nSelect Block" + ID_DEBUGGER_STOP "Stop the Python code.\nStop" + ID_VIEW_OPTIONS "View and change various Pythonwin options." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nShow/Hide Toolbar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nShow/Hide Statusbar" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR_DBG "Show or hide the debugging toolbar\nDebugging Toolbar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" + AFX_IDS_MDICHILD "Activate this window" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CHOOSE_FONT "Select display font for this window" + ID_SET_TABSTOPS "Set tab stops for this window" + ID_VIEW_FIXED_FONT "Toggle fixed or proportional fonts\nFixed/Proportional Font" + ID_WORD_WRAP "Toggle on/off word-wrap mode" + ID_INDICATOR_COLNUM "888" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDP_ERR_GET_PRINTER_DC "Cannot get printer device context." + IDP_ERR_GET_DEVICE_DEFAULTS "Cannot get printer device defaults." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_LINENUM "88888" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_HELP_OTHER "Other Python help files." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_CHECK "Checks the current file without executing it.\nCheck" + ID_FILE_SAVE_ALL "Save all open files\nSave All" + IDC_DBG_STACK "Show/Hide the stack view\nStack view" + IDC_DBG_BREAKPOINTS "Show/Hide the breakpoints list\nBreakpoint list" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDC_DBG_STEPOUT "Step out of the current function\nStep Out" + IDC_DBG_STEPOVER "Step over the current function\nStep Over" + IDC_DBG_GO "Continue Execution\nGo" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDC_DBG_ADD "Add/Remove a breakpoint\nAdd/Remove" + IDC_DBG_CLEAR "Clear all breakpoints\nClear All" + IDC_DBG_CLOSE "Close the debugger\nClose" + IDC_DBG_STEP "Step into the current statement\nStep" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDC_DBG_WATCH "Add/Remove a watch entry\nWatch" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PP_TOOLMENU DIALOG DISCARDABLE 0, 0, 246, 121 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Tools Menu" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "List1",IDC_LIST1,"SysListView32",LVS_REPORT | + LVS_EDITLABELS | WS_BORDER | WS_TABSTOP,7,7,174,90 + LTEXT "Python command",IDC_STATIC,7,103,65,10 + EDITTEXT IDC_EDIT2,85,101,154,12,ES_AUTOHSCROLL + PUSHBUTTON "Move &up",IDC_BUTTON1,189,7,50,14 + PUSHBUTTON "Move &down",IDC_BUTTON2,189,24,50,14 + PUSHBUTTON "&New",IDC_BUTTON3,189,65,50,14 + PUSHBUTTON "De&lete",IDC_BUTTON4,189,81,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PP_TOOLMENU, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 239 + VERTGUIDE, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 113 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "afxres.rc" // Standard components +#include "afxprint.rc" // printing/print preview resources + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Pythonwin/win32uiExt.h b/Pythonwin/win32uiExt.h new file mode 100644 index 0000000000..cb2a0cf52c --- /dev/null +++ b/Pythonwin/win32uiExt.h @@ -0,0 +1,981 @@ +/***************************************************************** + +The template classes for Pythonwin support. + +Typical usage of these classes are: + +// Define my class which extends the MFC view support. +class CMyView : public CView { + int SomeMethod(); + ... etc +}; + +// Now define a class which has all the hooks to make it +// a class fully extendable by Python. +typedef CPythonViewFramework< CMyView > CPythonMyView; + +// Now in the C++ sources, we always make sure we create +// the CPythonMyView... + + CMyView *pMyView = new CPythonMyView(args...); + +*****************************************************************/ +// @doc +#pragma once // Only ever want this once +#ifndef __WIN32UIEXT_H__ +#define __WIN32UIEXT_H__ + +template +class CPythonWndFramework : public T +{ +public: + + // EEEK - It seem necessary to have the _union_ of all possible base class ctors. + // The ctors seem to only be referenced when used, so they dont worry classes that dont use them?? + // What a pain - anyone know how to avoid???? + + // CWnd + CPythonWndFramework() : T() {;} + // CPropertySheet. + CPythonWndFramework(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) : + T(nIDCaption, pParentWnd, iSelectPage) {;} + CPythonWndFramework(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) : + T(pszCaption, pParentWnd, iSelectPage) {;} + // CPropertyPage + CPythonWndFramework(UINT id, UINT caption) : + T(id, caption) {;} + CPythonWndFramework(LPCTSTR id, UINT caption) : + T(id, caption) {;} + // CFormView + CPythonWndFramework(UINT id) : + T(id) {;} + CPythonWndFramework(LPCTSTR id) : + T(id) {;} + // CCtrlView + CPythonWndFramework(LPCTSTR lpszClass, DWORD dwStyle) : + T(lpszClass, dwStyle) {;} + // CPrintDialog. + CPythonWndFramework(BOOL bPrintSetupOnly, DWORD dwFlags, CWnd* pParentWnd) : + T(bPrintSetupOnly, dwFlags, pParentWnd) {;} + // End of ctor hacks!!!! + + ~CPythonWndFramework() { + Python_delete_assoc(this); // frame dieing - make sure Python knows about it. + } + virtual BOOL 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 T::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo); + } + virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam, LRESULT *pResult) { + // yield to Python first + if (Python_OnNotify (this, wParam, lParam, pResult)) + return TRUE; + else + return T::OnNotify (wParam, lParam, pResult); + } +#ifdef PYWIN_WITH_WINDOWPROC + virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { + LRESULT res; + CVirtualHelper helper( "WindowProc", this ); + if (!helper.HaveHandler() || !helper.call(message, wParam, lParam) || !helper.retval(res)) + return T::WindowProc(message, wParam, lParam); + return res; + } +#endif + virtual BOOL PreTranslateMessage(MSG *pMsg) { + // @pyvirtual tuple|PyCWnd|PreTranslateMessage|Allows a Window to override the PreTranslateMessage handling. + // @pyparm tuple|msg||Built from a MSG structure using format "iiiii(ii)" + CVirtualHelper helper("PreTranslateMessage", this); + // @rdesc The result should be a tuple of the same format as the msg param, + // in which case the MSG structure will be updated and TRUE returned + // from the C++ function. If None is returned, the default handler + // is called. + if (helper.HaveHandler() && + helper.call(pMsg) && + !helper.retnone() && + helper.retval(pMsg)) + return TRUE; + return _BasePreTranslateMessage(pMsg); + } + BOOL _BasePreTranslateMessage(MSG *pMsg) { + return T::PreTranslateMessage(pMsg); + } + virtual BOOL OnCommand( WPARAM wparam, LPARAM lparam ) + { + // @pyvirtual tuple|PyCWnd|OnCommand|Allows a Window to override the OnCommand handling. + // @pyparm int|wparam|| + // @pyparm int|lparam|| + // @comm The base class method must be called manually via . + // Failure to call the base implementation will prevent all Python command + // handlers (hooked via ). + BOOL ret; + CVirtualHelper helper("OnCommand", this); + if (helper.HaveHandler() && + helper.call(wparam, lparam) && + helper.retval(ret)) + return ret; + return _BaseOnCommand(wparam, lparam); + } + BOOL _BaseOnCommand(WPARAM wparam, LPARAM lparam ) + { + return T::OnCommand(wparam, lparam); + } + virtual BOOL PreCreateWindow(CREATESTRUCT &cs) { + // @pyvirtual BOOL|PyCWnd|PreCreateWindow|Called by the framework before the creation of the Windows window attached to this View object. + // @pyparm tuple|CREATESTRUCT||A tuple describing a CREATESTRUCT structure. + // @xref + CVirtualHelper helper("PreCreateWindow", this); + if (helper.HaveHandler()) { + if (!helper.call(&cs) || !helper.retval(cs)) + return FALSE; + return TRUE; + } else { + return T::PreCreateWindow(cs); + } + } + afx_msg void OnClose() { + // @pyvirtual void|PyCWnd|OnClose|Called for the WM_CLOSE message. + // @comm The default calls DestroyWindow(). If you supply a handler, the default is not called. + // @xref + CVirtualHelper helper( "OnClose", this ); + if (!helper.HaveHandler() || !helper.call()) + _BaseOnClose(); + } + void _BaseOnClose() { + T::OnClose(); + } + afx_msg void OnPaletteChanged(CWnd* pFocusWnd) { + // @comm The MFC base class is always called before the Python method. + T::OnPaletteChanged(pFocusWnd); + // @pyvirtual |PyCWnd|OnPaletteChanged|Called to allow windows that use a color palette to realize their logical palettes and update their client areas. + CVirtualHelper helper( "OnPaletteChanged", this ); + // @pyparm |focusWnd||The window that caused the system palette to change. + helper.call(pFocusWnd); + } + afx_msg void OnPaletteIsChanging(CWnd* pRealizeWnd) { + T::OnPaletteIsChanging(pRealizeWnd); + // @pyvirtual |PyCWnd|OnPaletteIsChanging|Informs other applications when an application is going to realize its logical palette. + CVirtualHelper helper( "OnPaletteIsChanging", this ); + // @pyparm |realizeWnd||Specifies the window that is about to realize its logical palette. + // @comm The MFC base class is always called before the Python method. + helper.call(pRealizeWnd); + } + afx_msg void OnWinIniChange(LPCTSTR lpszSection) { + T::OnWinIniChange(lpszSection); + // @pyvirtual |PyCWnd|OnWinIniChange|Called when the system configuration changes. + // @pyparm string|section||The section which changed. + // @comm The MFC base class is always called before the Python method. + CVirtualHelper helper ("OnWinIniChange", this); + helper.call(lpszSection); + } + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { + // @pyvirtual int|PyCWnd|OnCtlColor|Called for the WM_CTLCOLOR message. + // @comm Setup dc to paint the control pWnd of type nCtlColor. + // @comm The default calls OnCtlColor(). If you supply a handler, the default is not called. + // @rdesc Handle of the brush to paint the control's background. + // @xref + CVirtualHelper helper( "OnCtlColor", this ); + int hRet; + if (helper.HaveHandler() && helper.call(pDC, pWnd, nCtlColor) && + helper.retval(hRet)) + return (HBRUSH)hRet; + return T::OnCtlColor(pDC, pWnd, nCtlColor); + } + afx_msg void OnSysColorChange() { + T::OnSysColorChange(); + // @pyvirtual |PyCWnd|OnSysColorChange|Called for all top-level windows when a change is made in the system color setting. + // @comm The MFC base class is always called before the Python method. + CVirtualHelper helper ("OnSysColorChange", this); + helper.call(); + } + virtual BOOL OnEraseBkgnd(CDC* pDC) { + // @pyvirtual int|PyCWnd|OnEraseBkgnd|Called for the WN_ERASEBACKGROUND message. + // @rdesc Nonzero if it erases the background; otherwise 0. + // @xref + CVirtualHelper helper( "OnEraseBkgnd", this ); + if (!helper.HaveHandler()) + return T::OnEraseBkgnd(pDC); + BOOL bRet = FALSE; + // @pyparm |dc||The device context. + if (helper.call(pDC)) + helper.retval(bRet); + return bRet; + } + afx_msg BOOL OnQueryNewPalette() { + // @pyvirtual BOOL|PyCWnd|OnQueryNewPalette|Informs the window it is about to receive input focus, and shoudl realize its logical palette. + // @comm The base class method must be called manually via + // @xref + BOOL ret; + CVirtualHelper helper( "OnQueryNewPalette", this ); + if (helper.call() && helper.retval(ret)) + return ret; + return FALSE; + } + afx_msg void OnPaint() { + // @pyvirtual int|PyCWnd|OnPaint|Default message handler. + // @xref + CVirtualHelper helper( "OnPaint", this ); + if (!helper.HaveHandler() || !helper.call()) + T::OnPaint(); + } + afx_msg HCURSOR OnQueryDragIcon() { + // @pyvirtual int|PyCWnd|OnQueryDragIcon|Called for the WM_QUERYDRAGICON message. + // @rdesc The result is an integer containing a HCURSOR for the icon. + // @xref + CVirtualHelper helper( "OnQueryDragIcon", this ); + if (!helper.HaveHandler()) + return T::OnQueryDragIcon(); + int ret; + if (!helper.call()) + return NULL; + helper.retval(ret); + return (HCURSOR)ret; + } + afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ) { + // @pyvirtual int|PyCWnd|OnCreate|Called for the WM_CREATE message. + // @rdesc The result is an integer indicating if the window should be created. + // @comm The MFC implementation is always called before Python. + if (T::OnCreate(lpCreateStruct)) + return -1; + CVirtualHelper helper( "OnCreate", this ); + int ret = 0; + if (helper.call(lpCreateStruct)) + helper.retval(ret); + return ret; + } + afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) { + // @pyvirtual |PyCWnd|OnNcCalcSize|Called for the WM_NCCALCSIZE message. + CVirtualHelper helper( "OnNcCalcSize", this ); + if (helper.HaveHandler()) { + if (bCalcValidRects) { + PyObject *rc1 = PyWinObject_FromRECT(&lpncsp->rgrc[0], false); + PyObject *rc2 = PyWinObject_FromRECT(&lpncsp->rgrc[1], false); + PyObject *rc3 = PyWinObject_FromRECT(&lpncsp->rgrc[2], false); + WINDOWPOS *pwp = lpncsp->lppos; + PyObject *obPos; + if (pwp==NULL) { + obPos = Py_None; + Py_INCREF(obPos); + } else + obPos = Py_BuildValue("iiiiiii", pwp->hwnd, pwp->hwndInsertAfter, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + PyObject *args = Py_BuildValue("i(OOOO)",bCalcValidRects, rc1, rc2, rc3, obPos); + Py_XDECREF(rc1); + Py_XDECREF(rc2); + Py_XDECREF(rc3); + Py_XDECREF(obPos); + helper.call_args(args); + } else { + PyObject *rc1 = PyWinObject_FromRECT((RECT *)lpncsp, false); + PyObject *args = Py_BuildValue("i(Ozzz)",bCalcValidRects, rc1, NULL, NULL, NULL); + Py_XDECREF(rc1); + helper.call_args(args); + } + } else + T::OnNcCalcSize(bCalcValidRects, lpncsp); + } + afx_msg UINT OnNcHitTest(CPoint pt) { + // @pyvirtual int|PyCWnd|OnNcHitTest|Called for the WM_NCHITTEST message. + // @xref + CVirtualHelper helper( "OnNcHitTest", this ); + // @pyparm int, int|x,y||The point to test. + if (helper.HaveHandler()) { + PyObject *args = Py_BuildValue("((ii))", pt.x, pt.y); + if (helper.call_args(args)) { + int ret; + if (helper.retval(ret)) + return ret; + } + } + return T::OnNcHitTest(pt); + } + afx_msg UINT OnSetCursor(CWnd *pWnd, UINT ht, UINT msg) { + // @pyvirtual int|PyCWnd|OnSetCursor|Called for the WM_SETCURSOR message. + // @xref + CVirtualHelper helper( "OnSetCursor", this ); + BOOL ret; + // @pyparm |wnd|| + // @pyparm int|hitTest|| + // @pyparm int|msg|| + if (helper.HaveHandler() && helper.call(pWnd, ht, msg)) + helper.retval(ret); + else + ret = T::OnSetCursor(pWnd, ht, msg); + return ret; + } + afx_msg void OnMDIActivate(BOOL bActivate, CWnd* pAc, CWnd *pDe) { + // @pyvirtual int|PyCWnd|OnMDIActivate| + // @comm The MFC implementation is always called before this. + CVirtualHelper helper( "OnMDIActivate", this ); + // @pyparm int|bActivate|| + // @pyparm |wndActivate|| + // @pyparm |wndDeactivate|| + T::OnMDIActivate(bActivate, pAc, pDe); + if (helper.HaveHandler()) { + PyObject *oba, *obd; + if (pAc) { + oba = PyWinObject_FromCWnd(pAc); + } else { + oba = Py_None; + Py_INCREF(Py_None); + } + if (pDe) { + obd = PyWinObject_FromCWnd(pDe); + } else { + obd = Py_None; + Py_INCREF(Py_None); + } + PyObject *args = Py_BuildValue("(iOO)",bActivate, oba, obd); + Py_XDECREF(oba); + Py_XDECREF(obd); + helper.call_args(args); + // decref by helper. + } + } + afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) { + // @pyvirtual int|PyCWnd|OnMouseActivate|Called when the mouse is used to activate a window. + // @xref + CVirtualHelper helper( "OnMouseActivate", this ); + BOOL ret; + // @pyparm |wndDesktop|| + // @pyparm int|hitTest|| + // @pyparm int|msg|| + if (helper.HaveHandler() && helper.call(pDesktopWnd, nHitTest, message)) + helper.retval(ret); + else + ret = _BaseOnMouseActivate(pDesktopWnd, nHitTest, message); + return ret; + } + int _BaseOnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) { + return T::OnMouseActivate(pDesktopWnd, nHitTest, message); + } + +// DECLARE_MESSAGE_MAP() +private: \ + static const AFX_MSGMAP_ENTRY _messageEntries[]; +protected: \ + static AFX_DATA const AFX_MSGMAP messageMap; + + static const AFX_MSGMAP* PASCAL _GetBaseMessageMap() { + return &T::messageMap; + } + virtual const AFX_MSGMAP* GetMessageMap() const { + return &messageMap; + } +}; + +template +AFX_DATADEF const AFX_MSGMAP CPythonWndFramework::messageMap = + { &CPythonWndFramework::_GetBaseMessageMap, &CPythonWndFramework::_messageEntries[0] +}; + +template +const AFX_MSGMAP_ENTRY CPythonWndFramework::_messageEntries[] = { + ON_WM_PALETTECHANGED() + ON_WM_PALETTEISCHANGING() + ON_WM_WININICHANGE() + ON_WM_CTLCOLOR() + ON_WM_SYSCOLORCHANGE() + ON_WM_ERASEBKGND() + ON_WM_QUERYNEWPALETTE() + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_WM_CREATE() + ON_WM_CLOSE() + ON_WM_NCCALCSIZE() + ON_WM_NCHITTEST() + ON_WM_SETCURSOR() + ON_WM_MDIACTIVATE() + ON_WM_MOUSEACTIVATE() + {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } +}; + + +template +class CPythonDlgFramework : public CPythonWndFramework +{ +public: + // ctor hacks! + CPythonDlgFramework() : CPythonWndFramework() {;} + // CPropertyPage + CPythonDlgFramework(UINT id, UINT caption) : + CPythonWndFramework(id, caption) {;} + CPythonDlgFramework(LPCTSTR id, UINT caption) : + CPythonWndFramework(id, caption) {;} + // CPrintDialog + CPythonDlgFramework(BOOL bPrintSetupOnly, DWORD dwFlags, CWnd* pParentWnd) : + CPythonWndFramework(bPrintSetupOnly, dwFlags, pParentWnd) {;} + // End of ctor hacks!!!! + + virtual BOOL OnInitDialog() { + // @pyvirtual int|PyCDialog|OnInitDialog|Override to augment dialog-box initialization. + // @comm The base implementation is not called if a handler exists. This can be + // done via . + // @xref + BOOL result = FALSE; + CVirtualHelper helper( "OnInitDialog", this ); + if (!helper.HaveHandler()) { + result = T::OnInitDialog(); + } else { + if (helper.call()) + helper.retval(result); + } + return result; + // @rdesc Specifies whether the application has set the input focus to + // one of the controls in the dialog box. If OnInitDialog returns + // nonzero, Windows sets the input focus to the first control + // in the dialog box. The application can return 0/None only if + // it has explicitly set the input focus to one of the controls in the + // dialog box. + } + virtual void OnOK() { + // @pyvirtual int|PyCDialog|OnOK|Called by the MFC architecture when the user selects the OK button. + // @comm The procedure is expected to dismiss the window with the method. + // The base implementation (which dismisses the dialog) is not called if a handler exists. This can be + // done via . + // @xref + CVirtualHelper helper( "OnOK", this ); + if (!helper.HaveHandler()) { + if (m_lpDialogTemplate == NULL && m_hDialogTemplate == NULL) + // non modal dialog. + DestroyWindow(); + else + T::OnOK(); + } else { + helper.call(); + } + } + virtual void OnCancel() { + // @pyvirtual int|PyCDialog|OnCancel|Called by the MFC architecture when the user selects the Cancel button. + // @comm The procedure is expected to dismiss the window with the method. + // The base implementation (which dismisses the dialog) is not called if a handler exists. This can be + // done via . + // @xref + CVirtualHelper helper( "OnCancel", this ); + BOOL bCallDefault; + if (!helper.HaveHandler()) { + bCallDefault = TRUE; + } else { + bCallDefault = !helper.call(); // DO call default on exception, else dialog may never come down! + } + if (bCallDefault) { + DoOnCancel(); + } + } + void DoOnCancel() { + if (m_lpDialogTemplate == NULL && m_hDialogTemplate == NULL) + DestroyWindow(); + else + T::OnCancel(); + } + virtual void DoDataExchange(CDataExchange* pDX) { // DDX/DDV support + T::DoDataExchange(pDX); + Python_do_exchange( this, pDX ); + } +}; + +template +class CPythonPropertySheetFramework : public CPythonWndFramework +{ +public: + CPythonPropertySheetFramework(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0) : + CPythonWndFramework(nIDCaption, pParentWnd, iSelectPage) {;} + CPythonPropertySheetFramework(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0) : + CPythonWndFramework(pszCaption, pParentWnd, iSelectPage) {;} + + virtual BOOL OnInitDialog() { + // @pyvirtual int|PyCPropertySheet|OnInitDialog|Override to augment dialog-box initialization. + // @comm The base implementation is not called if a handler exists. This can be + // done via . + // @xref + BOOL result = FALSE; + CVirtualHelper helper( "OnInitDialog", this ); + if (!helper.HaveHandler()) { + result = T::OnInitDialog(); + } else { + if (helper.call()) + helper.retval(result); + } + return result; + // @rdesc Specifies whether the application has set the input focus to + // one of the controls in the dialog box. If OnInitDialog returns + // nonzero, Windows sets the input focus to the first control + // in the dialog box. The application can return 0/None only if + // it has explicitly set the input focus to one of the controls in the + // dialog box. + } +}; + +// Property Pages. +template +class CPythonPropertyPageFramework : public CPythonDlgFramework +{ +public: + CPythonPropertyPageFramework(UINT id, UINT caption) : + CPythonDlgFramework(id, caption) {;} + CPythonPropertyPageFramework(LPCTSTR id, UINT caption) : + CPythonDlgFramework(id, caption) {;} + + virtual BOOL OnKillActive() + { + // @pyvirtual int|PyCPropertyPage|OnKillActive|Called when the page loses focus. + // @rdesc The method should return TRUE if the page can be de-activated. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnKillActive", this ); + if (!helper.HaveHandler()) + return T::OnKillActive(); + BOOL bOK = TRUE; + if (helper.call()) + helper.retval(bOK); + return bOK; + } + + virtual BOOL OnSetActive() + { + // @pyvirtual int|PyCPropertyPage|OnSetActive|Called when the page becomes active. + // @rdesc The method should return TRUE if the page can be activated. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnSetActive", this ); + if (!helper.HaveHandler()) + return T::OnSetActive(); + BOOL bOK = TRUE; + if (helper.call()) + helper.retval(bOK); + return bOK; + } + + virtual BOOL OnApply() + { + // @pyvirtual int|PyCPropertyPage|OnApply|Called by the framework when the user chooses the OK or the Apply Now button. + // @rdesc Return Nonzero if the changes are accepted; otherwise 0. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnApply", this ); + if (!helper.HaveHandler()) + return T::OnApply(); + BOOL bOK = TRUE; + if (helper.call()) + helper.retval(bOK); + return bOK; + } + + virtual void OnReset() + { + // @pyvirtual void|PyCPropertyPage|OnReset|Called by the framework when the user chooses the Cancel button. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnReset", this ); + if (!helper.HaveHandler()) { + T::OnReset(); + } else { + helper.call(); + } + } + + virtual BOOL OnQueryCancel() + { + // @pyvirtual int|PyCPropertyPage|OnQueryCancel|Called by the framework when the user clicks the Cancel button and before the cancel action has taken place. + // @rdesc Return FALSE to prevent the cancel operation or TRUE to allow it. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnQueryCancel", this ); + if (!helper.HaveHandler()) + return T::OnQueryCancel(); + BOOL bOK = TRUE; + if (helper.call()) + helper.retval(bOK); + return bOK; + } + + virtual LRESULT OnWizardBack() + { + // @pyvirtual int|PyCPropertyPage|OnWizardBack|Called by the framework when the user clicks on the Back button in a wizard. + // @rdesc Return 0 to automatically advance to the next page; -1 to prevent the page from changing. To jump to a page other than the next one, return the identifier of the dialog to be displayed. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnWizardBack", this ); + if (!helper.HaveHandler()) + return T::OnWizardBack(); + long result = TRUE; + if (helper.call()) + helper.retval(result); + return result; + } + + virtual LRESULT OnWizardNext() + { + // @pyvirtual int|PyCPropertyPage|OnWizardNext|Called by the framework when the user clicks on the Next button in a wizard. + // @rdesc Return 0 to automatically advance to the next page; -1 to prevent the page from changing. To jump to a page other than the next one, return the identifier of the dialog to be displayed. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnWizardNext", this ); + if (!helper.HaveHandler()) + return T::OnWizardNext(); + long result = TRUE; + if (helper.call()) + helper.retval(result); + return result; + } + + virtual BOOL OnWizardFinish() + { + // @pyvirtual int|PyCPropertyPage|OnWizardFinish|Called by the framework when the user clicks on the Finish button in a wizard. + // @rdesc Return nonzero if the property sheet is destroyed when the wizard finishes; otherwise zero. + // @comm Note - If you provide a handler, you must call the underlying MFC method () yourself + // @xref + CVirtualHelper helper( "OnWizardFinish", this ); + if (!helper.HaveHandler()) + return T::OnWizardFinish(); + BOOL bOK = TRUE; + if (helper.call()) + helper.retval(bOK); + return bOK; + } + +}; + +// Views. +template +class CPythonViewFramework : public CPythonWndFramework +{ +public: + // ctor hacks + CPythonViewFramework() : CPythonWndFramework() {;} + // CFormView ctors. + CPythonViewFramework(UINT id) : CPythonWndFramework(id) {;} + CPythonViewFramework(LPCTSTR id) : CPythonWndFramework(id) {;} + // CCtrlView ctor + CPythonViewFramework(LPCTSTR lpszClass, DWORD dwStyle) : + CPythonWndFramework(lpszClass, dwStyle) {;} + virtual BOOL OnPreparePrinting (CPrintInfo *pInfo) { + // @pyvirtual |PyCView|OnPreparePrinting|Called by the framework before a document is printed or previewed + // @xref + CVirtualHelper helper ("OnPreparePrinting", this); + BOOL result; + if (!helper.HaveHandler()) { + result = T::OnPreparePrinting (pInfo); + } else { + helper.call(pInfo); + helper.retval(result); + } + return result; + // @pyparm |pInfo||The print info object. + } + virtual void OnBeginPrinting (CDC *pDC, CPrintInfo *pInfo) { + // @pyvirtual |PyCView|OnBeginPrinting|Called by the framework at the beginning of a print or print preview job, after OnPreparePrinting has been called. + // @xref + CVirtualHelper helper ("OnBeginPrinting", this); + if (!helper.HaveHandler()) { + T::OnBeginPrinting (pDC, pInfo); + return; + } + helper.call (pDC, pInfo); + // @pyparm |dc||The DC object. + // @pyparm |pInfo||The print info object. + } + virtual void OnEndPrinting (CDC *pDC, CPrintInfo *pInfo) { + // @pyvirtual |PyCView|OnEndPrinting|Called by the framework after a document has been printed or previewed. + // @xref + CVirtualHelper helper ("OnEndPrinting", this); + if (!helper.HaveHandler()) { + T::OnEndPrinting (pDC, pInfo); + return; + } + helper.call (pDC, pInfo); + // @pyparm |dc||The DC object. + // @pyparm |pInfo||The print info object. + } + virtual void OnPrepareDC (CDC *pDC, CPrintInfo *pInfo) { + // @pyvirtual |PyCView|OnPrepareDC|Called to prepare the device context for a view. + // @xref + CVirtualHelper helper ("OnPrepareDC", this); + if (!helper.HaveHandler()) { + T::OnPrepareDC (pDC, pInfo); + return; + } + T::OnPrepareDC (pDC, pInfo); + helper.call (pDC, pInfo); + // @pyparm |dc||The DC object. + // @pyparm |pInfo||The print info object. + } + virtual void OnInitialUpdate() { + // @pyvirtual tuple|PyCView|OnInitialUpdate|Called before the first update for a view. + // @comm The MFC base class is called only if no handler exists. + // @xref + CVirtualHelper helper ("OnInitialUpdate", this); + if (helper.HaveHandler()) + helper.call(); + else + T::OnInitialUpdate(); + + } + virtual void OnActivateView( BOOL bActivate, CView* pActivateView, CView* pDeactiveView ) { + // @pyvirtual |PyCView|OnActivateView|Called by the framework when a view is activated or deactivated. + // @xref + CVirtualHelper helper ("OnActivateView", this); + if (!helper.HaveHandler()) + T::OnActivateView(bActivate, pActivateView, pDeactiveView ); + else { + // @pyparm int|bActivate||Indicates whether the view is being activated or deactivated. + // @pyparm |activateView||The view object that is being activated. + // @pyparm |DeactivateView||The view object that is being deactivated. + helper.call (bActivate, pActivateView, pDeactiveView); + + // @comm If a handler exists, the base MFC implementation is not called. + // The activateView and deactiveView parameters are the same objects if the + // application's main frame window is activated with no change in the + // active view for example, if the focus is being transferred from + // another application to this one, rather than from one view to + // another within the application. + // This allows a view to re-realize its palette, if needed. + } + } + virtual void OnDraw( CDC *pDC ) { + // @pyvirtual |PyCView|OnDraw|Called when the view should be drawn. + CVirtualHelper helper( "OnDraw", this ); + // @pyparm |dc||The DC object. + // @xref + helper.call(pDC); + } + virtual void OnPrint( CDC *pDC, CPrintInfo *pInfo ) { + // @pyvirtual |PyCView|OnPrint|Called when the view should be printed. + // @xref + CVirtualHelper helper( "OnPrint", this ); + // @pyparm |dc||The DC object. + // @pyparm |pInfo||The PrintIfo object. + helper.call(pDC, pInfo); + } + virtual void OnUpdate (CView* pSender, LPARAM lHint, CObject* pHint) { + // @pyvirtual |PyCView|OnUpdate|Called by the framework when a view needs updating. + // @comm Typically you should not perform any drawing directly from OnUpdate. + // Instead, determine the rectangle describing, in device coordinates, the + // area that requires updating; pass this rectangle to . + // You can then paint the update next + // @xref + CVirtualHelper helper ("OnUpdate", this); + if (!helper.HaveHandler()) + // @pyparm |sender|| + // @pyparm int|lHint|| + // @pyparm object|hint|| + T::OnUpdate (pSender, lHint, pHint); + else + helper.call(pSender, (PyObject *)lHint); + } +}; + +template +class CPythonFrameFramework : public CPythonWndFramework +{ +public: + +protected: + // Operations + virtual BOOL OnCreateClient(LPCREATESTRUCT cs, CCreateContext* pContext) { + // @pyvirtual |PyCMDIChildWnd|OnCreateClient|Called by the framework during the execution of OnCreate. + + CVirtualHelper helper( "OnCreateClient", this ); + PythonCreateContext *pCC = (PythonCreateContext *)pContext; + // @pyparm tuple|CREATESTRUCT||A tuple describing a CREATESTRUCT structure. + // @pyparm object|object||A Python object initially passed to LoadFrame + if (helper.HaveHandler() && !helper.call(cs, pCC ? pCC->GetPythonObject() : NULL)) + return FALSE; + // @rdesc The return value from this method is ignored, but an exception will prevent window creation. + return T::OnCreateClient( cs, pContext ); + } + virtual void GetMessageString( UINT nID, CString& rMessage ) const { + // @pyvirtual string|PyCMDIChildWnd|GetMessageString|Gets the message string to use for a control specific ID. + CVirtualHelper helper("GetMessageString", (void *)this); + // @pyparm int|id||The command ID to retrieve the string for. + // @xref + if (helper.call((int &)nID)) { + char *ret; + if (helper.retval(ret)) + rMessage = ret; + } + else + T::GetMessageString(nID, rMessage); + } + virtual void ActivateFrame(int nCmdShow) { + // @pyvirtual |PyCMDIChildWnd|ActivateFrame|Called to activate the frame window. + // @comm If a handler for this function exists, then the base MFC implementation will not be called. + // If you wish to use the default functionality, can be called. + // If there is no handler, the base MFC implementation will be called. + // @xref + CVirtualHelper helper( "ActivateFrame", this ); + // @pyparm int|cmdShow||The paramater to be passed to + if (helper.HaveHandler()) { + helper.call(nCmdShow); + } else { + T::ActivateFrame(nCmdShow); + } + } +}; + +#define IDC_PRINT_TO_FILE 1040 +#define IDC_PRINT_COLLATE 1041 +#define IDC_PRINT_COPIES 1154 +#define IDC_PRINT_RANGE_ALL 1056 +#define IDC_PRINT_RANGE_SELECTION 1057 +#define IDC_PRINT_RANGE_PAGES 1058 +#define IDC_PRINT_COPIES_LABEL 1092 +#define IDC_PRINT_FROM 1152 +#define IDC_PRINT_TO 1153 + +template +class CPythonPrtDlgFramework : public CPythonDlgFramework +{ +public: + // ctor hacks! + CPythonPrtDlgFramework(BOOL bPrintSetupOnly, DWORD dwFlags, CWnd* pParentWnd) : + CPythonDlgFramework(bPrintSetupOnly, dwFlags, pParentWnd) {;} + // End of ctor hacks!!!! + + afx_msg LRESULT HandleInitDialog(WPARAM, LPARAM) { + PreInitDialog(); + BOOL result = Default(); + CVirtualHelper helper( "OnInitDialog", this ); + BOOL hresult; + if (helper.HaveHandler()) { + if (helper.call()) + helper.retval(hresult); + } + return result; + } + + virtual void OnOK() { + // @pyvirtual int|PyCPrintDialog|OnOK|Called by the MFC architecture when the user selects the OK button. + // @comm The procedure is expected to dismiss the window with the method. + // The base implementation (which dismisses the dialog) is not called if a handler exists. This can be + // done via . + // @xref + + CString csText; + int val; + + CWnd* ctl = GetDlgItem(IDC_PRINT_FROM); + ctl->GetWindowText(csText); + sscanf(csText, "%d", &val); + m_pd.nFromPage = val; + + ctl = GetDlgItem(IDC_PRINT_TO); + ctl->GetWindowText(csText); + sscanf(csText, "%d", &val); + m_pd.nToPage = val; + + ctl = GetDlgItem(IDC_PRINT_COPIES); + ctl->GetWindowText( csText ); + sscanf(csText, "%d", &val); + if (m_pd.Flags&PD_USEDEVMODECOPIES) { + LPDEVMODE devMode = (LPDEVMODE)::GlobalLock((HGLOBAL)m_pd.hDevMode); + devMode->dmCopies = val; + ::GlobalUnlock((HGLOBAL)m_pd.hDevMode); + } + m_pd.nCopies = val; + + CVirtualHelper helper( "OnOK", this ); + if (helper.HaveHandler()) { + helper.call(); + } + } + + virtual void OnCancel() { + // @pyvirtual int|PyCPrintDialog|OnCancel|Called by the MFC architecture when the user selects the Cancel button. + // @comm The procedure is expected to dismiss the window with the method. + // The base implementation (which dismisses the dialog) is not called if a handler exists. This can be + // done via . + // @xref + CVirtualHelper helper( "OnCancel", this ); + if (helper.HaveHandler()) { + helper.call(); + } + T::OnCancel(); + } + + afx_msg void HandlePrintToFile() { + CWnd* ctl = GetDlgItem(IDC_PRINT_TO_FILE); + int val = ((CButton*)ctl)->GetCheck(); + if (val) { // was checked, now will not be... + m_pd.Flags |= PD_PRINTTOFILE; + } else { // was not checked, now will be... + m_pd.Flags &= ~PD_PRINTTOFILE; + } + } + + afx_msg void HandleCollate() { + CWnd* ctl = GetDlgItem(IDC_PRINT_COLLATE); + int val = ((CButton*)ctl)->GetCheck(); + int useDevMode = (m_pd.Flags&PD_USEDEVMODECOPIES || + m_pd.Flags&PD_USEDEVMODECOPIESANDCOLLATE); + LPDEVMODE devMode; + if (useDevMode) { + devMode = (LPDEVMODE)::GlobalLock((HGLOBAL)m_pd.hDevMode); + } + if (val) { // was checked, now will not be... + m_pd.Flags |= PD_COLLATE; + } else { // was not checked, now will be... + m_pd.Flags &= ~PD_COLLATE; + } + if (useDevMode) { + devMode->dmCollate = DMCOLLATE_TRUE; + ::GlobalUnlock((HGLOBAL)m_pd.hDevMode); + } + } + + afx_msg void HandlePrintRange(UINT nID) { + CheckRadioButton(IDC_PRINT_RANGE_ALL, + IDC_PRINT_RANGE_PAGES, + nID); + m_pd.Flags &= ~(PD_ALLPAGES|PD_SELECTION|PD_PAGENUMS); + if (nID==IDC_PRINT_RANGE_ALL) { + m_pd.Flags |= PD_ALLPAGES; + } else if (nID==IDC_PRINT_RANGE_SELECTION) { + m_pd.Flags |= PD_SELECTION; + } else if (nID==IDC_PRINT_RANGE_PAGES) { + m_pd.Flags |= PD_PAGENUMS; + } + } + +// DECLARE_MESSAGE_MAP() +private: \ + static const AFX_MSGMAP_ENTRY _messageEntries[]; +protected: \ + static AFX_DATA const AFX_MSGMAP messageMap; + + static const AFX_MSGMAP* PASCAL _GetBaseMessageMap() { + return &T::messageMap; + } + virtual const AFX_MSGMAP* GetMessageMap() const { + return &messageMap; + } +}; + +template +AFX_DATADEF const AFX_MSGMAP CPythonPrtDlgFramework::messageMap = + { &CPythonPrtDlgFramework::_GetBaseMessageMap, &CPythonPrtDlgFramework::_messageEntries[0] +}; + +template +const AFX_MSGMAP_ENTRY CPythonPrtDlgFramework::_messageEntries[] = { + ON_MESSAGE(WM_INITDIALOG, HandleInitDialog) + ON_COMMAND(IDC_PRINT_TO_FILE, HandlePrintToFile) + ON_COMMAND(IDC_PRINT_COLLATE, HandleCollate) + ON_COMMAND_RANGE(IDC_PRINT_RANGE_ALL, IDC_PRINT_RANGE_PAGES, HandlePrintRange) + {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } +}; + +#undef IDC_PRINT_TO_FILE +#undef IDC_PRINT_COLLATE +#undef IDC_PRINT_COPIES +#undef IDC_PRINT_RANGE_ALL +#undef IDC_PRINT_RANGE_SELECTION +#undef IDC_PRINT_RANGE_PAGES +#undef IDC_PRINT_COPIES_LABEL +#undef IDC_PRINT_FROM +#undef IDC_PRINT_TO + +#endif // __WIN32UIEXT_H__ diff --git a/Pythonwin/win32uimodule.cpp b/Pythonwin/win32uimodule.cpp new file mode 100644 index 0000000000..1f86017a55 --- /dev/null +++ b/Pythonwin/win32uimodule.cpp @@ -0,0 +1,2253 @@ +/* + + first hack at a UI module, built using MFC V2.0 (on NT) + +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 +#include "win32uiHostGlue.h" +#include "win32win.h" +#include "win32control.h" +#include "win32doc.h" +#include "win32menu.h" +#include "win32dlg.h" +#include "win32dc.h" +#include "win32gdi.h" +#include "win32brush.h" +#include "win32bitmap.h" +#include "win32font.h" +#include "win32dll.h" +#include "win32splitter.h" +#include "win32toolbar.h" +#include "win32prop.h" +#include "win32template.h" +#include "win32pen.h" +#include "win32RichEdit.h" +#include "win32RichEditDocTemplate.h" +#include "win32dlgbar.h" +#ifdef HIER_LIST +#include "win32hl.h" +#endif +#ifdef _DEBUG_HEAP +# include "malloc.h" // for _heapchk +#endif + +#include "reswin32ui.h" +#include "sysmodule.h" + +extern "C" __declspec(dllimport) int PyCallable_Check(PyObject *); // python - object.c +extern DWORD DebuggerThreadFunc( LPDWORD lpdwWhatever ); + +static char BASED_CODE uiModName[] = "win32ui"; +static char BASED_CODE errorName[] = "win32ui"; + +PYW_EXPORT PyObject *ui_module_error; +Win32uiHostGlue *pHostGlue = NULL; + +// When TRUE, we are in an abort or after shutdown mode, and therefore should +// do nothing related to Python at all! +BOOL bInFatalShutdown = FALSE; + +PyObject *ReturnAPIError(const char *fn) +{ + CString msg=GetAPIErrorString((char *)fn); + PyErr_SetString(ui_module_error,msg.GetBuffer(0)); + return NULL; +} + +///////////////////////////////////////////////////////////////////////// +// +// Windows API Hook +// This is used to trap a DESTROY message. I use this, rather than +// the Translate functions, so that a non CWnd window can still be used. +// For example, in the future, the common dialog boxes may have Python +// support, so it is critical we know when they have been destroyed. + +// max speed for hook function (even when debugging!!) +#pragma optimize("2", on) +// Windows hook. +static HHOOK hhook = 0; +LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + // hooking seems to slow down the system alot - go for speed + // (but still gotta follow the rules!) + if (nCode==HC_ACTION && !bInFatalShutdown) { // I can process it. + CWPSTRUCT *cwp = (CWPSTRUCT *)lParam; + MSG msg; + msg.message = cwp->message; + msg.hwnd = cwp->hwnd; + msg.lParam=cwp->lParam; + msg.wParam=cwp->wParam; + msg.time=0 ; // set these to zero - better value? + msg.pt.x = msg.pt.y = 0; + Python_check_message(&msg); + + if (cwp->message==WM_NCDESTROY) { // seems to be last decent message + CWnd *wnd=CWnd::FromHandlePermanent(cwp->hwnd); + if (wnd) + Python_delete_assoc( wnd ); + } + } + return CallNextHookEx(hhook, nCode,wParam, lParam); +} +// back to default. +#pragma optimize("", on) + +BOOL HookWindowsMessages() +{ + hhook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, + (HINSTANCE) NULL, GetCurrentThreadId()); + if (!hhook) + TRACE("Hooking windows messages failed\n"); + + return (hhook!=0); +} + +///////////////////////////////////////////////////////////////////// +// +// ui_type object +// +////////////////////////////////////////////////////////////////////// +ui_type::ui_type( const char *name, ui_type *pBase, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ) +{ +// originally, this copied the typeobject of the parent, but as it is impossible +// to gurantee order of static object construction, I went this way. This is +// probably better, as is forces _all_ python objects have the same type sig. + static PyTypeObject type_template = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "template", /*tp_name*/ + sizeof(ui_base_class), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor) ui_base_class::sui_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc) ui_base_class::sui_getattr, /*tp_getattr*/ + (setattrfunc) ui_base_class::sui_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)ui_base_class::sui_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + }; + + *((PyTypeObject *)this) = type_template; + methods = methodList; + // cast away const, as Python doesnt use it. + tp_name = (char *)name; + tp_basicsize = typeSize; + base = pBase; + ctor = thector; +} +ui_type::~ui_type() +{ +} + +////////////////////////////// +// +// ui_type_CObject +ui_type_CObject::CRuntimeClassTypeMap *ui_type_CObject::typemap = NULL; + +ui_type_CObject::ui_type_CObject( const char *name, ui_type *pBaseType, CRuntimeClass *pRT, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ): + ui_type(name, pBaseType, typeSize, methodList, thector ) +{ + pCObjectClass = pRT; + if (pRT) { + if (!typemap) typemap = new CRuntimeClassTypeMap; + typemap->SetAt(pCObjectClass, this); + } +} + +ui_type_CObject::~ui_type_CObject() +{ + if (pCObjectClass) { + typemap->RemoveKey(pCObjectClass); + if (typemap->IsEmpty()) { + delete typemap; + typemap = NULL; + } + } +} + +///////////////////////////////////////////////////////////////////// +// +// base class object +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +IMPLEMENT_DYNAMIC(ui_base_class, CObject); +#endif + +ui_base_class::ui_base_class() +{ + strcpy(sig, SIG); +} +ui_base_class::~ui_base_class() +{ +#ifdef TRACE_LIFETIMES + TRACE("Destructing a '%s' at %p\n", ob_type->tp_name, this); +#endif +} + +ui_base_class *ui_base_class::make( ui_type &makeTypeRef) +{ + ui_type *makeType = &makeTypeRef; // use to pass ptr as param! + if (makeType->ctor==NULL) { + RETURN_ERR("Internal error - the type does not declare a win32ui constructor"); + } + + ui_base_class *pNew = (*makeType->ctor)(); + pNew->ob_type = makeType; + _Py_NewReference(pNew); +#ifdef _DEBUG // this is really only for internal errors, and they should be ironed out! + if (!pNew->is_uiobject(makeType)) + RETURN_ERR("Internal error - created type isnt what was requested!"); +#endif +#ifdef TRACE_LIFETIMES + TRACE("Constructing a '%s' at %p\n",pNew->ob_type->tp_name, pNew); +#endif + return pNew; +} +/*static*/ BOOL ui_base_class::is_uiobject(PyObject *&o, ui_type *which) +{ + ui_base_class *ob = (ui_base_class *)o; + if (ob==NULL || ob==Py_None) + return FALSE; + // quick fasttrack. + if ((ui_type *)ob->ob_type==which) + return TRUE; + // if Python instance, my be able to derive the paired Python type. + if (PyInstance_Check(ob)) { + PyObject *obattr= PyObject_GetAttrString(ob, "_obj_"); + if (obattr==NULL) { + PyErr_Clear(); + TRACE("is_uiobject fails due to object being an instance without an _obj_ attribute!\n"); + return FALSE; + } + if (obattr==Py_None) { + TRACE("is_uiobject fails due to object being an instance with _obj_==None\n"); + return FALSE; + } + o = obattr; + ob = (ui_base_class *)o; + } + if (memcmp(ob->sig, SIG, sizeof(SIG))) { + TRACE("is_uiobject fails due to sig failure!\n"); + return FALSE; + } + return is_nativeuiobject(ob, which); +} + +/*static*/BOOL ui_base_class::is_nativeuiobject(PyObject *ob, ui_type *which) +{ + // check for inheritance. + ui_type *thisType = (ui_type *)ob->ob_type; + while (thisType) { + if (which==thisType) + return TRUE; + thisType = thisType->base; + } + return FALSE; +} +BOOL ui_base_class::is_uiobject(ui_type *which) +{ + PyObject *cpy = this; + BOOL ret = is_uiobject(cpy,which); +#ifdef _DEBUG + return ret && (cpy==this); +#endif + return ret; +} + +PyObject * +ui_base_class::sui_getattr(PyObject *self, char *name) +{ + return ((ui_base_class *)self)->getattr(name); +} + +PyObject * +ui_base_class::getattr(char *name) +{ + // implement inheritance. + PyObject *retMethod = NULL; + ui_type *thisType = (ui_type *)ob_type; + while (thisType) { + retMethod = Py_FindMethod(thisType->methods, (PyObject *)this, name); + if (retMethod) + break; + thisType = thisType->base; + if (thisType) + PyErr_Clear(); + } + return retMethod; +} +int +ui_base_class::sui_setattr(PyObject *op, char *name, PyObject *v) +{ + ui_base_class* bc = (ui_base_class *)op; + return bc->setattr(name,v); +} +int ui_base_class::setattr(char *name, PyObject *v) +{ + char buf[128]; + sprintf(buf, "%s has read-only attributes", ob_type->tp_name ); + PyErr_SetString(PyExc_TypeError, buf); + return -1; +} +/*static*/ PyObject * +ui_base_class::sui_repr( PyObject *op ) +{ + ui_base_class* w = (ui_base_class *)op; + CString ret = w->repr(); + return Py_BuildValue("s",(const char *)ret); +} +CString ui_base_class::repr() +{ + CString csRet; + char *buf = csRet.GetBuffer(50); + sprintf(buf, "object '%s'", ob_type->tp_name); + csRet.ReleaseBuffer(); + return csRet; +} +void ui_base_class::cleanup() +{ + CString rep = repr(); + const char *szRep = rep; + TRACE("cleanup detected %s, refcount = %d\n",szRep,ob_refcnt); +} + +/*static*/ void ui_base_class::sui_dealloc(PyObject *window) +{ + delete (ui_base_class *)window; +} + +// @pymethod |PyAssocObject|GetMethodByType|Given a method name and a type object, return the attribute. +static PyObject * +ui_base_class_GetMethodByType(PyObject *self, PyObject *args) +{ + // @comm This function allows you to obtain attributes for base types. + // For example, calling appObject.GetAttributeByType("Run", threadType) will return + // the built-in Run method for the CWinThread object rather than the CWinApp object. + PyObject *obType; + char *attr; + ui_base_class *pAssoc = (ui_base_class *)self; + if (pAssoc==NULL) return NULL; + if (!PyArg_ParseTuple(args, "sO:GetAttributeByType", &attr, &obType )) + return NULL; + + // check it is one of ours. + PyObject *retMethod = NULL; + ui_type *thisType = (ui_type *)pAssoc->ob_type; + while (thisType) { + if ((PyObject *)thisType==obType) + break; + thisType = thisType->base; + } + if (thisType==NULL) + RETURN_TYPE_ERR("The object is not of that type"); + + return Py_FindMethod(thisType->methods, self, attr); +} + + +struct PyMethodDef ui_base_class::empty_methods[] = { + {NULL, NULL} +}; + +struct PyMethodDef ui_base_class_methods[] = { + {"GetMethodByType", ui_base_class_GetMethodByType, 1}, + {NULL, NULL} +}; + +ui_type ui_base_class::type( "PyCBase", + NULL, + sizeof(ui_base_class), + ui_base_class_methods, + NULL); + + +#ifdef _DEBUG +void DumpAssocPyObject( CDumpContext &dc , void *object ) +{ + ui_assoc_object *py_bob = ui_assoc_object::handleMgr.GetAssocObject( object ); + if (py_bob==NULL) + dc << ", have no attached Python object"; + else { +#if !defined(_MAC) && !defined(_AFX_PORTABLE) + try +#endif + { + dc << ", Python object "; + if (AfxIsValidAddress(py_bob, sizeof(ui_assoc_object))) { + dc << py_bob << " with refcounf " << + py_bob->ob_refcnt; + } else + dc << ""; + } +#if !defined(_MAC) && !defined(_AFX_PORTABLE) + catch(int code) { + // short form for trashed objects + afxDump << ""; + } + catch(...) { + // short form for trashed objects + afxDump << ""; + } +#endif + } +} + +void ui_base_class::Dump( CDumpContext &dc ) const +{ + CObject::Dump(dc); + dc << "Object of type " << ob_type->tp_name << ", ob_refcnt=" << ob_refcnt; +} +#endif + + +///////////////////////////////////////////////////////////////////// +// +// Helpers for the application. Avoid pulling python headers everywhere. +// +///////////////////////////////////////////////////////////////////// +extern "C" __declspec(dllimport) void PySys_SetPath(char *); +void PYW_EXPORT Python_addpath( const char *paths ) +{ + char workBuf[MAX_PATH+20]; + char fullThisPath[MAX_PATH+20]; + char fullWorkBuf[MAX_PATH+20]; + + PyObject *p = PySys_GetObject("path"); + if (!PyList_Check(p)) + return; + + int posFirst = 0; + int posLast = 0; + while (paths[posLast]) { + // skip all ';' + while (paths[posFirst]==';') + posFirst++; + posLast = posFirst; + while (paths[posLast]!='\0' && paths[posLast]!=';') + posLast++; + int len = min(sizeof(workBuf)-1,posLast - posFirst); + if (len>0) { + strncpy(workBuf, paths+posFirst, len ); + workBuf[len]='\0'; + // Check if it is already on the path... + if (!GetFullPath(fullWorkBuf, workBuf)) // not a valid path + continue; // ignore it. + int listLen = PyList_Size(p); + int itemNo; + for (itemNo=0;itemNo=listLen) { // not in list + // Need to add it. + PyObject *add = PyString_FromString(fullWorkBuf); + if (add) { + PyList_Insert(p, 0, add); + Py_DECREF(add); + } + } + } + posFirst = posLast; + } +} + +#define GPEM_ERROR(what) {errorMsg = "";goto done;} +static char *GetPythonTraceback(PyObject *exc_tb) +{ + char *result = NULL; + char *errorMsg = NULL; + PyObject *modStringIO = NULL; + PyObject *modTB = NULL; + PyObject *obFuncStringIO = NULL; + PyObject *obStringIO = NULL; + PyObject *obFuncTB = NULL; + PyObject *argsTB = NULL; + PyObject *obResult = NULL; + + /* Import the modules we need - cStringIO and traceback */ + modStringIO = PyImport_ImportModule("cStringIO"); + if (modStringIO==NULL) GPEM_ERROR("cant import cStringIO"); + modTB = PyImport_ImportModule("traceback"); + if (modTB==NULL) GPEM_ERROR("cant import traceback"); + + /* Construct a cStringIO object */ + obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); + if (obFuncStringIO==NULL) GPEM_ERROR("cant find cStringIO.StringIO"); + obStringIO = PyObject_CallObject(obFuncStringIO, NULL); + if (obStringIO==NULL) GPEM_ERROR("cStringIO.StringIO() failed"); + + /* Get the traceback.print_exception function, and call it. */ + obFuncTB = PyObject_GetAttrString(modTB, "print_tb"); + if (obFuncTB==NULL) GPEM_ERROR("cant find traceback.print_tb"); + argsTB = Py_BuildValue("OOO", + exc_tb ? exc_tb : Py_None, + Py_None, + obStringIO); + if (argsTB==NULL) GPEM_ERROR("cant make print_tb arguments"); + + obResult = PyObject_CallObject(obFuncTB, argsTB); + if (obResult==NULL) GPEM_ERROR("traceback.print_tb() failed"); + + /* Now call the getvalue() method in the StringIO instance */ + Py_DECREF(obFuncStringIO); + obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue"); + if (obFuncStringIO==NULL) GPEM_ERROR("cant find getvalue function"); + Py_DECREF(obResult); + obResult = PyObject_CallObject(obFuncStringIO, NULL); + if (obResult==NULL) GPEM_ERROR("getvalue() failed."); + + /* And it should be a string all ready to go - duplicate it. */ + if (!PyString_Check(obResult)) + GPEM_ERROR("getvalue() did not return a string"); + result = strdup(PyString_AsString(obResult)); +done: + if (result==NULL && errorMsg != NULL) + result = strdup(errorMsg); + Py_XDECREF(modStringIO); + Py_XDECREF(modTB); + Py_XDECREF(obFuncStringIO); + Py_XDECREF(obStringIO); + Py_XDECREF(obFuncTB); + Py_XDECREF(argsTB); + Py_XDECREF(obResult); + return result; +} + +BOOL DisplayPythonTraceback(PyObject *exc_type, PyObject *exc_val, PyObject *exc_tb, const char *extraTitleMsg = NULL) +{ + class CTracebackDialog : public CDialog { + public: + CTracebackDialog(PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb, const char *extraTitleMsg) : + CDialog(IDD_LARGE_EDIT) + { + m_exc_type = exc_type; + Py_XINCREF(exc_type); + m_exc_value = exc_value; + Py_XINCREF(exc_value); + m_exc_tb = exc_tb; + Py_XINCREF(exc_tb); + m_extraTitleMsg = extraTitleMsg; + } + ~CTracebackDialog() + { + Py_XDECREF(m_exc_tb); + Py_XDECREF(m_exc_type); + Py_XDECREF(m_exc_value); + } + BOOL OnInitDialog() { + CDialog::OnInitDialog(); + CEnterLeavePython _celp; + CString title("Python Traceback"); + if (m_extraTitleMsg) + title = title + m_extraTitleMsg; + + SetWindowText(title); + GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE); + GetDlgItem(IDOK)->SetWindowText("Close"); + char *msg = GetPythonTraceback(m_exc_tb); + char *msg_free = msg; + // Translate '\n' to '\r\n' - do it the easy way! + CString useMsg; + for (;*msg;msg++) + if (*msg=='\n') + useMsg += "\r\n"; + else + useMsg += *msg; + free(msg_free); + PyObject *obStrType = PyObject_Str(m_exc_type); + char *szType = PyString_AsString(obStrType); + useMsg += szType; + useMsg += ": "; + + PyObject *obStrVal = PyObject_Str(m_exc_value); + char *szVal = PyString_AsString(obStrVal); + useMsg+=szVal; +#ifdef _DEBUG + { + // doesnt seem to like long strings. + CString cs(useMsg); + int i = 0; + while (iSetWindowText(useMsg); + GetDlgItem(IDC_EDIT1)->SetFocus(); + return FALSE; + }; + PyObject *m_exc_tb, *m_exc_type, *m_exc_value; + const char *m_extraTitleMsg; + }; + CTracebackDialog dlg(exc_type, exc_val, exc_tb, extraTitleMsg); + GUI_BGN_SAVE; + dlg.DoModal(); + GUI_END_SAVE; + return TRUE; +} + +int Python_run_command_with_log(const char *command, const char * logFileName = NULL) +{ + _ASSERTE(logFileName==NULL); // The logFileName param is no longer used! + PyObject *m, *d, *v; + m = PyImport_AddModule("__main__"); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + v = PyRun_String((char *)command, file_input, d, d); + if (v == NULL) { + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + DisplayPythonTraceback(type, value, traceback); + PyErr_Restore(type, value, traceback); +/******* + PyObject *fo = PyFile_FromString((char *)logFileName, "w" ); + if (fo==NULL) + return -1; + PyObject *old = PySys_GetObject( "stderr" ); + if (old==NULL) + return -1; + Py_INCREF(old); + PySys_SetObject( "stderr", fo ); + PyErr_Print(); + PySys_SetObject( "stderr", old ); + Py_DECREF(old); + Py_XDECREF(fo); + return 1; // indicate failure, with valid log. +*******/ + return 1; // indicate failure, with traceback correctly shown. + } + DODECREF(v); + return 0; +} + +// The "Official" way to destroy an associated (ie, MFC) object. +// The object will be destroyed if appropriate. +// Requires the Python thread state be NOT acquired. +void Python_delete_assoc( void *ob ) +{ + // Notify Python object of my attached object removal. + { + CVirtualHelper helper ("OnAttachedObjectDeath", ob); + helper.call(); + } + ui_assoc_object *pObj; + if ((pObj=ui_assoc_object::GetPyObject(ob)) && !bInFatalShutdown) { + CEnterLeavePython _celp; // KillAssoc requires it is held! + pObj->KillAssoc(); + } +} + +void Python_set_error(const char *msg) +{ +} +// In DEBUG builds, access voilations will normally trip my debugger, and +// hence I dont want them trapped. Stack Overflows normally mean runaway Python +// code, and I dont really want these trapped. +#ifdef _DEBUG +static int bTrapAccessViolations = FALSE; +#endif + +// exception handler. +static DWORD FilterFunc (DWORD dwExceptionCode) { + + // Assume that we do not know how to handle the exception + // by telling the system to continue to search for an SEH + // handler. + DWORD dwRet = EXCEPTION_CONTINUE_SEARCH; + switch (dwExceptionCode) { + case STATUS_STACK_OVERFLOW: + OutputDebugString("win32ui has stack overflow!\n"); + PyErr_SetString(PyExc_SystemError,"Stack Overflow"); + dwRet = EXCEPTION_EXECUTE_HANDLER; + break; + case EXCEPTION_ACCESS_VIOLATION: + OutputDebugString("win32ui has access vln!\n"); +#ifdef _DEBUG + if (!bTrapAccessViolations) + return dwRet; +#endif // _DEBUG + PyErr_SetString(PyExc_SystemError,"Access Violation"); + dwRet = EXCEPTION_EXECUTE_HANDLER; + break; + default: + break; + } + return(dwRet); +} + +PyObject *gui_call_object(PyObject *themeth, PyObject *thearglst) +{ + return PyEval_CallObject(themeth,thearglst); +} + +void gui_print_error(void) +{ + // basic recursion control. + static BOOL bInError = FALSE; + if (bInError) return; + bInError=TRUE; + + // Check if the exception is SystemExit - if so, + // PyErr_Print will terminate then and there! This is + // not good (and not what we want!? + PyObject *exception, *v, *tb; + PyErr_Fetch(&exception, &v, &tb); + PyErr_NormalizeException(&exception, &v, &tb); + + if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SystemExit)) { + // Replace it with a RuntimeError. + TRACE("WARNING!! win32ui had a SystemError - Replacing with RuntimeError!!\n"); + Py_DECREF(exception); + Py_XINCREF(PyExc_RuntimeError); + PyErr_Restore(PyExc_RuntimeError, v, tb); + } else + PyErr_Restore(exception, v, tb); + // Now print it. + + PyErr_Print(); + bInError=FALSE; +} + +// A Python program can install a callback notifier, to make all +// callbacks! +static PyObject *pCallbackCaller = NULL; +PyObject *Python_do_callback(PyObject *themeth, PyObject *thearglst) +{ + PyObject *result; + if (pCallbackCaller) { + PyObject *newarglst = Py_BuildValue("(OO)",themeth,thearglst); + result = gui_call_object( pCallbackCaller, newarglst ); + DODECREF(newarglst); + } else + result = gui_call_object( themeth, thearglst ); + DODECREF(thearglst); + if (result==NULL) { + TRACE("Python_do_callback: callback failed with exception\n"); + gui_print_error(); + } + return result; +} + +int Python_do_int_callback(PyObject *themeth, PyObject *thearglst) +{ + int retVal=UINT_MAX; // an identifiable, but unlikely genuine value. + BOOL isError = FALSE; + PyObject *result = Python_do_callback(themeth, thearglst); + if (result==NULL) + return retVal; + if (result==Py_None) // allow for None==0 + retVal = 0; + else if (result != Py_None && (!PyArg_Parse(result,"i",&retVal))) { + TRACE("Python_do_int_callback: callback had bad return type\n"); + PyErr_SetString(ui_module_error, "Callback must return an integer, or None"); + gui_print_error(); + } +#ifdef _DEBUG_HEAP // perform some diagnostics. May help trap reference errors. + if (_heapchk()!=_HEAPOK) + TRACE("**** Warning-heap corrupt after application callback ****\n"); +#endif + DODECREF(result); + return retVal; +} +int Python_callback(PyObject *method, int val) +{ + PyObject *meth = method; + PyObject *thearglst = Py_BuildValue("(i)",val); + return Python_do_int_callback(meth,thearglst); +} +int Python_callback(PyObject *method, int val1, int val2) +{ + PyObject *meth = method; + PyObject *arglst = Py_BuildValue("(ii)",val1,val2); + return Python_do_int_callback(meth,arglst); +} + +int Python_callback(PyObject *method) +{ + PyObject *meth = method; + PyObject *arglst = Py_BuildValue("()"); + return Python_do_int_callback(meth,arglst); +} +int Python_callback(PyObject *method, const MSG *msg) +{ + PyObject *meth = method; + PyObject *arglst = Py_BuildValue("((iiiii(ii)))",msg->hwnd,msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); + return Python_do_int_callback(meth,arglst); +} +int Python_callback(PyObject *method, PyObject *object) +{ + PyObject *meth = method; + PyObject *arglst = Py_BuildValue("(O)", object); + return Python_do_int_callback(meth,arglst); +} + + +///////////////////////////////////////////////////////////////////// +// +// Helpers for the methods. +// +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// +// +// Python Functions etc. +// +///////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|PrintTraceback|Prints a traceback using the internal Python mechanism. +static PyObject * +ui_python_print_traceback( PyObject *self, PyObject *args ) +{ + PyObject *tb, *output; + // @pyparm object|tb||The traceback to print. + // @pyparm object|output||The object to write the traceback to. + if (!PyArg_ParseTuple(args, "OO:PrintTraceback", &tb, &output)) + return NULL; + PyTraceBack_Print(tb,output); + RETURN_NONE; +} + +// @pymethod |win32ui|OutputDebugString|Sends a string to the Windows debugging device. +static PyObject * +ui_output_debug(PyObject *self, PyObject *args) +{ + char *msg; + // @pyparm string|msg||The string to write. + if (!PyArg_ParseTuple(args, "s:OutputDebugString", &msg)) + return NULL; + GUI_BGN_SAVE; +#ifdef BULLSHIT_BUG + CString csuiod; + char *uiod_base = csuiod.GetBuffer(strlen(msg)); + char *uiod = uiod_base; + + while (*msg) + { + // not sure what's going on here. NT seems to add a \n each call.. + // Im sure msvc16 doesnt...(well, I _think_ Im sure..:) + while (*msg && *msg!='\n') + *uiod++ = *msg++; + *uiod='\0'; // replace with NULL; + if (*msg) { // must be \n + uiod=uiod_base; + OutputDebugString(uiod) ; + ++msg; + } + } +#else + OutputDebugString(msg); +#endif + GUI_END_SAVE; + RETURN_NONE; +} + +///////////////////////////////////////////////////////////////////// +// +// Python Methods etc. +// +///////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|CreateMDIFrame|Creates an MDI Frame window. +static PyObject * +ui_create_mdi_frame(PyObject *self, PyObject *args) +{ + // @comm An MDI Frame Window is usually the main application window. + // Therefore there is uaually only one of these windows per application. + CHECK_NO_ARGS2(args,CreateMDIFrame); + CWinApp *pApp = GetApp(); + if (pApp==NULL) return NULL; + GUI_BGN_SAVE; + CPythonMDIFrameWnd* pMainFrame = new CPythonMDIFrameWnd; + GUI_END_SAVE; + return ui_assoc_object::make(PyCMDIFrameWnd::type, pMainFrame)->GetGoodRet(); + // @rdesc The window object created. An exception is raised if an error occurs. + // @comm An application can only hae one main window. This method will fail if the application + // window already exists. +} + +// @pymethod |win32ui|CreateMDIChild|Creates an MDI Child window. +static PyObject * +ui_create_mdi_child(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,CreateMDIChild); + GUI_BGN_SAVE; + CPythonMDIChildWnd* pFrame = new CPythonMDIChildWnd; + GUI_END_SAVE; + return ui_assoc_object::make(PyCMDIChildWnd::type, pFrame)->GetGoodRet(); + // @rdesc The window object created. An exception is raised if an error occurs. +} + +// @pymethod int|win32ui|Enable3dControls|Enables 3d controls for the application. +static PyObject * +ui_enable_3d_controls(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,Enable3dControls); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + GUI_BGN_SAVE; + int rc = pApp->Enable3dControls(); + GUI_END_SAVE; + + return Py_BuildValue("i",rc); + // @rdesc True if 3d controls could be enabled, false otherwise. +} + +// @pymethod string|win32ui|GetCommandLine|Returns the application's command line. +static PyObject * +ui_get_command_line (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetCommandLine); + return Py_BuildValue("s", ::GetCommandLine()); // @pyseeapi GetCommandLine +} + +// @pymethod int|win32ui|GetInitialStateRequest|Returns the requested state that the application start in. This is the same as the paramaters available to +static PyObject * +ui_get_initial_state_request(PyObject *self, PyObject *args) +{ + // @comm In some cases, it may not be possible to start in the requested mode. An application + // may start in its default mode, then set its mode to match the value returned from this method. + CHECK_NO_ARGS2(args,GetInitialStateRequest); + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + return Py_BuildValue("i", pApp->m_nCmdShow ); +} +// @pymethod string|win32ui|GetName|Returns the name of the current executable. +static PyObject * +ui_get_name(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetName); + // MFC AppName gives title, ExeName gives module name! + char fileName[MAX_PATH+1]; + + GetModuleFileName( GetModuleHandle(NULL), fileName, sizeof(fileName)); + return Py_BuildValue("s", fileName ); +} + +// @pymethod tuple|win32ui|GetRect|Returns the rectangle of the main application frame. See for further details. +static PyObject * +ui_get_rect(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetRect); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + CWnd *pFrame = pApp->GetMainFrame(); + if (!pFrame) + RETURN_ERR("The frame does not exist"); + + CRect rect; + GUI_BGN_SAVE; + pFrame->GetWindowRect( &rect ); + GUI_END_SAVE; + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); + // @rdesc A tuple of integers with (left, top, right, bottom) +} +// @pymethod |win32ui|WriteProfileVal|Writes a value to the application's INI file. +static PyObject * +ui_write_profile_val(PyObject *self, PyObject *args) +{ + char *sect, *entry, *strVal; + int intVal; + // @pyparm string|section||The section in the INI file to write to. + // @pyparm string|entry||The entry within the section in the INI file to write to. + // @pyparm int/string|value||The value to write. The type of this parameter determines the method's return type. + BOOL bHaveInt = TRUE; + if (!PyArg_ParseTuple(args, "ssi:WriteProfileVal", §, &entry, &intVal)) { + bHaveInt = FALSE; + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "ssz", §, &entry, &strVal)) { + // set my own error + PyErr_Clear(); + RETURN_TYPE_ERR("WriteProfileVal must have format (ssi) or (ssz)"); + } + } + BOOL rc; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + + if (bHaveInt) { +// TRACE("Write profile value (int)[%s] - %s=%d\n",sect,entry,intVal); + GUI_BGN_SAVE; + rc = pApp->WriteProfileInt( sect, entry, intVal ); + GUI_END_SAVE; + } + else { +// TRACE("Write profile value (str)[%s] - %s=%s\n",sect,entry,strVal?strVal:""); + GUI_BGN_SAVE; + rc = pApp->WriteProfileString( sect, entry, strVal ); + GUI_END_SAVE; + } + if (!rc) + RETURN_ERR("WriteProfileInt/String failed"); + return Py_BuildValue("i",rc); +} +// @pymethod int/string|win32ui|GetProfileVal|Returns a value from the application's INI file. +static PyObject * +ui_get_profile_val(PyObject *self, PyObject *args) +{ + char *sect, *entry, *strDef; + int intDef; + BOOL bHaveInt = TRUE; + // @pyparm string|section||The section in the INI file to read from. + // @pyparm string|entry||The entry within the section in the INI file to read. + // @pyparm int/string|defValue||The default value. The type of this parameter determines the method's return type. + if (!PyArg_ParseTuple(args, "ssi", §, &entry, &intDef)) { + bHaveInt = FALSE; + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "sss:GetProfileVal", §, &entry, &strDef)) { + // set my own error + PyErr_Clear(); + RETURN_TYPE_ERR("GetProfileVal must have format (ssi) or (sss)"); + } + } + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + if (bHaveInt) { + GUI_BGN_SAVE; + PyObject *rc = Py_BuildValue("i",pApp->GetProfileInt(sect, entry, intDef )); + GUI_END_SAVE; + return rc; + } + else { + GUI_BGN_SAVE; + CString res = pApp->GetProfileString(sect, entry, strDef ); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)res); + } +} +// @pymethod |win32ui|SetProfileFilename|Sets the name of the INI file used by the application. +static PyObject * +ui_set_profile_filename(PyObject *self, PyObject *args) +{ + char *filename; + // @pyparm string|filename||The name of the ini file. + if (!PyArg_ParseTuple(args, "s:SetProfileFilename", &filename)) + return NULL; + // this is a memory leak! + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + + char *newBuf = strdup(filename); + pApp->m_pszProfileName = newBuf; + RETURN_NONE; +} +// @pymethod string|win32ui|GetProfileFileName|Returns the name of the INI file used by the application. +static PyObject * +ui_get_profile_filename(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetProfileFileName); + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + return Py_BuildValue("s", pApp->m_pszProfileName); +} +// @pymethod |win32ui|LoadStdProfileSettings|Loads MFC standard settings from the applications INI file. This includes the Recent File List, etc. +static PyObject * +ui_load_std_profile_settings(PyObject *self, PyObject *args) +{ + int maxFiles = _AFX_MRU_COUNT; + // @pyparm int|maxFiles|_AFX_MRU_COUNT|The maximum number of files to maintain on the Recently Used File list. + if (!PyArg_ParseTuple(args, "|i:LoadStdProfileSettings", &maxFiles)) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + // @comm This function can only be called once in an applications lifetime, else an exception is raised. + if (pApp->HaveLoadStdProfileSettings()) + RETURN_ERR("The profile settings have already been loaded."); + GUI_BGN_SAVE; + pApp->LoadStdProfileSettings(maxFiles); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |win32ui|SetStatusText|Sets the text in the status bar of the application. +static PyObject * +ui_set_status_text(PyObject *self, PyObject *args) +{ + char *msg; + BOOL bForce = FALSE; + // @pyparm string|msg||The message to write to the status bar. + // @pyparm int|bForce|0|A flag indicating if the message should be forced to the status bar, or written in idle time. + if (!PyArg_ParseTuple(args,"s|i:SetStatusText",&msg, &bForce)) + return NULL; + + // If the glue wants it, the glue can have it :-) + if (pHostGlue && pHostGlue->bWantStatusBarText) { + pHostGlue->SetStatusText(msg, bForce); + RETURN_NONE; + } + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + CWnd *pWnd = pApp->GetMainFrame(); + if (pWnd==NULL) + RETURN_ERR("There is no main window"); + + CWnd *pStatusBar = pWnd->GetDlgItem(AFX_IDW_STATUS_BAR); + // Check for NULL or invalid handle. (GetSafe.. does the NULL!) + if (!::IsWindow(pStatusBar->GetSafeHwnd())) + RETURN_ERR("The status bar window is invalid"); + + GUI_BGN_SAVE; + pStatusBar->SetWindowText(msg); + GUI_END_SAVE; + if (bForce) { + pStatusBar->InvalidateRect(NULL); + pStatusBar->UpdateWindow(); + } + RETURN_NONE; +} + +// @pymethod list|win32ui|GetRecentFileList|Returns the entries in the applications Recent File List. +static PyObject * +ui_get_recent_file_list(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetRecentFileList); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + int cnt = pApp->GetProfileInt("Settings", "Recent File List Size", _AFX_MRU_COUNT); + PyObject *list = PyList_New(cnt); + if (list==NULL) { + PyErr_SetString(PyExc_MemoryError, "Allocating list for MRU documents"); + return NULL; + } + for (int i=0;iGetRecentFileName(i)); + // hack to non-const for Python + char *name = (char *)(const char *)csFile; + PyList_SetItem(list,i,PyString_FromString(name)); + } + return list; + // @rdesc A list of strings containing the fully qualified file names. +} +// @pymethod |win32ui|AddToRecentFileList|Adds an entry to the applications Recent File List. +static PyObject * +ui_add_to_recent_file_list(PyObject *self, PyObject *args) +{ + // @pyparm string|fileName||The file name to be added to the list. + char *msg; + if (!PyArg_ParseTuple(args,"s:AddToRecentFileList",&msg)) + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + pApp->AddToRecentFileList(msg); // @pyseemfc CWinApp|AddToRecentFileList + RETURN_NONE; +} + +// @pymethod |win32ui|RemoveRecentFile|Removes the entry in the applications Recent File List at index. +static PyObject * +ui_remove_recent_file(PyObject *self, PyObject *args) +{ + int index = 0; + // @pyparm int|index|0|Zero-based index of the file to be removed from the MRU (most recently used) file list. + if (!PyArg_ParseTuple(args,"i:RemoveRecentFile",&index)) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + pApp->RemoveRecentFile(index); + RETURN_NONE; +} + +// @pymethod |win32ui|GetMainFrame|Returns a window object for the main application frame. +static PyObject * +ui_get_main_frame(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetMainFrame); + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + CWnd *pFrame = pApp->GetMainFrame(); + if (!pFrame) + RETURN_ERR("The frame does not exist"); + // Do some RTTI on the object. + ui_type &makeType = UITypeFromCObject(pFrame); + return ui_assoc_object::make(makeType, pFrame)->GetGoodRet(); +} + +// @pymethod |win32ui|StartDebuggerPump|Starts a recursive message loop, waiting for an application close message. +int bIsPumping = FALSE; +BOOL bDebuggerPumpStopRequested = FALSE; +static PyObject * +ui_start_debugger_pump(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,StartDebuggerPump); + if (bIsPumping) + RETURN_ERR("Error starting debugger pumper - already pumping"); + bIsPumping = TRUE; + bDebuggerPumpStopRequested = FALSE; + CProtectedWinThread *pThread = GetProtectedThread(); + if (!pThread) return NULL; + GUI_BGN_SAVE; + pThread->PumpMessages(); + if (!bDebuggerPumpStopRequested) // App shutdown request. + PostQuitMessage(0); + GUI_END_SAVE; + RETURN_NONE; + // @comm This function is used by the debugger. It allows the debugger to + // interact with the user, even while the Python code is stopped. + // As the Python code may be responding to a Windows Event, this function + // works around the inherent message queue problems. +} +// @pymethod |win32ui|StopDebuggerPump|Stops the debugger pump. See . +static PyObject * +ui_stop_debugger_pump(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,StopDebuggerPump); + if (!bIsPumping) + RETURN_ERR("Error stopping debugger pumper - pump not started"); + bIsPumping = FALSE; + GUI_BGN_SAVE; + bDebuggerPumpStopRequested = TRUE; // Set this BEFORE QuitMessage + PostQuitMessage(0); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |win32ui|PumpWaitingMessages|Recursively start a new message dispatching loop while any message remain in the queue. +static PyObject * +ui_pump_waiting_messages(PyObject *self, PyObject *args) +{ + // @pyparm int|firstMessage|WM_PAINT|The lowest message ID to retrieve + // @pyparm int|lastMessage|WM_PAINT|The highest message ID to retrieve + UINT firstMsg = WM_PAINT, lastMsg = WM_PAINT; + if (!PyArg_ParseTuple (args, "|ii:PumpWaitingMessages", &firstMsg, &lastMsg)) + return NULL; + CProtectedWinThread *pThread = GetProtectedThread(); + if (!pThread) return NULL; + GUI_BGN_SAVE; + pThread->PumpWaitingMessages(firstMsg, lastMsg); + GUI_END_SAVE; + RETURN_NONE; + // @comm This allows an application which is performing a long operation to dispatch paint messages during the operation. +} +// @pymethod |win32ui|CreateDebuggerThread|Starts a debugging thread (ie, creates the "break" button). +static PyObject * +ui_create_debugger_thread(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,CreateDebuggerThread); + DWORD tid; + DWORD param = 0; + ::CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)DebuggerThreadFunc, ¶m, 0, &tid ); + RETURN_NONE; + // @comm This allows an application which is performing a long operation to dispatch paint messages during the operation. +} + +// @pymethod |win32ui|DestroyDebuggerThread|Cleans up the debugger thread. See . +static PyObject * +ui_destroy_debugger_thread(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,DestroyDebuggerThread); + extern void StopDebuggerThread(void); + GUI_BGN_SAVE; + StopDebuggerThread(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|win32ui|MessageBox|Display a message box. +static PyObject * +ui_message_box(PyObject * self, PyObject * args) +{ + char *message; + long style = MB_OK; + const char *title = NULL; + // @pyparm string|message||The message to be displayed in the message box. + // @pyparm string/None|title|None|The title for the message box. If None, the applications title will be used. + // @pyparm int|style|win32con.MB_OK|The style of the message box. + if (!PyArg_ParseTuple(args, "s|zl:MessageBox", &message, &title, &style)) + return NULL; + CWinApp *pApp = GetApp(); + if (pApp==NULL) return NULL; + + if (title==NULL) + title = pApp->m_pszAppName; + int rc; + GUI_BGN_SAVE; + rc = ::MessageBox(pApp->m_pMainWnd->GetSafeHwnd(), message, title, style); + GUI_END_SAVE; + return Py_BuildValue("i",rc); + // @rdesc An integer identifying the button pressed to dismiss the dialog. +} + +// @pymethod string|win32ui|FullPath|Return the fully qualified path of a file name. +static PyObject * +ui_full_path(PyObject * self, PyObject * args) +{ + char *path; + + // @pyparm string|path||The path name. + if (!PyArg_ParseTuple(args, "s:FullPath", &path)) + return NULL; + char szOutPath[_MAX_PATH]; + if (!GetFullPath(szOutPath, path)) + RETURN_ERR("The file name is invalid"); + return Py_BuildValue("s", szOutPath); +} + +// @pymethod int|win32ui|ComparePath|Compares 2 paths. +static PyObject * +ui_compare_path(PyObject * self, PyObject * args) +{ + BOOL AFXAPI AfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2); + char *path1, *path2; + // @pyparm string|path1||The path name. + // @pyparm string|path2||The path name. + if (!PyArg_ParseTuple(args, "ss:ComparePath", &path1, &path2)) + return NULL; + return Py_BuildValue("i", AfxComparePath(path1, path2)); +} + +// @pymethod string|win32ui|GetFileTitle|Given a file name, return its title +static PyObject * +ui_get_file_title(PyObject * self, PyObject * args) +{ + UINT AFXAPI AfxGetFileTitle(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax); + char *fname; + // @pyparm string|fileName||The file name. + if (!PyArg_ParseTuple(args, "s:GetFileTitle", &fname)) + return NULL; + char buf[_MAX_FNAME+1]; + if (AfxGetFileTitle(fname, buf, sizeof(buf))!=0) + RETURN_ERR("AfxGetFileTitle failed"); + return PyString_FromString(buf); +} + + +// @pymethod |win32ui|DoWaitCursor|Dispay a wait cursor. +static PyObject * +ui_do_wait_cursor(PyObject * self, PyObject * args) +{ + int code; + // @pyparm int|code||If this parameter is 0, the original cursor is restored. If 1, a wait cursor appears. If -1, the wait cursor ends. + if (!PyArg_ParseTuple(args, "i:DoWaitCursor", &code)) + return NULL; + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + + GUI_BGN_SAVE; + pApp->DoWaitCursor(code); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod object|win32ui|InstallCallBackCaller|Install a Python method which will dispatch all callbacks into Python. +static PyObject * +ui_install_callback_caller(PyObject *self, PyObject *args) +{ + PyObject *caller = NULL; + if (!PyArg_ParseTuple(args,"|O:InstallCallBackCaller",&caller)) + return NULL; + PyObject *retval = pCallbackCaller; + if (caller==Py_None) + caller = NULL; + Py_XDECREF(pCallbackCaller); + if (caller) { + if (!PyCallable_Check(caller)) + RETURN_ERR("Argument must be a callable object"); + pCallbackCaller = caller; + Py_INCREF(caller); + } else + pCallbackCaller = NULL; + if (retval) + return Py_BuildValue("O", retval); + else + RETURN_NONE; + // @rdesc The previous callback caller. +} + +// @pymethod int|win32ui|IsWin32s|Determines if the application is running under Win32s. +static PyObject * +ui_is_win32s(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,IsWin32s); + return Py_BuildValue("i", IsWin32s()); +} +// @pymethod int|win32ui|IsObject|Determines if the passed object is a win32ui object. +static PyObject * +ui_is_object(PyObject *self, PyObject *args) +{ + PyObject *obj; + // @pyparm object|o||The object to check. + if (!PyArg_ParseTuple(args, "O:IsObject", &obj)) + return NULL; + return Py_BuildValue("i", ui_base_class::is_nativeuiobject(obj,&ui_base_class::type) ? 1 : 0 ); +} + +// @pymethod |win32ui|GetResource|Retrieve the object associated with the applications resources. +static PyObject * +ui_get_resource(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetResource); + HINSTANCE dll = AfxGetResourceHandle(); + dll_object *ret = (dll_object *)ui_assoc_object::make(dll_object::type, dll)->GetGoodRet(); + return ret; +} + +// @pymethod |win32ui|LoadString|Loads a string from a resource file. +static PyObject *ui_load_string(PyObject *self, PyObject *args) +{ + UINT stringId; + if ( !PyArg_ParseTuple(args, "i", + &stringId)) // @pyparm int|stringId||The ID of the string to load. + return NULL; + CString ret; + if (!ret.LoadString(stringId)) + RETURN_API_ERR("LoadString failed"); + const TCHAR *data = ret; + return PyWinObject_FromTCHAR((TCHAR *)data, ret.GetLength()); +} + +// @pymethod |win32ui|SetResource|Specifies the default DLL object for application resources. +static PyObject * +ui_set_resource(PyObject *self, PyObject *args) +{ + PyObject *obDLL; + HINSTANCE hMod; + if (!PyArg_ParseTuple(args,"O:SetResource", + &obDLL)) // @pyparm |dll||The dll object to use for default resources. + return NULL; + if (!ui_base_class::is_uiobject(obDLL, &dll_object::type)) + RETURN_TYPE_ERR("passed object must be a PyDLL"); + hMod = ((dll_object *)obDLL)->GetDll(); + if (hMod==NULL) + RETURN_ERR("Can not set resource to an uninitialised DLL object"); + // setup for return value + HINSTANCE oldDll = AfxGetResourceHandle(); + dll_object *ret = (dll_object *)ui_assoc_object::make(dll_object::type, oldDll)->GetGoodRet(); + AfxSetResourceHandle(hMod); + return ret; + // @rdesc The previous default DLL object. +} + +// @pymethod |win32ui|WinHelp|Invokes the Windows Help system. +static PyObject * +ui_win_help( PyObject *self, PyObject *args ) +{ + UINT cmd = HELP_CONTEXT; + PyObject *dataOb; + DWORD data; + if (!PyArg_ParseTuple(args, "iO:WinHelp", + &cmd, // @pyparm int|cmd|win32con.HELP_CONTEXT|The type of help. See the api for full details. + &dataOb)) // @pyparm int/string|data||Additional data specific to the help call. + return NULL; + if (PyString_Check(dataOb)) + data = (DWORD)PyString_AsString(dataOb); + else if (PyInt_Check(dataOb)) + data = (DWORD)PyInt_AsLong(dataOb); + else { + RETURN_TYPE_ERR("First argument must be a string or an integer."); + } + CWinApp *pApp = GetApp(); + if (!pApp) return NULL; + + GUI_BGN_SAVE; + pApp->WinHelp(data, cmd); + GUI_END_SAVE; + Py_INCREF(Py_None); + return Py_None; +} + +// @pymethod int|win32ui|SetAppHelpPath|Set the pApp->m_pszHelpFilePath variable. +static PyObject * +ui_set_app_help_path(PyObject * self, PyObject * args) +{ + char *name; + long style = MB_OK; + if (!PyArg_ParseTuple(args, "s:SetAppHelpPath", &name)) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (pApp==NULL) return NULL; + + GUI_BGN_SAVE; + free((void*)pApp->m_pszHelpFilePath); + pApp->m_pszHelpFilePath=_tcsdup(_T(name)); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |win32ui|SetRegistryKey|Causes application settings to be stored in the registry instead of INI files. +static PyObject * +ui_set_registry_key(PyObject *self, PyObject *args) +{ + char *szKey; + if (!PyArg_ParseTuple(args,"s:SetRegistryKey",&szKey)) // @pyparm string|key||A string containing the name of the key. + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + GUI_BGN_SAVE; + pApp->SetRegistryKey(szKey); + GUI_END_SAVE; + // @comm Causes application settings to be stored in the registry instead of INI files. This function sets m_pszRegistryKey, which + // is then used by the GetProfileXXX and WriteProfileXXX member functions of CWinApp. If this function has been + // called, the list of most recently-used (MRU) files is also stored in the registry. The registry key is usually the name of a + // company. It is stored in a key of the following form: + // HKEY_CURRENT_USER\\Software\\\\\\\\\
\\\. + RETURN_NONE; +} + +// @pymethod |win32ui|GetAppRegistryKey|Returns the registry key for the application. +static PyObject * +ui_get_app_registry_key(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":SetRegistryKey")) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + + /* Avoid MFC assertion */ + if (pApp->m_pszRegistryKey == NULL || pApp->m_pszProfileName==NULL) + RETURN_ERR("There is no registry key open"); + + GUI_BGN_SAVE; + HKEY hk = pApp->GetAppRegistryKey(); + GUI_END_SAVE; + if (hk==0) RETURN_ERR("There is no registry key open"); + return PyWinObject_FromHKEY(hk); +} + +// @pymethod int|win32ui|SetDialogBkColor|Sets the default background and text color for dialog boxes and message boxes within the application. +static PyObject * +ui_set_dialog_bk_color(PyObject *self, PyObject *args) +{ + int clrCtlBk = RGB(192, 192, 192); + int clrCtlText = RGB(0, 0, 0); + + // @pyparm int|clrCtlBk|win32ui.RGB(192, 192, 192)|The color for the controls background. + // @pyparm int|clrCtlText|win32ui.RGB(0, 0, 0)|The color for the controls text. + if (!PyArg_ParseTuple(args,"|ii:SetDialogBkColor", &clrCtlBk, &clrCtlText)) + return NULL; + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + GUI_BGN_SAVE; + pApp->SetDialogBkColor(clrCtlBk, clrCtlText); + GUI_END_SAVE; + RETURN_NONE; + // @pyseemfc CWinApp|SetDialogBkColor +} + +// @pymethod int|win32ui|EnableControlContainer|Enables support for containment of OLE controls. +static PyObject * +ui_enable_control_container(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":EnableControlContainer")) + return NULL; + GUI_BGN_SAVE; + AfxEnableControlContainer(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|win32ui|GetAppName|Returns the application name. +static PyObject * +ui_get_app_name(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":GetAppName")) + return NULL; + return Py_BuildValue("s", AfxGetAppName()); +} + +// @pymethod int|win32ui|SetAppName|Sets the name of the application. +static PyObject * +ui_set_app_name(PyObject * self, PyObject * args) +{ + char *name; + long style = MB_OK; + const char *title = NULL; + // @pyparm string|appName||The new name for the application. This is used for the default registry key, and the title bar of the application. + if (!PyArg_ParseTuple(args, "s:SetAppName", &name)) + return NULL; + CWinApp *pApp = GetApp(); + if (pApp==NULL) return NULL; + + GUI_BGN_SAVE; + free((void*)pApp->m_pszAppName); + pApp->m_pszAppName=_tcsdup(_T(name)); + GUI_END_SAVE; + RETURN_NONE; + // @pyseemfc CWinApp|m_pszAppName +} + +// @pymethod int|win32ui|IsDebug|Returns a flag indicating if the current win32ui build is a DEBUG build. +static PyObject * +ui_is_debug(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":IsDebug")) + return NULL; +#ifdef _DEBUG + return PyInt_FromLong(1); +#else + return PyInt_FromLong(0); +#endif + // @comm This should not normally be of relevance to the Python + // programmer. However, under certain circumstances Python code may + // wish to detect this. +} + +// @pymethod string|win32ui|RegisterWndClass|Registers a window class +static PyObject * +ui_register_wnd_class(PyObject *self, PyObject *args) +{ + long style; + long hCursor = 0, hBrush = 0, hIcon = 0; + if (!PyArg_ParseTuple(args,"l|lll:RegisterWndClass", + &style, // @pyparm int|style||Specifies the Windows class style or combination of styles + &hCursor, // @pyparm int|hCursor|0| + &hBrush, // @pyparm int|hBrush|0| + &hIcon)) // @pyparm int|hIcon|0| + return NULL; + + GUI_BGN_SAVE; + LPCTSTR ret = AfxRegisterWndClass( style, (HCURSOR)hCursor, (HBRUSH)hBrush, (HICON)hIcon); + GUI_END_SAVE; + return PyString_FromString(ret); + // @comm The Microsoft Foundation Class Library automatically registers several standard window classes for you. Call this function if you want to register your own window classes. +} + +// @pymethod |win32ui|GetThread|Retrieves the current thread object. +static PyObject * +ui_get_thread(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,GetThread); + CWinThread *pThread = AfxGetThread(); + if (pThread==NULL) + RETURN_ERR("AfxGetThread failed"); + return ui_assoc_object::make(PyCWinThread::type, pThread)->GetGoodRet(); +} + +// @pymethod object|win32ui|GetType|Retrieves a Python Type object given its name +static PyObject * +ui_get_type(PyObject *self, PyObject *args) +{ + extern ui_type_CObject *UITypeFromName( const char *name ); + char *name; + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + PyObject *ret = (PyObject *)UITypeFromName(name); + if (ret==NULL) + RETURN_ERR("There is no type with that name"); + Py_INCREF(ret); + return ret; +} + +// @pymethod int|win32ui|SetCurrentInstanceHandle|Sets the MFC variable afxCurrentInstanceHandle +static PyObject * +ui_set_afxCurrentInstanceHandle(PyObject *self, PyObject *args) +{ + HMODULE newVal; + // @pyparm int|newVal||The new value for afxCurrentInstanceHandle + if (!PyArg_ParseTuple(args, "l", &newVal)) + return NULL; + HMODULE old = afxCurrentInstanceHandle; + afxCurrentInstanceHandle = newVal; + return PyInt_FromLong((long)old); + // @rdesc The result is the previous value of afxCurrentInstanceHandle +} + +// @pymethod int|win32ui|SetCurrentResourceHandle|Sets the MFC variable afxCurrentResourceHandle +static PyObject * +ui_set_afxCurrentResourceHandle(PyObject *self, PyObject *args) +{ + HMODULE newVal; + // @pyparm int|newVal||The new value for afxCurrentResourceHandle + if (!PyArg_ParseTuple(args, "l", &newVal)) + return NULL; + HMODULE old = afxCurrentResourceHandle; + afxCurrentResourceHandle = newVal; + return PyInt_FromLong((long)old); + // @rdesc The result is the previous value of afxCurrentResourceHandle +} + +// @pymethod string|win32ui|GetBytes|Gets raw bytes from memory +static PyObject *ui_get_bytes(PyObject *self, PyObject *args) +{ + long address; + int size; + // @pyparm int|address||The memory address + // @pyparm int|size||The size to get. + // @comm This method is useful to help decode unknown notify messages. + // You must be very carefull when using this method. + // @rdesc The result is a string with a length of size. + if (!PyArg_ParseTuple(args, "li|GetBytes", &address, &size)) + return NULL; + return PyString_FromStringAndSize((char *)address, size); +} +// @pymethod string|win32ui|InitRichEdit|Initializes the rich edit framework. +static PyObject *ui_init_rich_edit(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + GUI_BGN_SAVE; + BOOL ok = AfxInitRichEdit(); + GUI_END_SAVE; + if (!ok) RETURN_ERR("AfxInitRichEdit failed"); + RETURN_NONE; +} + +extern PyObject *ui_get_dialog_resource( PyObject *, PyObject *args ); +extern PyObject *ui_create_app( PyObject *, PyObject *args ); +extern PyObject *ui_get_app( PyObject *, PyObject *args ); +extern PyObject *PyCButton_create(PyObject *self, PyObject *args); +extern PyObject *PyCEdit_create(PyObject *self, PyObject *args); +extern PyObject *PyCProgressCtrl_create(PyObject *self, PyObject *args); +extern PyObject *PyCSliderCtrl_create(PyObject *self, PyObject *args); +extern PyObject *PyCStatusBarCtrl_create(PyObject *self, PyObject *args); +extern PyObject *PyCToolBarCtrl_create(PyObject *self, PyObject *args); +extern PyObject *ui_window_create(PyObject *self, PyObject *args); +extern PyObject *PyCImageList_Create(PyObject *self, PyObject *args); +extern PyObject *PyCRichEditCtrl_create(PyObject *self, PyObject *args); +extern PyObject *win32uiCreatePalette(PyObject *self, PyObject *args); +extern PyObject *ui_create_dc_from_handle (PyObject *self, PyObject *args); +extern PyObject *ui_create_frame(PyObject *self, PyObject *args); +extern PyObject *ui_get_halftone_brush(PyObject *self, PyObject *args); +extern PyObject *PyCTreeCtrl_create(PyObject *self, PyObject *args); +extern PyObject *PyCListCtrl_create(PyObject *self, PyObject *args); + +/* List of functions exported by this module */ + +// @module win32ui|A module, encapsulating the Microsoft Foundation Classes. +static struct PyMethodDef ui_functions[] = { + {"AddToRecentFileList", ui_add_to_recent_file_list, 1}, // @pymeth AddToRecentFileList|Add a file name to the Recent File List. + {"ComparePath", ui_compare_path, 1}, // @pymeth ComparePath|Compares 2 paths. + {"CreateMDIFrame", ui_create_mdi_frame, 1}, // @pymeth CreateMDIFrame|Creates an MDI Frame window. + {"CreateMDIChild", ui_create_mdi_child, 1}, // @pymeth CreateMDIChild|Creates an MDI Child window. + {"CreateBitmap", ui_bitmap::create, 1}, // @pymeth CreateBitmap|Create a bitmap object. + {"CreateBitmapFromHandle", ui_bitmap::create_from_handle, 1}, // @pymeth CreateBitmapFromHandle|Creates a bitmap object from a HBITMAP. + {"CreateBrush", PyCBrush::create, 1}, // @pymeth CreateBrush|Creates a new GDI brush object. Returns a object. + {"CreateButton", PyCButton_create, 1}, // @pymeth CreateButton|Creates a button object. creates the actual control. + {"CreateColorDialog", PyCColorDialog::create, 1}, // @pymeth CreateColorDialog|Creates a color selection dialog box. + {"CreateControl", PyCWnd::CreateControl, 1}, // @pymeth CreateControl|Creates an OLE control. + {"CreateControlBar", PyCControlBar::create, 1}, // @pymeth CreateControlBar|Creates an ControlBar + {"CreateCtrlView", PyCCtrlView::create, 1}, // @pymeth CreateCtrlView|Creates a control view object. + {"CreateDC", ui_dc_object::create_dc, 1}, // @pymeth CreateDC|Creates a object. + {"CreateDCFromHandle", ui_create_dc_from_handle, 1}, // @pymeth CreateDCFromHandle|Creates a object from an integer handle. + {"CreateDialog", PyCDialog::create, 1}, // @pymeth CreateDialog|Creates a object. + {"CreateDialogBar", PyCDialogBar::create, 1}, // @pymeth CreateDialogBar|Creates a object. + {"CreateDialogIndirect", PyCDialog::createIndirect, 1}, // @pymeth CreateDialogIndirect|Creates a object from a template. + {"CreatePrintDialog", PyCPrintDialog::create, 1}, // @pymeth CreatePrintDialog|Creates a object. + {"CreateDocTemplate", PyCDocTemplate::create, 1}, // @pymeth CreateDocTemplate|Create a object. + {"CreateEdit", PyCEdit_create, 1}, // @pymeth CreateEdit|Creates an edit object. creates the actual control. + {"CreateFileDialog", PyCFileDialog::ui_file_dialog_create, 1}, // @pymeth CreateFileDialog|Creates a FileOpen common dialog. + {"CreateFontDialog", PyCFontDialog::ui_font_dialog_create, 1}, // @pymeth CreateFontDialog|Creates a font selection dialog box. + {"CreateFormView", PyCFormView::create, 1}, // @pymeth CreateFormView|Creates a form view object. + {"CreateFrame", ui_create_frame, 1}, // @pymeth CreateFrame|Creates a frame window. + {"CreateImageList", PyCImageList_Create, 1}, // @pymeth CreateImageList|Creates an object. + {"CreateListCtrl", PyCListCtrl_create, 1}, // @pymeth CreateListCtrl|Creates a list control. + {"CreateListView", PyCListView::create, 1}, // @pymeth CreateListView|Creates a object. + {"CreateTreeCtrl", PyCTreeCtrl_create, 1}, // @pymeth CreateTreeCtrl|Creates a tree control. + {"CreateTreeView", PyCTreeView::create, 1}, // @pymeth CreateTreeView|Creates a object. + {"CreatePalette", win32uiCreatePalette, 1}, // @pymeth CreatePalette|Returns a HPALETTE + {"CreatePopupMenu", PyCMenu::create_popup, 1}, // @pymeth CreatePopupMenu|Creates a popup menu. + {"CreateMenu", PyCMenu::create_menu, 1}, // @pymeth CreateMenu|Creates a menu + {"CreatePen", ui_pen_object::create, 1}, // @pymeth CreatePen|Creates a object. + {"CreateProgressCtrl", PyCProgressCtrl_create, 1}, // @pymeth CreateProgressCtrl|Creates a progress bar object. creates the actual control. + {"CreatePropertyPage", PyCPropertyPage::create, 1}, // @pymeth CreatePropertyPage|Creates a object. + {"CreatePropertyPageIndirect", PyCPropertyPage::createIndirect, 1}, // @pymeth CreatePropertyPageIndirect|Creates a object from a template. + {"CreatePropertySheet", PyCPropertySheet::create, 1}, // @pymeth CreatePropertySheet|Creates a object + {"CreateRichEditCtrl", PyCRichEditCtrl_create, 1}, // @pymeth CreateRichEditCtrl|Creates a rich edit control. + {"CreateRichEditDocTemplate", PyCRichEditDocTemplate::create, 1}, // @pymeth CreateRichEditDocTemplate|Create a object. + {"CreateRichEditView", PyCRichEditView::create, 1}, // @pymeth CreateRichEditView|Creates a object. + {"CreateSliderCtrl", PyCSliderCtrl_create, 1}, // @pymeth CreateSliderCtrl|Creates a slider control object. creates the actual control. + {"CreateSplitter", PyCSplitterWnd::create, 1}, // @pymeth CreateSplitter|Creates a splitter window. + {"CreateStatusBar", PyCStatusBar::create, 1}, // @pymeth CreateStatusBar|Creates a status bar object. + {"CreateStatusBarCtrl", PyCStatusBarCtrl_create, 1}, // @pymeth CreateStatusBarCtrl|Creates a new status bar control object. creates the actual control. + {"CreateFont", PyCFont::create, 1}, // @pymeth CreateFont|Creates a object. + {"CreateToolBar", PyCToolBar::create, 1}, // @pymeth CreateToolBar|Creates a toolbar object. + {"CreateToolBarCtrl", PyCToolBarCtrl_create, 1}, // @pymeth CreateToolBar|Creates a toolbar object. + {"CreateThread", PyCWinThread::create, 1}, // @pymeth CreateThread|Creates a object. + {"CreateView", PyCScrollView::create, 1}, // @pymeth CreateView|Creates a object. + {"CreateEditView", PyCEditView::create, 1}, // @pymeth CreateEditView|Creates an object. + {"CreateDebuggerThread", ui_create_debugger_thread, 1}, // @pymeth CreateDebuggerThread|Starts a debugging thread. + {"CreateWindowFromHandle", PyCWnd::CreateWindowFromHandle, 1}, // @pymeth CreateWindowFromHandle|Creates a from an integer containing a HWND + {"CreateWnd", ui_window_create, 1}, // @pymeth CreateWnd|Create a new unitialized object + {"DestroyDebuggerThread", ui_destroy_debugger_thread, 1}, // @pymeth DestroyDebuggerThread|Cleans up the debugger thread. + {"DoWaitCursor", ui_do_wait_cursor, 1}, // @pymeth DoWaitCursor|Changes the cursor to/from a wait cursor. + {"Enable3dControls", ui_enable_3d_controls, 1 }, // @pymeth Enable3dControls|Enables 3d controls for the application. + {"FindWindow", PyCWnd::FindWindow, 1}, // @pymeth FindWindow|Searches for the specified top-level window + {"FindWindowEx", PyCWnd::FindWindowEx, 1}, // @pymeth FindWindowEx|Searches for the specified top-level or child window + {"FullPath", ui_full_path, 1}, // @pymeth FullPath|Returns the full path name of the file. + {"GetActiveWindow", PyCWnd::GetActiveWindow, 1}, // @pymeth GetActiveWindow|Retrieves the active window. + {"GetApp", ui_get_app, 1 }, // @pymeth GetApp|Retrieves the application object. + {"GetAppName", ui_get_app_name, 1 }, // @pymeth GetAppName|Retrieves the name of the current application. + {"GetAppRegistryKey", ui_get_app_registry_key, 1}, // @pymeth GetAppRegistryKey|Returns the registry key for the application. + {"GetBytes", ui_get_bytes, 1}, // @pymeth GetBytes|Gets raw bytes from memory + {"GetCommandLine", ui_get_command_line, 1}, // @pymeth GetCommandLine|Returns the command line for hte application. + {"GetFileTitle", ui_get_file_title, 1}, // @pymeth GetFileTitle|Given a file name, return its title + {"GetFocus", PyCWnd::GetFocus, 1}, // @pymeth GetFocus|Retrieves the window with the focus. + {"GetForegroundWindow", PyCWnd::GetForegroundWindow, 1}, // @pymeth GetForegroundWindow|Retrieves the foreground window. + {"GetHalftoneBrush", ui_get_halftone_brush, 1}, // @pymeth GetHalftoneBrush|Returns a halftone brush. + {"GetInitialStateRequest", ui_get_initial_state_request, 1}, // @pymeth GetInitialStateRequest|Returns the requested state that the application start in. This is the same as the paramaters available to + {"GetMainFrame", ui_get_main_frame, 1}, // @pymeth GetMainFrame|Returns a window object for the main application frame. + {"GetName", ui_get_name, 1}, // @pymeth GetName|Returns the name of the current application. + {"GetProfileFileName", ui_get_profile_filename, 1}, // @pymeth GetProfileFileName|Returns the name of the INI file used by the application. + {"GetProfileVal", ui_get_profile_val, 1}, // @pymeth GetProfileVal|Returns a value from the applications INI file. + {"GetRecentFileList", ui_get_recent_file_list, 1}, // @pymeth GetRecentFileList|Returns the recent file list. + {"GetResource", ui_get_resource, 1}, // @pymeth GetResource|Gets a resource. + {"GetThread", ui_get_thread, 1 }, // @pymeth GetThread|Retrieves the current thread object. + {"GetType", ui_get_type, 1 }, // @pymeth GetType|Retrieves a Python Type object given its name + {"InitRichEdit", ui_init_rich_edit, 1}, // @pymeth InitRichEdit|Initializes the rich edit framework. + {"InstallCallbackCaller", ui_install_callback_caller, 1}, // @pymeth InstallCallbackCaller|Installs a callback caller. + {"IsDebug", ui_is_debug, 1}, // @pymeth IsDebug|Returns a flag indicating if the current win32ui build is a DEBUG build. + {"IsWin32s", ui_is_win32s, 1}, // @pymeth IsWin32s|Determines if the application is running under Win32s. + {"IsObject", ui_is_object, 1}, // @pymeth IsObject|Determines if the passed object is a win32ui object. + {"LoadDialogResource", ui_get_dialog_resource, 1}, // @pymeth LoadDialogResource|Loads a dialog resource, and returns a list detailing the objects. + {"LoadLibrary", dll_object::create, 1}, // @pymeth LoadLibrary|Creates a object. + {"LoadMenu", PyCMenu::load_menu, 1}, // @pymeth LoadMenu|Loads a menu. + {"LoadStdProfileSettings", ui_load_std_profile_settings, 1}, // @pymeth LoadStdProfileSettings|Loads standard application profile settings. + {"LoadString", ui_load_string, 1}, // @pymeth LoadString|Loads a string from a resource file. + {"MessageBox", ui_message_box, 1}, // @pymeth MessageBox|Displays a message box. + {"OutputDebug", ui_output_debug, 1}, + {"OutputDebugString", ui_output_debug, 1}, // @pymeth OutputDebugString|Writes output to the Windows debugger. + {"EnableControlContainer", ui_enable_control_container, 1, }, // @pymeth EnableControlContainer|Call this function in your application object's InitInstance function to enable support for containment of OLE controls. + {"PrintTraceback", ui_python_print_traceback, 1}, // @pymeth PrintTraceback|Prints a Traceback using the default Python traceback printer. + {"PumpWaitingMessages", ui_pump_waiting_messages, 1}, // @pymeth PumpWaitingMessages|Pumps all waiting messages to the application. + {"RegisterWndClass", ui_register_wnd_class, 1}, // @pymeth RegisterWndClass|Registers a window class + {"RemoveRecentFile", ui_remove_recent_file, 1}, // @pymeth RemoveRecentFile|Removes the recent file at list index. + {"SetAppHelpPath", ui_set_app_help_path, 1}, // @pymeth SetAppHelpPath|Sets the application help file path, i.e. the pApp->m_pszHelpFilePath member variable. + {"SetAppName", ui_set_app_name, 1}, // @pymeth SetAppName|Sets the application name. + {"SetCurrentInstanceHandle",ui_set_afxCurrentInstanceHandle, 1}, // @pymeth SetCurrentInstanceHandle|Sets the MFC variable afxCurrentInstanceHandle. + {"SetCurrentResourceHandle",ui_set_afxCurrentResourceHandle, 1}, // @pymeth SetCurrentResourceHandle|Sets the MFC variable afxCurrentResourceHandle. + {"SetDialogBkColor", ui_set_dialog_bk_color, 1}, // @pymeth SetDialogBkColor|Sets the default background and text color for dialog boxes and message boxes within the application. + {"SetProfileFileName", ui_set_profile_filename, 1}, // @pymeth SetProfileFileName|Sets the INI file name used by the application. + {"SetRegistryKey", ui_set_registry_key, 1 }, // @pymeth SetRegistryKey|Causes application settings to be stored in the registry instead of INI files. + {"SetResource", ui_set_resource, 1}, // @pymeth SetResource|Specifies the default DLL object for application resources. + {"SetStatusText", ui_set_status_text, 1}, // @pymeth SetStatusText|Sets the text in the status bar. + {"StartDebuggerPump", ui_start_debugger_pump, 1}, // @pymeth StartDebuggerPump|Starts the debugger message pump. + {"StopDebuggerPump", ui_stop_debugger_pump, 1}, // @pymeth StopDebuggerPump|Stops the debugger message pump. + {"WinHelp", ui_win_help, 1}, // @pymeth WinHelp|Invokes the Window Help engine. + {"WriteProfileVal", ui_write_profile_val, 1}, // @pymeth WriteProfileVal|Writes a value to the INI file. + + {NULL, NULL} +}; + +static int AddConstant(PyObject *dict, char *key, long value) +{ + PyObject *okey = PyString_FromString(key); + PyObject *oval = PyInt_FromLong(value); + if (!okey || !oval) { + XDODECREF(okey); + XDODECREF(oval); + return 1; + } + int rc = PyDict_SetItem(dict,okey, oval); + DODECREF(okey); + DODECREF(oval); + return rc; +} +#define ADD_CONSTANT(tok) if (rc=AddConstant(dict,#tok, tok)) return rc +#define ADD_ENUM(parta, partb) if (rc=AddConstant(dict,#parta "_" #partb, parta::partb)) return rc +#define ADD_ENUM3(parta, partb, partc) if (rc=AddConstant(dict,#parta "_" #partb "_" #partc, parta::partb::partc)) return rc + +int AddConstants(PyObject *dict) +{ + int rc; +#ifdef _DEBUG + int debug = 1; +#else + int debug = 0; +#endif + ADD_CONSTANT(debug); // @const win32ui|debug|1 if we are current using a _DEBUG build of win32ui, else 0. + ADD_CONSTANT(AFX_IDW_PANE_FIRST); // @const win32ui|AFX_IDW_PANE_FIRST|Id of the first splitter pane + ADD_CONSTANT(AFX_IDW_PANE_LAST); // @const win32ui|AFX_IDW_PANE_LAST|Id of the last splitter pane + ADD_CONSTANT(AFX_WS_DEFAULT_VIEW); // @const win32ui|AFX_WS_DEFAULT_VIEW| + ADD_CONSTANT(FWS_ADDTOTITLE); // @const win32ui|FWS_ADDTOTITLE|MFC Frame Window style extension. Add document title to window title. + ADD_CONSTANT(FWS_PREFIXTITLE); // @const win32ui|FWS_PREFIXTITLE|MFC Frame Window style extension. + ADD_CONSTANT(FWS_SNAPTOBARS); // @const win32ui|FWS_SNAPTOBARS|MFC Frame Window style extension. + + ADD_CONSTANT(IDD_ABOUTBOX); // @const win32ui|IDD_ABOUTBOX|Id of built in 'About Box' dialog + ADD_CONSTANT(IDD_DUMMYPROPPAGE); // @const win32ui|IDD_DUMMYPROPPAGE|Id of built in dummy property page + ADD_CONSTANT(IDD_PROPDEMO1); // @const win32ui|IDD_PROPDEMO1|Id of built in Property Page demo dialog 1 + ADD_CONSTANT(IDD_PROPDEMO2); // @const win32ui|IDD_PROPDEMO2|Id of built in Property Page demo dialog 2 + ADD_CONSTANT(IDB_DEBUGGER_HIER); // @const win32ui|IDB_DEBUGGER_HIER| + ADD_CONSTANT(IDB_HIERFOLDERS); // @const win32ui|IDB_HIERFOLDERS|Id of built in bitmap for default hierarchical list + ADD_CONSTANT(IDB_BROWSER_HIER); // @const win32ui|IDB_BROWSER_HIER|Id of built in bitmap for the browser + ADD_CONSTANT(IDD_GENERAL_STATUS); // @const win32ui|IDD_GENERAL_STATUS|Id of a general status dialog box (fairly small, 3 static controls, minimize box) + ADD_CONSTANT(IDD_LARGE_EDIT); // @const win32ui|IDD_LARGE_EDIT|Id of built in 'Large Edit' dialog (dialog box with a large edit control) + ADD_CONSTANT(IDD_TREE); // @const win32ui|IDD_TREE|Id of built in dialog with a tree control. + ADD_CONSTANT(IDD_TREE_MB);// @const win32ui|IDD_TREE_MB|Id of built in dialog with a tree control with multiple buttons. + ADD_CONSTANT(IDD_RUN_SCRIPT); // @const win32ui|IDD_RUN_SCRIPT|Id of built in 'Run Script' dialog + ADD_CONSTANT(IDD_PP_EDITOR); // @const win32ui|IDD_PP_EDITOR|Id of built in 'Editor' property page + ADD_CONSTANT(IDD_PP_DEBUGGER); // @const win32ui|IDD_PP_DEBUGGER| + ADD_CONSTANT(IDD_PP_FORMAT); // @const win32ui|IDD_PP_FORMAT|Id of built in 'Format' property page + ADD_CONSTANT(IDD_PP_IDE); // @const win32ui|IDD_PP_IDE|Id of built in 'IDE' property page + ADD_CONSTANT(IDD_PP_TOOLMENU); // @const win32ui|IDD_PP_TOOLMENU|Id of built in 'ToolsMenu' property page + ADD_CONSTANT(IDD_SIMPLE_INPUT); // @const win32ui|IDD_SIMPLE_INPUT|Id of built in 'Simple Input' property page. + ADD_CONSTANT(IDD_SET_TABSTOPS); // @const win32ui|IDD_SET_TABSTOPS|Id of built in 'Set Tab Stops' dialog + + ADD_CONSTANT(IDC_DBG_STEP); + ADD_CONSTANT(IDC_DBG_STEPOUT); + ADD_CONSTANT(IDC_DBG_STEPOVER); + ADD_CONSTANT(IDC_DBG_GO); + ADD_CONSTANT(IDC_DBG_ADD); + ADD_CONSTANT(IDC_DBG_CLEAR); + ADD_CONSTANT(IDC_DBG_CLOSE); + ADD_CONSTANT(IDC_DBG_STACK); + ADD_CONSTANT(IDC_DBG_BREAKPOINTS); + ADD_CONSTANT(IDC_DBG_WATCH); + + ADD_CONSTANT(IDC_ABOUT_VERSION); // @const win32ui|IDC_ABOUT_VERSION|Id of 'Version' control + ADD_CONSTANT(IDC_AUTO_RELOAD); // @const win32ui|IDC_AUTO_RELOAD| + ADD_CONSTANT(IDC_BUTTON1); // @const win32ui|IDC_BUTTON1| + ADD_CONSTANT(IDC_BUTTON2); // @const win32ui|IDC_BUTTON2| + ADD_CONSTANT(IDC_BUTTON3); // @const win32ui|IDC_BUTTON3| + ADD_CONSTANT(IDC_BUTTON4); // @const win32ui|IDC_BUTTON4| + ADD_CONSTANT(IDC_CHECK1); // @const win32ui|IDC_CHECK1| + ADD_CONSTANT(IDC_CHECK2); // @const win32ui|IDC_CHECK2| + ADD_CONSTANT(IDC_CHECK3); // @const win32ui|IDC_CHECK3| + ADD_CONSTANT(IDC_COMBO1); // @const win32ui|IDC_COMBO1| + ADD_CONSTANT(IDC_COMBO2); // @const win32ui|IDC_COMBO2| + ADD_CONSTANT(IDC_EDIT1); // @const win32ui|IDC_EDIT1| + ADD_CONSTANT(IDC_EDIT2);// @const win32ui|IDC_EDIT2| + ADD_CONSTANT(IDC_EDIT3);// @const win32ui|IDC_EDIT3| + ADD_CONSTANT(IDC_EDIT4);// @const win32ui|IDC_EDIT4| + ADD_CONSTANT(IDC_EDIT_TABS);// @const win32ui|IDC_EDIT_TABS| + ADD_CONSTANT(IDC_EDITOR_COLOR); // @const win32ui|IDC_EDIT_COLOE| + ADD_CONSTANT(IDC_INDENT_SIZE);// @const win32ui|IDC_INDENT_SIZE| + ADD_CONSTANT(IDC_KEYBOARD_CONFIG); // @const win32ui|IDC_KEYBOARD_CONFIG| + ADD_CONSTANT(IDC_LIST1);// @const win32ui|IDC_LIST1| + ADD_CONSTANT(IDC_PROMPT_TABS);// @const win32ui|IDC_PROMPT_TABS| + ADD_CONSTANT(IDC_PROMPT1);// @const win32ui|IDC_PROMPT1| + ADD_CONSTANT(IDC_PROMPT2);// @const win32ui|IDC_PROMPT2| + ADD_CONSTANT(IDC_PROMPT3);// @const win32ui|IDC_PROMPT3| + ADD_CONSTANT(IDC_PROMPT4);// @const win32ui|IDC_PROMPT4| + ADD_CONSTANT(IDC_RADIO1);// @const win32ui|IDC_RADIO1| + ADD_CONSTANT(IDC_RADIO2);// @const win32ui|IDC_RADIO2| + ADD_CONSTANT(IDC_VIEW_WHITESPACE);// @const win32ui|IDC_VIEW_WHITESPACE| + ADD_CONSTANT(IDC_AUTOCOMPLETE); // @const win32ui|IDC_AUTOCOMPLETE| + ADD_CONSTANT(IDC_CALLTIPS); // @const win32ui|IDC_CALLTIPS| + + + ADD_CONSTANT(IDC_SPIN1); // @const win32ui|IDC_SPIN1| + + ADD_CONSTANT(IDC_TAB_SIZE);// @const win32ui|IDC_TAB_SIZE| + ADD_CONSTANT(IDC_USE_TABS);// @const win32ui|IDC_USE_TABS| + ADD_CONSTANT(IDC_USE_SMART_TABS);// @const win32ui|IDC_USE_SMART_TABS| + ADD_CONSTANT(IDC_VSS_INTEGRATE);// @const win32ui|IDC_VSS_INTEGRATE| + + ADD_CONSTANT(ID_INDICATOR_LINENUM);// @const win32ui|ID_INDICATOR_LINENUM| + ADD_CONSTANT(ID_INDICATOR_COLNUM);// @const win32ui|ID_INDICATOR_COLNUM| + + ADD_CONSTANT(ID_FILE_NEW);// @const win32ui|ID_FILE_NEW| + ADD_CONSTANT(ID_FILE_OPEN);// @const win32ui|ID_FILE_OPEN| + ADD_CONSTANT(ID_FILE_CLOSE);// @const win32ui|ID_FILE_CLOSE| + ADD_CONSTANT(ID_FILE_RUN);// @const win32ui|ID_FILE_RUN| + ADD_CONSTANT(ID_FILE_IMPORT);// @const win32ui|ID_FILE_IMPORT| + ADD_CONSTANT(ID_FILE_LOCATE);// @const win32ui|ID_FILE_LOCATE| + ADD_CONSTANT(ID_FILE_CHECK);// @const win32ui|ID_FILE_CHECK| + ADD_CONSTANT(ID_FILE_SAVE);// @const win32ui|ID_FILE_SAVE| + ADD_CONSTANT(ID_FILE_SAVE_AS);// @const win32ui|ID_FILE_SAVE_AS| + ADD_CONSTANT(ID_FILE_SAVE_ALL);// @const win32ui|ID_FILE_SAVE_ALL| + + ADD_CONSTANT(ID_FILE_PAGE_SETUP);// @const win32ui|ID_FILE_PAGE_SETUP| + ADD_CONSTANT(ID_FILE_PRINT_SETUP);// @const win32ui|ID_FILE_PRINT_SETUP| + ADD_CONSTANT(ID_FILE_PRINT);// @const win32ui|ID_FILE_PRINT| + ADD_CONSTANT(ID_FILE_PRINT_PREVIEW);// @const win32ui|ID_FILE_PRINT_PREVIEW| + ADD_CONSTANT(ID_HELP_PYTHON);// @const win32ui|ID_HELP_PYTHON| + ADD_CONSTANT(ID_HELP_GUI_REF);// @const win32ui|ID_HELP_GUI_REF| + ADD_CONSTANT(ID_HELP_OTHER);// @const win32ui|ID_HELP_OTHER| + ADD_CONSTANT(ID_APP_ABOUT);// @const win32ui|ID_APP_ABOUT| + ADD_CONSTANT(ID_APP_EXIT);// @const win32ui|ID_APP_EXIT| + ADD_CONSTANT(ID_FILE_MRU_FILE1);// @const win32ui|ID_FILE_MRU_FILE1| + ADD_CONSTANT(ID_FILE_MRU_FILE2);// @const win32ui|ID_FILE_MRU_FILE2| + ADD_CONSTANT(ID_FILE_MRU_FILE3);// @const win32ui|ID_FILE_MRU_FILE3| + ADD_CONSTANT(ID_FILE_MRU_FILE4);// @const win32ui|ID_FILE_MRU_FILE4| + ADD_CONSTANT(ID_VIEW_BROWSE);// @const win32ui|ID_VIEW_BROWSE| + ADD_CONSTANT(ID_VIEW_FIXED_FONT);// @const win32ui|ID_VIEW_FIXED_FONT| + ADD_CONSTANT(ID_VIEW_INTERACTIVE);// @const win32ui|ID_VIEW_INTERACTIVE| + ADD_CONSTANT(ID_VIEW_OPTIONS); // @const win32ui|ID_VIEW_OPTIONS| + ADD_CONSTANT(ID_VIEW_TOOLBAR_DBG); // @const win32ui|ID_VIEW_TOOLBAR_DBG| + ADD_CONSTANT(ID_VIEW_WHITESPACE); // @const win32ui|ID_VIEW_WHITESPACE| + ADD_CONSTANT(ID_NEXT_PANE);// @const win32ui|ID_NEXT_PANE| + ADD_CONSTANT(ID_PREV_PANE);// @const win32ui|ID_PREV_PANE| + ADD_CONSTANT(ID_WINDOW_NEW);// @const win32ui|ID_WINDOW_NEW| + ADD_CONSTANT(ID_WINDOW_ARRANGE);// @const win32ui|ID_WINDOW_ARRANGE| + ADD_CONSTANT(ID_WINDOW_CASCADE);// @const win32ui|ID_WINDOW_CASCADE| + ADD_CONSTANT(ID_WINDOW_TILE_HORZ);// @const win32ui|ID_WINDOW_TILE_HORZ| + ADD_CONSTANT(ID_WINDOW_TILE_VERT);// @const win32ui|ID_WINDOW_TILE_VERT| + ADD_CONSTANT(ID_WINDOW_SPLIT);// @const win32ui|ID_WINDOW_SPLIT| + ADD_CONSTANT(ID_EDIT_CLEAR);// @const win32ui|ID_EDIT_CLEAR| + ADD_CONSTANT(ID_EDIT_CLEAR_ALL);// @const win32ui|ID_EDIT_CLEAR_ALL| + ADD_CONSTANT(ID_EDIT_COPY);// @const win32ui|ID_EDIT_COPY| + ADD_CONSTANT(ID_EDIT_CUT);// @const win32ui|ID_EDIT_CUT| + ADD_CONSTANT(ID_EDIT_FIND);// @const win32ui|ID_EDIT_FIND| + ADD_CONSTANT(ID_EDIT_GOTO_LINE); // @const win32ui|ID_EDIT_GOTO_LINE| + ADD_CONSTANT(ID_EDIT_PASTE);// @const win32ui|ID_EDIT_PASTE| + ADD_CONSTANT(ID_EDIT_REPEAT);// @const win32ui|ID_EDIT_REPEAT| + ADD_CONSTANT(ID_EDIT_REPLACE);// @const win32ui|ID_EDIT_REPLACE| + ADD_CONSTANT(ID_EDIT_SELECT_ALL);// @const win32ui|ID_EDIT_SELECT_ALL| + ADD_CONSTANT(ID_EDIT_SELECT_BLOCK);// @const win32ui|ID_EDIT_SELECT_BLOCK| + ADD_CONSTANT(ID_EDIT_UNDO);// @const win32ui|ID_EDIT_UNDO| + ADD_CONSTANT(ID_EDIT_REDO);// @const win32ui|ID_EDIT_REDO| + ADD_CONSTANT(ID_VIEW_TOOLBAR);// @const win32ui|ID_VIEW_TOOLBAR| + ADD_CONSTANT(ID_VIEW_STATUS_BAR);// @const win32ui|ID_VIEW_STATUS_BAR| + ADD_CONSTANT(ID_SEPARATOR);// @const win32ui|ID_SEPARATOR| + + ADD_CONSTANT(IDR_DEBUGGER); // @const win32ui|IDR_DEBUGGER| + ADD_CONSTANT(IDR_PYTHONTYPE_CNTR_IP);// @const win32ui|IDR_PYTHONTYPE_CNTR_IP| + ADD_CONSTANT(IDR_MAINFRAME);// @const win32ui|IDR_MAINFRAME| + ADD_CONSTANT(IDR_PYTHONTYPE);// @const win32ui|IDR_PYTHONTYPE| + ADD_CONSTANT(IDR_PYTHONCONTYPE);// @const win32ui|IDR_PYTHONCONTYPE| + ADD_CONSTANT(IDR_TEXTTYPE);// @const win32ui|IDR_TEXTTYPE| + ADD_CONSTANT(IDR_CNTR_INPLACE);// @const win32ui|IDR_CNTR_INPLACE| + ADD_ENUM(CDocTemplate,windowTitle);// @const win32ui|CDocTemplate_windowTitle| + ADD_ENUM(CDocTemplate,docName);// @const win32ui|CDocTemplate_docName| + ADD_ENUM(CDocTemplate,fileNewName);// @const win32ui|CDocTemplate_fileNewName| + ADD_ENUM(CDocTemplate,filterName);// @const win32ui|CDocTemplate_filterName| + ADD_ENUM(CDocTemplate,filterExt);// @const win32ui|CDocTemplate_filterExt| + ADD_ENUM(CDocTemplate,regFileTypeId);// @const win32ui|CDocTemplate_regFileTypeId| + ADD_ENUM(CDocTemplate,regFileTypeName);// @const win32ui|CDocTemplate_regFileTypeName| + + ADD_ENUM3(CDocTemplate, Confidence, noAttempt); // @const win32ui|CDocTemplate_Confidence_noAttempt| + ADD_ENUM3(CDocTemplate, Confidence, maybeAttemptForeign); // @const win32ui|CDocTemplate_Confidence_maybeAttemptForeign| + ADD_ENUM3(CDocTemplate, Confidence, maybeAttemptNative); // @const win32ui|CDocTemplate_Confidence_maybeAttemptNative| + ADD_ENUM3(CDocTemplate, Confidence, yesAttemptForeign); // @const win32ui|CDocTemplate_Confidence_yesAttemptForeign| + ADD_ENUM3(CDocTemplate, Confidence, yesAttemptNative); // @const win32ui|CDocTemplate_Confidence_yesAttemptNative| + ADD_ENUM3(CDocTemplate, Confidence, yesAlreadyOpen); // @const win32ui|CDocTemplate_Confidence_yesAlreadyOpen| + + ADD_ENUM(CRichEditView,WrapNone);// @const win32ui|CRichEditView_WrapNone| + ADD_ENUM(CRichEditView,WrapToWindow);// @const win32ui|CRichEditView_WrapToWindow| + ADD_ENUM(CRichEditView,WrapToTargetDevice);// @const win32ui|CRichEditView_WrapToTargetDevice| + + ADD_CONSTANT(PD_ALLPAGES); // @const win32ui|PD_ALLPAGES|The default flag that indicates that the All radio button is initially selected. This flag is used as a placeholder to indicate that the PD_PAGENUMS and PD_SELECTION flags are not specified. + ADD_CONSTANT(PD_COLLATE); // @const win32ui|PD_COLLATE|If this flag is set, the Collate check box is checked. If this flag is set when the PrintDlg function returns, the application must simulate collation of multiple copies. For more information, see the description of the PD_USEDEVMODECOPIESANDCOLLATE flag. + ADD_CONSTANT(PD_DISABLEPRINTTOFILE); // @const win32ui|PD_DISABLEPRINTTOFILE|Disables the Print to File check box. + ADD_CONSTANT(PD_ENABLEPRINTHOOK); // @const win32ui|PD_ENABLEPRINTHOOK|Enables the hook procedure specified in the lpfnPrintHook member. This enables the hook procedure for the Print dialog box. + ADD_CONSTANT(PD_ENABLEPRINTTEMPLATE); // @const win32ui|PD_ENABLEPRINTTEMPLATE|PD_ENABLEPRINTTEMPLATE + ADD_CONSTANT(PD_ENABLEPRINTTEMPLATEHANDLE); // @const win32ui|PD_ENABLEPRINTTEMPLATEHANDLE|Indicates that the hPrintTemplate member identifies a data block that contains a preloaded dialog box template. This template replaces the default template for the Print dialog box. The system ignores the lpPrintTemplateName member if this flag is specified. + ADD_CONSTANT(PD_ENABLESETUPHOOK); // @const win32ui|PD_ENABLESETUPHOOK|Enables the hook procedure specified in the lpfnSetupHook member. This enables the hook procedure for the Print Setup dialog box. + ADD_CONSTANT(PD_ENABLESETUPTEMPLATE); // @const win32ui|PD_ENABLESETUPTEMPLATE|Indicates that the hInstance and lpSetupTemplateName members specify a replacement for the default Print Setup dialog box template. + ADD_CONSTANT(PD_ENABLESETUPTEMPLATEHANDLE); // @const win32ui|PD_ENABLESETUPTEMPLATEHANDLE|Indicates that the hSetupTemplate member identifies a data block that contains a preloaded dialog box template. This template replaces the default template for the Print Setup dialog box. The system ignores the lpSetupTemplateName member if this flag is specified. + ADD_CONSTANT(PD_HIDEPRINTTOFILE); // @const win32ui|PD_HIDEPRINTTOFILE|Hides the Print to File check box. + ADD_CONSTANT(PD_NONETWORKBUTTON); // @const win32ui|PD_NONETWORKBUTTON|Hides and disables the Network button. + ADD_CONSTANT(PD_NOPAGENUMS); // @const win32ui|PD_NOPAGENUMS|Disables the Pages radio button and the associated edit controls. + ADD_CONSTANT(PD_NOSELECTION); // @const win32ui|PD_NOSELECTION|Disables the Selection radio button. + ADD_CONSTANT(PD_NOWARNING); // @const win32ui|PD_NOWARNING|Prevents the warning message from being displayed when there is no default printer. + ADD_CONSTANT(PD_PAGENUMS); // @const win32ui|PD_PAGENUMS|If this flag is set, the Pages radio button is selected. If this flag is set when the PrintDlg function returns, the nFromPage and nFromPage members indicate the starting and ending pages specified by the user. + ADD_CONSTANT(PD_PRINTSETUP); // @const win32ui|PD_PRINTSETUP|Causes the system to display the Print Setup dialog box rather than the Print dialog box. + ADD_CONSTANT(PD_PRINTTOFILE); // @const win32ui|PD_PRINTTOFILE|If this flag is set, the Print to File check box is selected. If this flag is set when the PrintDlg function returns, the offset indicated by the wOutputOffset member of the DEVNAMES structure contains the string "FILE:". When you call theStartDoc function to start the printing operation, specify this "FILE:" string in the lpszOutput member of theDOCINFO structure. Specifying this string causes the print subsystem to query the user for the name of the output file. + ADD_CONSTANT(PD_RETURNDC); // @const win32ui|PD_RETURNDC|Causes PrintDlg to return a device context matching the selections the user made in the dialog box. The device context is returned in hDC. + ADD_CONSTANT(PD_RETURNDEFAULT); // @const win32ui|PD_RETURNDEFAULT|If this flag is set, the PrintDlg function does not display the dialog box. Instead, it sets the hDevNames and hDevMode members to handles toDEVMODE and DEVNAMES structures that are initialized for the system default printer. Both hDevNames and hDevMode must be NULL, or PrintDlg returns an error. If the system default printer is supported by an old printer driver (earlier than Windows version 3.0), only hDevNames is returned; hDevMode is NULL. + ADD_CONSTANT(PD_RETURNIC); // @const win32ui|PD_RETURNIC|Similar to the PD_RETURNDC flag, except this flag returns an information context rather than a device context. If neither PD_RETURNDC nor PD_RETURNIC is specified, hDC is undefined on output. + ADD_CONSTANT(PD_SELECTION); // @const win32ui|PD_SELECTION|If this flag is set, the Selection radio button is selected. If neither PD_PAGENUMS nor PD_SELECTION is set, the All radio button is selected. + ADD_CONSTANT(PD_SHOWHELP); // @const win32ui|PD_SHOWHELP|Causes the dialog box to display the Help button. The hwndOwner member must specify the window to receive the HELPMSGSTRING registered messages that the dialog box sends when the user clicks the Help button. + ADD_CONSTANT(PD_USEDEVMODECOPIES); // @const win32ui|PD_USEDEVMODECOPIES|Same as PD_USEDEVMODECOPIESANDCOLLATE + ADD_CONSTANT(PD_USEDEVMODECOPIESANDCOLLATE); // @const win32ui|PD_USEDEVMODECOPIESANDCOLLATE|This flag indicates whether your application supports multiple copies and collation. Set this flag on input to indicate that your application does not support multiple copies and collation. In this case, the nCopies member of the PRINTDLG structure always returns 1, and PD_COLLATE is never set in the Flags member. If this flag is not set, the application is responsible for printing and collating multiple copies. In this case, the nCopies member of the PRINTDLG structure indicates the number of copies the user wants to print, and the PD_COLLATE flag in the Flags member indicates whether the user wants collation. Regardless of whether this flag is set, an application can determine from nCopies and PD_COLLATE how many copies to render and whether to print them collated. If this flag is set and the printer driver does not support multiple copies, the Copies edit control is disabled. Similarly, if this flag is set and the printer driver does not support collation, the Collate checkbox is disabled. The dmCopies and dmCollate members of theDEVMODE structure contain the copies and collate information used by the printer driver. If this flag is set and the printer driver supports multiple copies, the dmCopies member indicates the number of copies requested by the user. If this flag is set and the printer driver supports collation, the dmCollate member of the DEVMODE structure indicates whether the user wants collation. If this flag is not set, the dmCopies member always returns 1, and the dmCollate member is always zero. + + ADD_CONSTANT(PSWIZB_BACK); // @const win32ui|PSWIZB_BACK|Enable/Disable the Property sheet Back button + ADD_CONSTANT(PSWIZB_NEXT); // @const win32ui|PSWIZB_NEXT|Enable/Disable the Property sheet Next button + ADD_CONSTANT(PSWIZB_FINISH); // @const win32ui|PSWIZB_FINISH|Enable/Disable the Property sheet Finish button + ADD_CONSTANT(PSWIZB_DISABLEDFINISH); // @const win32ui|PSWIZB_DISABLEDFINISH|Enable/Disable the Property sheet disabled Finish button + + ADD_CONSTANT(MFS_SYNCACTIVE); // @const win32ui|MFS_SYNCACTIVE|syncronize activation w/ parent + ADD_CONSTANT(MFS_4THICKFRAME); // @const win32ui|MFS_4THICKFRAME|thick frame all around (no tiles) + ADD_CONSTANT(MFS_THICKFRAME); // @const win32ui|MFS_THICKFRAME|use instead of WS_THICKFRAME + ADD_CONSTANT(MFS_MOVEFRAME); // @const win32ui|MFS_MOVEFRAME|no sizing, just moving + ADD_CONSTANT(MFS_BLOCKSYSMENU); // @const win32ui|MFS_BLOCKSYSMENU|block hit testing on system menu + + // Layout Modes for CalcDynamicLayout + ADD_CONSTANT(LM_STRETCH); // @const win32ui|LM_STRETCH|same meaning as bStretch in CalcFixedLayout. If set, ignores nLength and returns dimensions based on LM_HORZ state, otherwise LM_HORZ is used to determine if nLength is the desired horizontal or vertical length and dimensions are returned based on nLength + ADD_CONSTANT(LM_HORZ); // @const win32ui|LM_HORZ|same as bHorz in CalcFixedLayout + ADD_CONSTANT(LM_MRUWIDTH); // @const win32ui|LM_MRUWIDTH|Most Recently Used Dynamic Width + ADD_CONSTANT(LM_HORZDOCK); // @const win32ui|LM_HORZDOCK|Horizontal Docked Dimensions + ADD_CONSTANT(LM_VERTDOCK); // @const win32ui|LM_VERTDOCK|Vertical Docked Dimensions + ADD_CONSTANT(LM_LENGTHY); // @const win32ui|LM_LENGTHY|Set if nLength is a Height instead of a Width + ADD_CONSTANT(LM_COMMIT); // @const win32ui|LM_COMMIT|Remember MRUWidth + +/** + ADD_CONSTANT(); +***/ + return rc; +} + +extern bool CheckGoodWinApp(); +extern HINSTANCE hWin32uiDll; // Handle to this DLL, from dllmain.cpp + +/* Initialize this module. */ +extern "C" __declspec(dllexport) void +initwin32ui(void) +{ + if (!CheckGoodWinApp()) { + PyErr_SetString(PyExc_RuntimeError, "The win32ui module could not initialize the application object."); + return; + } + PyWinGlobals_Ensure(); + PyObject *dict, *module; + module = Py_InitModule(uiModName, ui_functions); + dict = PyModule_GetDict(module); + ui_module_error = PyString_FromString(errorName); + PyDict_SetItemString(dict, "error", ui_module_error); + PyObject *copyright = PyString_FromString("Copyright 1994-1999 Mark Hammond (MHammond@skippinet.com.au)"); + PyDict_SetItemString(dict, "copyright", copyright); + Py_XDECREF(copyright); + PyObject *dllhandle = PyInt_FromLong((long)hWin32uiDll); + PyDict_SetItemString(dict, "dllhandle", dllhandle); + Py_XDECREF(dllhandle); + + HookWindowsMessages(); // need to be notified of certain events... + AddConstants(dict); + + // Add all the types. + PyObject *typeDict = PyDict_New(); + POSITION pos = ui_type_CObject::typemap->GetStartPosition(); + while (pos) { + CRuntimeClass *pRT; + ui_type_CObject *pT; + ui_type_CObject::typemap->GetNextAssoc(pos, pRT, pT); + PyObject *typeName = PyString_FromString(pT->tp_name); + PyDict_SetItem(typeDict, typeName, (PyObject *)pT); + Py_XDECREF(typeName); + } + PyObject *mapName = PyString_FromString("types"); + PyDict_SetItem(dict,mapName, typeDict); + Py_XDECREF(mapName); + Py_XDECREF(typeDict); +} + +// Utilities for glue support. +BOOL Win32uiInitInstance() +{ + CVirtualHelper helper("InitInstance", GetApp(), VEH_DISPLAY_DIALOG); + int rc = 0; + if (helper.HaveHandler() && (!helper.call() || !helper.retval(rc))) { + // An error here is pretty critical - so we display the traceback dialog. +// AfxMessageBox("A Python error prevented the application from initializing"); + } + return (rc==0); +} + +// Run is the last thing _exited_. During the Run call the ExitInstance call +// is made. Whoever calls "Run" must call Win32uiFinalize after. +int Win32uiRun(void) +{ + int ret = -1; + // An error here is too late for anything to usefully print it, + // so we use a dialog. + CVirtualHelper helper("Run", GetApp(), VEH_DISPLAY_DIALOG); + if (!helper.HaveHandler()) + ret = GetApp()->CWinApp::Run(); + else { + helper.call(); + helper.retval(ret); + } + return ret; +} + +static PyThreadState *threadStateSave = NULL; + +void Win32uiFinalize() +{ + if (threadStateSave) + PyEval_RestoreThread(threadStateSave); + + if (pHostGlue && pHostGlue->bShouldFinalizePython) { + Py_Finalize(); + } + bInFatalShutdown = TRUE; + // These are primarily here as a debugging aid. Destroy what I created + // to help MFC detect useful memory leak reports +// PyCWinApp::cleanup(); + ui_assoc_object::handleMgr.cleanup(); +} + +int Win32uiExitInstance(void) +{ + int ret = 0; + CVirtualHelper helper("ExitInstance", GetApp(), VEH_DISPLAY_DIALOG); + if (helper.call()) { + helper.retval(ret); + } + return ret; +} + +BOOL Win32uiPreTranslateMessage(MSG *pMsg) +{ + BOOL ret = FALSE; + switch (pMsg->message) { + case WM_CHAR: + ret=Python_check_key_message(pMsg); + break; + // this message is (seem to be!) trapped by both the hook and this, so + // no need to waste lookups. + case WM_MDIGETACTIVE: + break; +// These 3 are also handled by both, but the hook only works for MFC Windows? +// case WM_SYSKEYDOWN: +// case WM_SYSKEYUP: +// case WM_SYSCHAR: +// break; + default: + ret=Python_check_message(pMsg); + } + return ret; +} + +BOOL Win32uiOnIdle( LONG lCount ) +{ + CVirtualHelper helper("OnIdle", GetApp()); + if (!helper.call(lCount)) return FALSE; + int ret; + if (!helper.retval(ret)) + return FALSE; + return ret; +} + +extern "C" PYW_EXPORT BOOL Win32uiApplicationInit(Win32uiHostGlue *pGlue, char *cmd, const char *additionalPaths) +{ +#ifdef _DEBUG + afxDump.SetDepth(1); // deep dump of objects at exit. + bool bDebug = true; +#else + bool bDebug = false; +#endif + PyWinGlobals_Ensure(); + + // Set sys.argv if not already done! + PyObject *argv = PySys_GetObject("argv"); + if (argv==NULL) + PySys_SetArgv(__argc-1, __argv+1); + // If the versions of the .h file are not in synch, then we are in trouble! + if (pGlue->versionNo != WIN32UIHOSTGLUE_VERSION) { + MessageBox(0, "There is a mismatch between version of the application and win32ui.pyd.\n\nIt is likely the application needs to be rebuilt.", "Error", MB_OK); + return FALSE; + } + + // Debug/Release mismatch means we are gunna die very soon anyway... + // (although this is unlikely now Debug/Release have different names!) + if (pGlue->bDebugBuild != bDebug) { + MessageBox(0, "There is a mismatch between the Debug/Release versions of the application and win32ui.pyd", "Error", MB_OK); + return FALSE; + } + + // set up the glue class. +/**** + if (PyWin_MainModuleThreadState==NULL) + PyWin_MainModuleThreadState = AfxGetModuleThreadState(); +****/ + pGlue->pfnInitInstance = Win32uiInitInstance; + pGlue->pfnExitInstance = Win32uiExitInstance; + pGlue->pfnOnCmdMsg = Python_OnCmdMsg; + pGlue->pfnPreTranslateMessage = Win32uiPreTranslateMessage; + pGlue->pfnOnIdle = Win32uiOnIdle; + pGlue->pfnRun = Win32uiRun; + pGlue->pfnFinalize = Win32uiFinalize; + pHostGlue = pGlue; + if (additionalPaths) + Python_addpath(additionalPaths); + if (cmd!=NULL) { + if (Python_run_command_with_log(cmd, NULL)) + return FALSE; + } // Processing cmd. + + if (pGlue->bShouldAbandonThreadState) { + // Abandon the thread state, saved until Finalize(). + threadStateSave = PyEval_SaveThread(); + } + + return TRUE; +} diff --git a/Pythonwin/win32uiole.cpp b/Pythonwin/win32uiole.cpp new file mode 100644 index 0000000000..5b7475b0d7 --- /dev/null +++ b/Pythonwin/win32uiole.cpp @@ -0,0 +1,143 @@ +#include "stdafxole.h" + +#include "win32dlg.h" +#include "win32oleDlgs.h" +#include "win32uioledoc.h" +#include "win32template.h" +#include "PythonCOM.h" +#include "afxdao.h" + +// Sorry about this - OLE support needs MFC private header. +// Adding MFC source path to include path causes grief! +//#include "c:\program files\DevStudio\VC\mfc\src\occimpl.h" +#include "..\src\occimpl.h" + +extern PyObject *PyCOleClientItem_Create(PyObject *self, PyObject *args); + +// @doc + +class WndHack : public CWnd { +public: + COleControlSite *GetCtrlSite() {return m_pCtrlSite;} +}; + +// @pymethod |win32uiole|GetIDispatchForWindow|Gets an OCX IDispatch pointer, if the window has one! +static PyObject * +win32uiole_GetIDispatchForWindow(PyObject *self, PyObject *args) +{ + PyObject *obWnd; + if (!PyArg_ParseTuple(args, "O:GetIDispatchForWindow", &obWnd)) + return NULL; + WndHack *pWnd = (WndHack *)GetWndPtr(obWnd); + if (!pWnd) + return NULL; + COleControlSite *pSite = pWnd->GetCtrlSite(); + if (pSite==NULL || pSite->m_pObject==NULL) { + RETURN_ERR("There is no OLE object available"); + + } + IDispatch *disp = NULL; + GUI_BGN_SAVE; + SCODE sc = pSite->m_pObject->QueryInterface(IID_IDispatch, (void**)&disp); + GUI_END_SAVE; + if (FAILED(sc) || disp == NULL) + RETURN_ERR("The OLE object does not support IDispatch"); + return PyCom_PyObjectFromIUnknown(disp, IID_IDispatch, FALSE); +} + +// @pymethod int|win32uiole|OleGetUserCtrl|Returns the application name. +static PyObject * +win32uiole_get_user_ctrl(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":OleGetUserCtrl")) + return NULL; + GUI_BGN_SAVE; + int rc = AfxOleGetUserCtrl(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|win32uiole|OleSetUserCtrl|Sets or clears the user control flag. +static PyObject * +win32uiole_set_user_ctrl(PyObject *self, PyObject *args) +{ + BOOL flag; + if (!PyArg_ParseTuple(args,"i:OleSetUserCtrl", &flag)) + // @pyparm int|bUserCtrl||Specifies whether the user-control flag is to be set or cleared. + return NULL; + GUI_BGN_SAVE; + AfxOleSetUserCtrl(flag); + GUI_END_SAVE; + RETURN_NONE; +} + +// A DAO hack! +// @pymethod |win32uiole|DaoGetEngine| +static PyObject *DaoGetEngine(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "DaoGetEngine"); + + AfxDaoInit(); + DAODBEngine* pEngine = AfxDaoGetEngine(); + IDispatch *pDisp; + HRESULT hr = pEngine->QueryInterface(IID_IDispatch, (void **)&pDisp); + if (FAILED(hr)) + return OleSetOleError(hr); + pEngine->Release(); + return PyCom_PyObjectFromIUnknown(pDisp, IID_IDispatch, FALSE); +} + +// @module win32uiole|A module, encapsulating the Microsoft Foundation Classes OLE functionality. +static struct PyMethodDef uiole_functions[] = { + {"CreateInsertDialog", PyCOleInsertDialog::create, 1}, // @pymeth CreateInsertDialog|Creates a InsertObject dialog. + {"CreateOleClientItem", PyCOleClientItem_Create, 1}, // @pymeth CreateOleClientItem|Creates a object. + {"CreateOleDocument", PyCOleDocument::Create, 1}, // @pymeth CreateOleDocument|Creates a object. + {"DaoGetEngine", DaoGetEngine, 1}, // @pymeth DaoGetEngine| + {"GetIDispatchForWindow",win32uiole_GetIDispatchForWindow,1}, // @pymeth GetIDispatchForWindow|Gets an OCX IDispatch pointer, if the window has one! + {"OleGetUserCtrl", win32uiole_get_user_ctrl, 1}, // @pymeth OleGetUserCtrl|Retrieves the current user-control flag. + {"OleSetUserCtrl", win32uiole_set_user_ctrl, 1}, // @pymeth OleSetUserCtrl|Sets the current user-control flag. + {NULL, NULL} +}; + +#define ADD_CONSTANT(tok) if (rc=AddConstant(dict,#tok, tok)) return rc +#define ADD_ENUM(parta, partb) if (rc=AddConstant(dict,#parta "_" #partb, parta::partb)) return rc + +static int AddConstant(PyObject *dict, char *key, long value) +{ + PyObject *okey = PyString_FromString(key); + PyObject *oval = PyInt_FromLong(value); + if (!okey || !oval) { + XDODECREF(okey); + XDODECREF(oval); + return 1; + } + int rc = PyDict_SetItem(dict,okey, oval); + DODECREF(okey); + DODECREF(oval); + return rc; +} + +int AddConstants(PyObject *dict) +{ + int rc; + ADD_ENUM(COleClientItem, emptyState);// @const win32uiole|COleClientItem_emptyState| + ADD_ENUM(COleClientItem, loadedState);// @const win32uiole|COleClientItem_loadedState| + ADD_ENUM(COleClientItem, openState);// @const win32uiole|COleClientItem_openState| + ADD_ENUM(COleClientItem, activeState);// @const win32uiole|COleClientItem_activeState| + ADD_ENUM(COleClientItem, activeUIState);// @const win32uiole|COleClientItem_activeUIState| + ADD_CONSTANT(OLE_CHANGED); // @const win32uiole|OLE_CHANGED|representation of a draw aspect has changed + ADD_CONSTANT(OLE_SAVED); // @const win32uiole|OLE_SAVED|the item has committed its storage + ADD_CONSTANT(OLE_CLOSED); // @const win32uiole|OLE_CLOSED|the item has closed + ADD_CONSTANT(OLE_RENAMED); // @const win32uiole|OLE_RENAMED|the item has changed its moniker + ADD_CONSTANT(OLE_CHANGED_STATE); // @const win32uiole|OLE_CHANGED_STATE|the item state (open, active, etc.) has changed + ADD_CONSTANT(OLE_CHANGED_ASPECT); // @const win32uiole|OLE_CHANGED_ASPECT|the item draw aspect has changed + return 0; +} + +extern "C" __declspec(dllexport) void +initwin32uiole(void) +{ + PyObject *module = Py_InitModule("win32uiole", uiole_functions); + PyObject *dict = PyModule_GetDict(module); + AddConstants(dict); + } diff --git a/Pythonwin/win32uioleClientItem.cpp b/Pythonwin/win32uioleClientItem.cpp new file mode 100644 index 0000000000..e2149ad5ba --- /dev/null +++ b/Pythonwin/win32uioleClientItem.cpp @@ -0,0 +1,383 @@ +#include "stdafxole.h" +#include "win32dc.h" +#include "win32uioledoc.h" +#include "PythonCOM.h" +// @doc + +class PyCOleClientItem : public PyCCmdTarget { +protected: + PyCOleClientItem() {;} + ~PyCOleClientItem() {;} +public: + static COleClientItem *GetOleClientItem(PyObject *self); + + MAKE_PY_CTOR(PyCOleClientItem); + static ui_type_CObject type; +}; + +class PythonOleClientItem : public COleClientItem +{ +public: + PythonOleClientItem( COleDocument* pContainerDoc = NULL ) : + COleClientItem( pContainerDoc ) {;} + virtual void OnChange(OLE_NOTIFICATION wNotification, DWORD dwParam) { + // @pyvirtual |PyCOleClientItem|OnChange| + CVirtualHelper helper("OnChange", this); + if (helper.HaveHandler()) + // @pyparm int|wNotification|| + // @pyparm int|dwParam|| + helper.call(wNotification, dwParam); + else + COleClientItem::OnChange(wNotification, dwParam); + } + virtual void OnActivate() { + // @pyvirtual |PyCOleClientItem|OnActivate| + CVirtualHelper helper("OnActivate", this); + if (helper.HaveHandler()) + helper.call(); + else + COleClientItem::OnActivate(); + } + virtual void OnGetItemPosition(CRect& rPosition) { + // @pyvirtual (int, int, int, int)|PyCOleClientItem|OnGetItemPosition| + CVirtualHelper helper("OnGetItemPosition", this); + if (helper.call()) { + PyObject *ret; + helper.retval(ret); + CEnterLeavePython _celp; + PyArg_ParseTuple(ret, "iiii", &rPosition.left, &rPosition.top, &rPosition.right, &rPosition.bottom); + } + } + virtual void OnDeactivateUI(BOOL bUndoable) { + // @pyvirtual |PyCOleClientItem|OnDeactivateUI| + // @pyparm int|bUndoable|| + CVirtualHelper helper("OnDeactivateUI", this); + if (helper.HaveHandler()) + helper.call(bUndoable); + else + COleClientItem::OnDeactivateUI(bUndoable); + + } + virtual BOOL OnChangeItemPosition(const CRect& rectPos) { + // @pyvirtual int|PyCOleClientItem|OnChangeItemPosition| + // @pyparm (int, int, int, int)|(left, top, right, bottom)||The new position + CVirtualHelper helper("OnChangeItemPosition", this); + BOOL bRet; + PyObject *args = Py_BuildValue("(iiii)", rectPos.left, rectPos.top, rectPos.right, rectPos.bottom); + if (helper.HaveHandler() && helper.call_args(args)) { + // Note = args decref'd by caller + helper.retval(bRet); + } + else + bRet = COleClientItem::OnChangeItemPosition(rectPos); + return bRet; + } + BOOL BaseOnChangeItemPosition(const CRect& rectPos) { + return COleClientItem::OnChangeItemPosition(rectPos); + } + +}; + +// @pymethod |win32uiole|CreateOleClientItem|Creates a object. +PyObject * +PyCOleClientItem_Create(PyObject *self, PyObject *args) +{ + PyObject *obDoc; + if (!PyArg_ParseTuple(args, "O:CreateOleClientItem", &obDoc)) + return NULL; + if (!PyCOleClientItem::is_uiobject(obDoc, &PyCOleDocument::type)) + RETURN_TYPE_ERR("the first argument must be a document object"); + COleDocument *pDoc = PyCOleDocument::GetDoc(obDoc); + if (pDoc==NULL) return NULL; + COleClientItem *pNew = new PythonOleClientItem(pDoc); + return ui_assoc_object::make( PyCOleClientItem::type, pNew ); +} + +/*static*/COleClientItem *PyCOleClientItem::GetOleClientItem(PyObject *self) +{ + return (COleClientItem *)GetGoodCppObject( self, &type); +} + + +// @pymethod |PyCOleClientItem|CreateNewItem|Creates an embedded item. +static PyObject *PyCOleClientItem_CreateNewItem(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + PyObject *obclsid; + CLSID clsid; + if (!PyArg_ParseTuple(args, "O:CreateNewItem", &obclsid)) + return NULL; + if (!PyWinObject_AsIID(obclsid, &clsid)) + return NULL; + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pCI->CreateNewItem(clsid); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CreateNewItem failed"); + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|Close|Closes the item +static PyObject *PyCOleClientItem_Close(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "Close"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + pCI->Close(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|DoVerb|Executes the specified verb. +static PyObject *PyCOleClientItem_DoVerb(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + long iVerb; + PyObject *obView; + if (!PyArg_ParseTuple(args, "lO", &iVerb, &obView)) + return NULL; + + if (!PyCOleClientItem::is_uiobject(obView, &PyCView::type)) + RETURN_TYPE_ERR("the first argument must be a view object"); + CView *pView = PyCView::GetViewPtr(obView); + + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + BOOL bOK = pCI->DoVerb(iVerb, pView); + GUI_END_SAVE; + if (!bOK) + RETURN_ERR("DoVerb failed"); + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|Draw|Draws the OLE item into the specified bounding rectangle using the specified device context. +static PyObject *PyCOleClientItem_Draw(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + PyObject *obDC; + RECT rect; + int aspect = -1; + if (!PyArg_ParseTuple(args, "O(iiii)|i", &obDC, &rect.left, &rect.top, &rect.right, &rect.bottom, &aspect)) + return NULL; + + if (!PyCOleClientItem::is_uiobject(obDC, &ui_dc_object::type)) + RETURN_TYPE_ERR("the first argument must be a view object"); + CDC *pDC = ui_dc_object::GetDC(obDC); + + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + BOOL bOK = pCI->Draw(pDC, &rect, (DVASPECT)aspect); + GUI_END_SAVE; + if (!bOK) + RETURN_ERR("Draw failed"); + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|GetActiveView|Obtains the active view for the item +static PyObject *PyCOleClientItem_GetActiveView(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetActiveView"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + CView *pWnd = pCI->GetActiveView(); + GUI_END_SAVE; + + return ui_assoc_object::make( UITypeFromCObject(pWnd), pWnd )->GetGoodRet(); +} + +// @pymethod |PyCOleClientItem|GetDocument|Obtains the current document for the item +static PyObject *PyCOleClientItem_GetDocument(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetDocument"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + CDocument *pDoc = pCI->GetDocument(); + GUI_END_SAVE; + + return ui_assoc_object::make( UITypeFromCObject(pDoc), pDoc )->GetGoodRet(); +} + +// @pymethod |PyCOleClientItem|GetInPlaceWindow|Obtains the window in which the item has been opened for in-place editing. +static PyObject *PyCOleClientItem_GetInPlaceWindow(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetInPlaceWindow"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + CWnd *pWnd = pCI->GetInPlaceWindow(); + GUI_END_SAVE; + + return ui_assoc_object::make( UITypeFromCObject(pWnd), pWnd )->GetGoodRet(); +} + +// @pymethod |PyCOleClientItem|GetItemState|Obtains the OLE item's current state +static PyObject *PyCOleClientItem_GetItemState(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetItemState"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + int rc = pCI->GetItemState(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod |PyCOleClientItem|GetObject|Returns the COM object to the item. This is the m_lpObject variable in MFC. +static PyObject *PyCOleClientItem_GetObject(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetObject"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + if (pCI->m_lpObject==NULL) + RETURN_NONE; + return PyCom_PyObjectFromIUnknown(pCI->m_lpObject, IID_IUnknown, TRUE); +} + +// @pymethod |PyCOleClientItem|GetStorage|Returns the COM object used for storage +static PyObject *PyCOleClientItem_GetStorage(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "GetObject"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + if (pCI->m_lpStorage==NULL) + RETURN_NONE; + return PyCom_PyObjectFromIUnknown(pCI->m_lpStorage, IID_IStorage, TRUE); +} + +// @pymethod |PyCOleClientItem|OnActivate|Calls the underlying MFC method. +static PyObject *PyCOleClientItem_OnActivate(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "OnActivate"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + pCI->COleClientItem::OnActivate(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|OnChange|Calls the underlying MFC method. +static PyObject *PyCOleClientItem_OnChange(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + long l1, l2; + if (!PyArg_ParseTuple(args, "ll", &l1, &l2)) + return NULL; + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + pCI->COleClientItem::OnChange((OLE_NOTIFICATION)l1, l2); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCOleClientItem|OnChangeItemPosition|Calls the underlying MFC method. +static PyObject *PyCOleClientItem_OnChangeItemPosition(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + RECT rect; + if (!PyArg_ParseTuple(args, "iiii", &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + BOOL bRet = ((PythonOleClientItem *)pCI)->BaseOnChangeItemPosition(rect); + GUI_END_SAVE; + // @rdesc The result is a BOOL indicating if the function succeeded. No exception is thrown. + return PyInt_FromLong(bRet); +} + +// @pymethod int|PyCOleClientItem|OnDeactivateUI|Calls the underlying MFC method. +static PyObject *PyCOleClientItem_OnDeactivateUI(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + int bUndoable; + if (!PyArg_ParseTuple(args, "i", &bUndoable)) + return NULL; + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + pCI->COleClientItem::OnDeactivateUI(bUndoable); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|Run|Runs the application associated with this item. +static PyObject *PyCOleClientItem_Run(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + CHECK_NO_ARGS2(args, "Run"); + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + pCI->Run(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCOleClientItem|SetItemRects|Sets the bounding rectangle or the visible rectangle of the OLE item. +static PyObject *PyCOleClientItem_SetItemRects(PyObject *self, PyObject *args) +{ + COleClientItem *pCI; + RECT rectPos = {-1, -1, -1, -1}; + RECT rectClip = {-1, -1, -1, -1}; + if (!PyArg_ParseTuple(args, "|(iiii)(iiii)", + &rectPos.left, &rectPos.top, &rectPos.right, &rectPos.bottom, + &rectClip.left, &rectClip.top, &rectClip.right, &rectClip.bottom)) + return NULL; + RECT *pRectPos = rectPos.left==-1 && rectPos.right==-1 ? NULL : &rectPos; + RECT *pRectClip = rectClip.left==-1 && rectClip.right==-1 ? NULL : &rectClip; + if (!(pCI=PyCOleClientItem::GetOleClientItem(self))) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pCI->SetItemRects(pRectPos, pRectClip); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetItemRects failed"); + RETURN_NONE; +} + +// @object PyCOleClientItem|An OLE client item class. Encapsulates an MFC class +static struct PyMethodDef PyCOleClientItem_methods[] = { + {"CreateNewItem", PyCOleClientItem_CreateNewItem, 1}, // @pymeth CreateNewItem|Creates an embedded item. + {"Close", PyCOleClientItem_Close, 1}, // @pymeth Close|Closes the item. + {"DoVerb", PyCOleClientItem_DoVerb, 1}, // @pymeth DoVerb|Executes the specified verb. + {"Draw", PyCOleClientItem_Draw, 1}, // @pymeth Draw|Draws the OLE item into the specified bounding rectangle using the specified device context. + {"GetActiveView", PyCOleClientItem_GetActiveView, 1}, // @pymeth GetActiveView|Obtains the active view for the item + {"GetDocument", PyCOleClientItem_GetDocument, 1}, // @pymeth GetDocument|Obtains the current document for the item + {"GetInPlaceWindow", PyCOleClientItem_GetInPlaceWindow, 1}, // @pymeth GetInPlaceWindow|Obtains the window in which the item has been opened for in-place editing. + {"GetItemState", PyCOleClientItem_GetItemState, 1}, // @pymeth GetItemState|Obtains the OLE item's current state + {"GetObject", PyCOleClientItem_GetObject, 1}, // @pymeth GetObject|Returns the COM object to the item. This is the m_lpObject variable in MFC. + {"GetStorage", PyCOleClientItem_GetStorage, 1}, // @pymeth GetStorage|Returns the COM object used for storage + {"OnActivate", PyCOleClientItem_OnActivate, 1}, // @pymeth OnActivate|Calls the underlying MFC handler. + {"OnChange", PyCOleClientItem_OnChange, 1}, // @pymeth OnChange|Calls the underlying MFC handler. + {"OnChangeItemPosition",PyCOleClientItem_OnChangeItemPosition, 1}, // @pymeth OnChangeItemPosition|Calls the underlying MFC method. + {"OnDeactivateUI", PyCOleClientItem_OnDeactivateUI, 1}, // @pymeth OnDeactivateUI|Calls the underlying MFC method. + {"Run", PyCOleClientItem_Run, 1}, // @pymeth Run|Runs the application associated with this item. + {"SetItemRects", PyCOleClientItem_SetItemRects, 1}, // @pymeth SetItemRects|Sets the bounding rectangle or the visible rectangle of the OLE item. + {NULL}, +}; + + + +ui_type_CObject PyCOleClientItem::type("PyCOleClientItem", + &PyCCmdTarget::type, // should be CDocItem when we support it. + RUNTIME_CLASS(COleClientItem), + sizeof(PyCOleClientItem), + PyCOleClientItem_methods, + GET_PY_CTOR(PyCOleClientItem) ); diff --git a/Pythonwin/win32uioledoc.cpp b/Pythonwin/win32uioledoc.cpp new file mode 100644 index 0000000000..a72aa1f3a0 --- /dev/null +++ b/Pythonwin/win32uioledoc.cpp @@ -0,0 +1,152 @@ +#include "stdafxole.h" +#include "win32app.h" +#include "win32uioledoc.h" +#include "win32template.h" +// +// OLE Document Object +// +// @doc + +#define PyObject_FromPOSITION PyInt_FromLong +#define POSITION_CAST long +#define POSITION_FORMATCHAR "l" + +class CProtectedDocument : public CDocument +{ +public: + void SetPathName( const char *pathName ) {m_strPathName = pathName;} +}; + +/*static*/COleDocument *PyCOleDocument::GetDoc(PyObject *self) +{ + return (COleDocument *)GetGoodCppObject( self, &type); +} + +// @pymethod |win32uiole|CreateOleDocument|Creates an OLE document. +PyObject *PyCOleDocument::Create(PyObject *self, PyObject *args) +{ + char *fileName = NULL; // default, untitled document + PyObject *obTemplate; + // @pyparm |template||The template to be attached to this document. + // @pyparm string|fileName|None|The filename for the document. + if (!PyArg_ParseTuple(args, "O|z", &obTemplate, &fileName)) + return NULL; + if (!PyCOleDocument::is_uiobject(obTemplate, &PyCDocTemplate::type)) + RETURN_TYPE_ERR("First param must be a document template"); + + COleDocument *pDoc = NULL; + if (fileName) { + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + // need to look for an open doc of same name, and return that object. + // Let MFC framework search for a filename for us. + CDocument *pLookDoc=pApp->FindOpenDocument(fileName); + if (pLookDoc->IsKindOf(RUNTIME_CLASS(COleDocument))) + pDoc = (COleDocument *)pLookDoc; + } + // no name given, or no open document of that name + if (pDoc==NULL) { + CPythonDocTemplate *pMFCTemplate = PyCDocTemplate::GetTemplate(obTemplate); + if (pMFCTemplate==NULL) + return NULL; + GUI_BGN_SAVE; + pDoc = new COleDocument(); + GUI_END_SAVE; + if (pDoc==NULL) + RETURN_MEM_ERR("error creating document object"); + pMFCTemplate->AddDocument(pDoc); + ASSERT_VALID(pDoc); + ASSERT(pDoc->IsKindOf(RUNTIME_CLASS(CDocument))); + if (fileName) + ((CProtectedDocument *)pDoc)->SetPathName(fileName); + } + return ui_assoc_object::make(PyCOleDocument::type, pDoc); +} + +// @pymethod |PyCOleDocument|EnableCompoundFile|Call this function if you want to store the document using the compound-file format. +static PyObject *PyCOleDocument_EnableCompoundFile(PyObject *self, PyObject *args) +{ + // @pyparm int|bEnable|1|Specifies whether compound file support is enabled or disabled. + int bEnable = 1; + if (!PyArg_ParseTuple(args, "|i:EnableCompoundFile", &bEnable)) + return NULL; + COleDocument *pDoc = PyCOleDocument::GetDoc(self); + if (pDoc==NULL) return NULL; + GUI_BGN_SAVE; + pDoc->EnableCompoundFile(bEnable); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCOleDocument|GetInPlaceActiveItem|Obtains the OLE item that is currently activated in place in the frame window containing the view identified by obWnd. +static PyObject *PyCOleDocument_GetInPlaceActiveItem(PyObject *self, PyObject *args) +{ + PyObject *obWnd; + // @pyparm |wnd||The window. + if (!PyArg_ParseTuple(args, "O", &obWnd)) + return NULL; + + if (!PyCOleDocument::is_uiobject(obWnd, &PyCWnd::type)) + RETURN_TYPE_ERR("the first argument must be a Windows object"); + CWnd *pWnd = (CWnd *)PyCWnd::GetPythonGenericWnd(obWnd, &PyCWnd::type); + + COleDocument *pDoc = PyCOleDocument::GetDoc(self); + if (pDoc==NULL) return NULL; + GUI_BGN_SAVE; + COleClientItem *pRet = pDoc->GetInPlaceActiveItem(pWnd); + GUI_END_SAVE; + if (pRet==NULL) + RETURN_NONE; + return ui_assoc_object::make( UITypeFromCObject(pRet), pRet )->GetGoodRet(); +} + +// @pymethod POSITION|PyCOleDocument|GetStartPosition|Obtains the position of the first item in the document. +static PyObject *PyCOleDocument_GetStartPosition(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, "GetStartPosition"); + COleDocument *pDoc = PyCOleDocument::GetDoc(self); + if (pDoc==NULL) return NULL; + GUI_BGN_SAVE; + POSITION pos = pDoc->GetStartPosition(); + GUI_END_SAVE; + return PyObject_FromPOSITION((POSITION_CAST)pos); +} + +// @pymethod (POSITION, )|PyCOleDocument|GetNextItem|Call this function repeatedly to access each of the items in your document. +static PyObject *PyCOleDocument_GetNextItem(PyObject *self, PyObject *args) +{ + POSITION position; + // @pyparm POSITION|pos||The position to iterate from. + if (!PyArg_ParseTuple(args, POSITION_FORMATCHAR, &position)) + return NULL; + COleDocument *pDoc = PyCOleDocument::GetDoc(self); + if (pDoc==NULL) return NULL; + GUI_BGN_SAVE; + CDocItem *pRet = pDoc->GetNextItem(position); + GUI_END_SAVE; + PyObject *obDocItem = ui_assoc_object::make( UITypeFromCObject(pRet), pRet )->GetGoodRet(); + if (obDocItem==NULL) return NULL; + PyObject *ret = Py_BuildValue("O" POSITION_FORMATCHAR, obDocItem, (POSITION_CAST)position); + Py_DECREF(obDocItem); + return ret; +} + +/////////////////////////////////////// +// +// OLE Document Methods +// +/////////////////////////////////////// +// @object PyCOleDocument|An OLE document class. Encapsulates an MFC class +static struct PyMethodDef PyCOleDocument_methods[] = { + {"EnableCompoundFile", PyCOleDocument_EnableCompoundFile, 1}, // @pymeth EnableCompoundFile|Call this function if you want to store the document using the compound-file format + {"GetStartPosition", PyCOleDocument_GetStartPosition, 1}, // @pymeth GetStartPosition|Obtains the position of the first item in the document. + {"GetNextItem", PyCOleDocument_GetNextItem, 1}, // @pymeth GetNextItem|Call this function repeatedly to access each of the items in your document. + {"GetInPlaceActiveItem", PyCOleDocument_GetInPlaceActiveItem, 1}, // @pymeth GetInPlaceActiveItem|Obtains the OLE item that is currently activated in place in the frame window containing the view identified by obWnd. + {NULL, NULL} /* sentinel */ +}; +ui_type_CObject PyCOleDocument::type("PyCOleDocument", + &PyCDocument::type, + RUNTIME_CLASS(COleDocument), + sizeof(PyCOleDocument), + PyCOleDocument_methods, + GET_PY_CTOR(PyCOleDocument) ); diff --git a/Pythonwin/win32uioledoc.h b/Pythonwin/win32uioledoc.h new file mode 100644 index 0000000000..e386aa392b --- /dev/null +++ b/Pythonwin/win32uioledoc.h @@ -0,0 +1,13 @@ +// Delcaration of the Python support for a PyCOleDocument object +#pragma once +#include "win32doc.h" + +class PyCOleDocument : public PyCDocument { +protected: +public: + static COleDocument *GetDoc(PyObject *self); + static PyObject *Create(PyObject *self, PyObject *args); + + MAKE_PY_CTOR(PyCOleDocument); + static ui_type_CObject type; +}; diff --git a/Pythonwin/win32util.cpp b/Pythonwin/win32util.cpp new file mode 100644 index 0000000000..cf297d184d --- /dev/null +++ b/Pythonwin/win32util.cpp @@ -0,0 +1,1089 @@ +/* + + win32 module utilities + + Created January 1996, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32control.h" +#include "win32menu.h" +#include "win32dlg.h" +#include "win32dc.h" +#include "win32gdi.h" +#include "win32bitmap.h" +#include "win32font.h" +#include "win32dll.h" +#include "win32splitter.h" +#include "win32toolbar.h" +#include "win32prop.h" +#include "win32template.h" +#include "win32ctrlList.h" +#include "win32ctrlTree.h" +#include "win32RichEdit.h" +#ifdef HIER_LIST +#include "win32hl.h" +#endif + +class PyCRectType : public ui_type { +public: + PyCRectType( const char *name, ui_type *pBaseType, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ); +}; +// @object PyCRect|A Python interface the the MFC CRect class. +class PyCRect : public ui_base_class +{ +public: + static PyCRectType type; + PyCRect(RECT *pRect, bool bTakeCopy) { + m_owned = bTakeCopy; + if (m_owned) + m_pRect = new CRect(*pRect); + else + m_pRect = pRect; + ob_type = &type; + _Py_NewReference(this); + } + PyCRect(const RECT &rect) { + m_owned = true; + m_pRect = new CRect(rect); + ob_type = &type; + _Py_NewReference(this); + } + ~PyCRect() {if (m_owned) delete m_pRect;} + virtual PyObject *getattr(char *name); + virtual int setattr(char *name, PyObject *v); + static PyObject *getitem(PyObject *self, int index); + static int getlength(PyObject *self); + CString repr(); +protected: + bool m_owned; + RECT *m_pRect; +}; + +PyObject *PyWinObject_FromRECT(RECT *p, bool bTakeCopy) +{ + return new PyCRect(p, bTakeCopy); +} +PyObject *PyWinObject_FromRECT(const RECT &r) +{ + return new PyCRect(r); +} +// Sequence stuff to provide compatibility with tuples. +static PySequenceMethods PyCRect_Sequence = +{ + PyCRect::getlength, // sq_length; + NULL, // sq_concat; + NULL, // sq_repeat; + PyCRect::getitem, // sq_item; + NULL, // sq_slice; + NULL, // sq_ass_item; + NULL, // sq_ass_slice; +}; + +PyObject *PyCRect::getattr(char *name) +{ + if (strcmp(name, "left")==0) + return PyInt_FromLong(m_pRect->left); + if (strcmp(name, "right")==0) + return PyInt_FromLong(m_pRect->right); + if (strcmp(name, "top")==0) + return PyInt_FromLong(m_pRect->top); + if (strcmp(name, "bottom")==0) + return PyInt_FromLong(m_pRect->bottom); + return ui_base_class::getattr(name); +} + +CString PyCRect::repr() +{ + CString csRet; + csRet.Format("%s (%d, %d, %d, %d)", ui_base_class::repr(), m_pRect->left, m_pRect->top, m_pRect->right, m_pRect->bottom ); + return csRet; +} + +/* static */ int PyCRect::getlength(PyObject *self) +{ + // NEVER CHANGE THIS - you will break all the old + // code written when these object were tuples! + return 4; +} + +/* static */ PyObject *PyCRect::getitem(PyObject *self, int index) +{ + PyCRect *p = (PyCRect *)self; + switch (index) { + case 0: // @tupleitem 0|int|left| + return PyInt_FromLong(p->m_pRect->left); + case 1: // @tupleitem 1|int|top| + return PyInt_FromLong(p->m_pRect->top); + case 2: // @tupleitem 2|int|right| + return PyInt_FromLong(p->m_pRect->right); + case 3: // @tupleitem 3|int|bottom| + return PyInt_FromLong(p->m_pRect->bottom); + } + PyErr_SetString(PyExc_IndexError, "index out of range"); + return NULL; +} + +int PyCRect::setattr(char *name, PyObject *v) +{ + if (!PyInt_Check(v)) + RETURN_TYPE_ERR("PyCRect objects only have integer properties"); + if (strcmp(name, "left")==0) { + m_pRect->left = PyInt_AsLong(v); + return 0; + } + if (strcmp(name, "right")==0) { + m_pRect->right = PyInt_AsLong(v); + return 0; + } + if (strcmp(name, "top")==0) { + m_pRect->top = PyInt_AsLong(v); + return 0; + } + if (strcmp(name, "bottom")==0) { + m_pRect->bottom = PyInt_AsLong(v); + return 0; + } + return ui_base_class::setattr(name, v); +} + +static struct PyMethodDef +PyCRect_methods[] = +{ + { NULL, NULL } +}; + +PyCRectType::PyCRectType( const char *name, ui_type *pBaseType, int typeSize, struct PyMethodDef* methodList, ui_base_class * (* thector)() ) : + ui_type(name, pBaseType, typeSize, methodList, thector) +{ + tp_as_sequence = &PyCRect_Sequence; +} + +PyCRectType PyCRect::type("PyCRect", + &ui_base_class::type, + sizeof(PyCRect), + PyCRect_methods, + NULL); + +// The CREATESTRUCT just has pointers (no buffers) for the name +// and classname. Therefore, I dont treat them as strings, just +// pointers (via long casts) +// @object CREATESTRUCT|A representation of a Windows CREATESTRUCT structure. +PyObject *PyObjectFromCreateStruct(LPCREATESTRUCT lpcs) +{ + return Py_BuildValue("(iiii(iiii)illi)", + lpcs->lpCreateParams, // @pyparm int|createParams|| + lpcs->hInstance, // @pyparm int|hInstance|| + lpcs->hMenu, // @pyparm int|hMenu|| + lpcs->hwndParent, // @pyparm int|hwndParent|| + lpcs->cy, // @pyparm (int, int, int, int)|cy, cx, y, x|| + lpcs->cx, + lpcs->y, + lpcs->x, + lpcs->style,// @pyparm int|style|| + (long)lpcs->lpszName,// @pyparm int|lpszName||A string cast to a long. + (long)lpcs->lpszClass,// @pyparm int|lpszClass||A string cast to a long!? + lpcs->dwExStyle);// @pyparm int|dwExStyle|| + + // @comm Note that the strings are passed as longs, which are there address + // in memory. This is due to the internal mechanics of passing this structure around. +} + +BOOL CreateStructFromPyObject(LPCREATESTRUCT lpcs, PyObject *ob, const char *fnName, BOOL bFromTuple) +{ + char argBuf[80]; + if (fnName==NULL) fnName = "CREATESTRUCT value"; + if (bFromTuple) + sprintf(argBuf, "(iiii(iiii)illi):%s", fnName); + else + sprintf(argBuf, "iiii(iiii)illi:%s", fnName); + long name, className; + BOOL ret = PyArg_ParseTuple(ob, argBuf, + &lpcs->lpCreateParams, + &lpcs->hInstance, + &lpcs->hMenu, + &lpcs->hwndParent, + &lpcs->cy, + &lpcs->cx, + &lpcs->y, + &lpcs->x, + &lpcs->style, + &name, + &className, + &lpcs->dwExStyle); + // CCreateStruct + lpcs->lpszName = (LPCTSTR)name; + lpcs->lpszClass = (LPCTSTR)className; + return ret; +} +///////////////////////////////////////////////////////////////////// +// +// Font conversion utilities +// +// +static const char *szFontName = "name"; +static const char *szFontWeight = "weight"; +static const char *szFontWidth = "width"; +static const char *szFontHeight = "height"; +static const char *szFontItalic = "italic"; +static const char *szFontUnderline = "underline"; +static const char *szFontPitch = "pitch and family"; +static const char *szFontCharSet = "charset"; + +PyObject *LogFontToDict(const LOGFONT &lf) +{ + PyObject *ret = PyDict_New(); + PyMapping_SetItemString( ret, (char *)szFontName, PyString_FromString((char *)lf.lfFaceName) ); + PyMapping_SetItemString( ret, (char *)szFontHeight, PyInt_FromLong(-lf.lfHeight)); + PyMapping_SetItemString( ret, (char *)szFontWidth, PyInt_FromLong(lf.lfWidth)); + PyMapping_SetItemString( ret, (char *)szFontWeight, PyInt_FromLong(lf.lfWeight)); + PyMapping_SetItemString( ret, (char *)szFontPitch, PyInt_FromLong(lf.lfPitchAndFamily)); + PyMapping_SetItemString( ret, (char *)szFontCharSet, PyInt_FromLong(lf.lfCharSet)); + PyMapping_SetItemString( ret, (char *)szFontUnderline, lf.lfUnderline?PyInt_FromLong(1):Py_None); + PyMapping_SetItemString( ret, (char *)szFontItalic, lf.lfItalic?PyInt_FromLong(1):Py_None); + return ret; +} + +BOOL DictToLogFont(PyObject *font_props, LOGFONT *pLF) +{ + ZeroMemory (pLF, sizeof(LOGFONT)); + + // font default values + pLF->lfCharSet = DEFAULT_CHARSET; // dont use ANSI_CHARSET to support Japanese charset. + pLF->lfQuality = PROOF_QUALITY; // don't scale raster fonts + PyObject *v; + + + v = PyDict_GetItemString (font_props, (char *)szFontName); + if (v != NULL) + if (PyString_Check(v)) + strncpy (pLF->lfFaceName, PyString_AsString(v), LF_FACESIZE - 1); + else + RETURN_ERR ("Expected string value for font name property"); + + v = PyDict_GetItemString (font_props, (char *)szFontHeight); + if (v != NULL) + if (PyInt_Check (v)) + pLF->lfHeight = -PyInt_AsLong(v); + else + RETURN_ERR ("Expected integer value for font height property"); + + v = PyDict_GetItemString (font_props, (char *)szFontWidth); + if (v != NULL) + if (PyInt_Check (v)) + pLF->lfWidth = PyInt_AsLong(v); + else + RETURN_ERR ("Expected integer value for font width property"); + + v = PyDict_GetItemString (font_props, (char *)szFontPitch); + if (v != NULL) + if (PyInt_Check (v)) + pLF->lfPitchAndFamily = (BYTE)PyInt_AsLong(v); + else + RETURN_ERR ("Expected integer value for font 'pitch and family' property"); + + v = PyDict_GetItemString (font_props, (char *)szFontCharSet); + if (v != NULL) + if (PyInt_Check (v)) + pLF->lfCharSet = (BYTE)PyInt_AsLong(v); + else + RETURN_ERR ("Expected integer value for font 'charset' property"); + + v = PyDict_GetItemString (font_props, (char *)szFontWeight); + if (v != NULL) + if (PyInt_Check (v)) + pLF->lfWeight = PyInt_AsLong(v); + else + RETURN_ERR ("Expected integer value for font weight property"); + + v = PyDict_GetItemString (font_props, (char *)szFontItalic); + if (v != NULL && v != Py_None) + pLF->lfItalic = TRUE; + + v = PyDict_GetItemString (font_props, (char *)szFontUnderline); + if (v != NULL && v != Py_None) + pLF->lfUnderline = TRUE; + return TRUE; +} + +///////////////////////////////////////////////////////////////////// +// +// ListView conversion utilities +// +// +// LV_ITEM +PyObject *MakeLV_ITEMTuple(LV_ITEM *item) +{ + PyObject *ret = PyTuple_New(7); + if (ret==NULL) return NULL; + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(item->iItem)); + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(item->iSubItem)); + if (item->mask & LVIF_STATE) { + PyTuple_SET_ITEM(ret, 2, PyInt_FromLong(item->state)); + PyTuple_SET_ITEM(ret, 3, PyInt_FromLong(item->stateMask)); + } else { + Py_INCREF(Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 2, Py_None); + PyTuple_SET_ITEM(ret, 3, Py_None); + } + if ((item->mask & LVIF_TEXT) && (item->pszText != NULL)) { + PyTuple_SET_ITEM(ret, 4, PyString_FromString(item->pszText)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 4, Py_None); + } + if (item->mask & LVIF_IMAGE) { + PyTuple_SET_ITEM(ret, 5, PyInt_FromLong(item->iImage)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 5, Py_None); + } + if (item->mask & LVIF_PARAM && item->lParam) { + PyObject *ob = PyInt_FromLong(item->lParam); + PyTuple_SET_ITEM(ret, 6, ob); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 6, Py_None); + } + return ret; +} + +// @pymethod |PyCListCtrl|LV_ITEM tuple|Describes an LV_ITEM tuple, used by the object. +// @pyparm int|item||The item number. +// @pyparm int|subItem||The sub-item number. +// @pyparm int|state||The items state. +// @pyparm int|stateMask||A mask indicating which of the state bits are valid.. +// @pyparm string|text||The text for the item +// @pyparm int|iImage||The image offset for the item +// @pyparm int|userObject||Any integer to be associated with the item. +// @comm When passed to Python, will always be a tuple of size 7, and items may be None if not available. +// When passed from Python, the tuple must be at least 2 items long, and any item may be None. +// userob is any Python object at all, but no reference count is kept, so you must ensure the object remains referenced throught the lists life. +BOOL ParseLV_ITEMTuple( PyObject *args, LV_ITEM *pItem) +{ + PyObject *ob; + pItem->mask = 0; + int len = PyTuple_Size(args); + if (len<2 || len > 7) { + PyErr_SetString(PyExc_TypeError, "LV_ITEM tuple has invalid size"); + return FALSE; + } + PyErr_Clear(); // clear any errors, so I can detect my own. + // 0 - iItem. + if ((ob=PyTuple_GetItem(args, 0))==NULL) + return FALSE; + pItem->iItem = (UINT)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + // 1 - iSubItem + if ((ob=PyTuple_GetItem(args, 1))==NULL) + return FALSE; + pItem->iSubItem = (UINT)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + // 1/2 - state/stateMask + if (len<4) return TRUE; + if ((ob=PyTuple_GetItem(args, 2))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->state = (UINT)PyInt_AsLong(ob); + if (PyErr_Occurred() || (ob=PyTuple_GetItem(args, 3))==NULL) + return FALSE; + pItem->stateMask = (UINT)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + pItem->mask |= LVIF_STATE; + } + if (len<5) return TRUE; + if ((ob=PyTuple_GetItem(args, 4))==NULL) + return FALSE; + if (ob != Py_None) { + if (!PyString_Check(ob)) RETURN_TYPE_ERR("The text item must be a string or None"); + pItem->mask |= LVIF_TEXT; + pItem->pszText = PyString_AsString(ob); + pItem->cchTextMax = strlen(pItem->pszText)+1; + } + if (len<6) return TRUE; + if ((ob=PyTuple_GetItem(args, 5))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->mask |= LVIF_IMAGE; + pItem->iImage = PyInt_AsLong(ob); + if (PyErr_Occurred()) + return FALSE; + } + if (len<7) return TRUE; + if ((ob=PyTuple_GetItem(args, 6))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->mask |= LVIF_PARAM; + pItem->lParam = PyInt_AsLong(ob); + } + return TRUE; +} + +// +// LV_COLUMN +PyObject *MakeLV_COLUMNTuple(LV_COLUMN *item) +{ + PyObject *ret = PyTuple_New(4); + if (ret==NULL) return NULL; + if (item->mask & LVCF_FMT) { + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(item->fmt)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 0, Py_None); + } + if (item->mask & LVCF_WIDTH) { + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(item->cx)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 1, Py_None); + } + if ((item->mask & LVCF_TEXT) && (item->pszText != NULL)) { + PyTuple_SET_ITEM(ret, 2, PyString_FromString(item->pszText)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 2, Py_None); + } + if (item->mask & LVCF_SUBITEM) { + PyTuple_SET_ITEM(ret, 3, PyInt_FromLong(item->iSubItem)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 3, Py_None); + } + return ret; +} +// @pymethod |PyCListCtrl|LV_COLUMN tuple|Describes an LV_COLUMN tuple, used by the object. +// A tuple of 4 items, being fmt, cx, pszText, iSubItem +// When passed to Python, will always be a tuple of size 4, and items may be None if not available. +// When passed from Python, the tuple may be any length up to 4, and any item may be None. +BOOL ParseLV_COLUMNTuple( PyObject *args, LV_COLUMN *pItem) +{ + PyObject *ob; + pItem->mask = 0; + int len = PyTuple_Size(args); + if (len > 4) { + PyErr_SetString(PyExc_TypeError, "LV_COLUMN tuple has invalid size"); + return FALSE; + } + PyErr_Clear(); // clear any errors, so I can detect my own. + // 0 - fmt + if (len<1) return TRUE; + if ((ob=PyTuple_GetItem(args, 0))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->fmt = (int)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + pItem->mask |= LVCF_FMT; + } + // 1 - cx + if (len<2) return TRUE; + if ((ob=PyTuple_GetItem(args, 1))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->cx = (int)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + pItem->mask |= LVCF_WIDTH; + } + // 2 - text + if (len<3) return TRUE; + if ((ob=PyTuple_GetItem(args, 2))==NULL) + return FALSE; + if (ob != Py_None) { + if (!PyString_Check(ob)) RETURN_TYPE_ERR("The text item must be a string or None"); + pItem->mask |= LVCF_TEXT; + pItem->pszText = PyString_AsString(ob); + pItem->cchTextMax = strlen(pItem->pszText)+1; + } + // 3 - subitem + if (len<4) return TRUE; + if ((ob=PyTuple_GetItem(args, 3))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->mask |= LVCF_SUBITEM; + pItem->iSubItem = PyInt_AsLong(ob); + if (PyErr_Occurred()) + return FALSE; + } + return TRUE; +} + +///////////////////////////////////////////////////////////////////// +// +// TreeView conversion utilities +// +// +// TV_ITEM +PyObject *MakeTV_ITEMTuple(TV_ITEM *item) +{ + PyObject *ret = PyTuple_New(8); + if (ret==NULL) return NULL; + if (item->mask & TVIF_HANDLE) + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong((long)item->hItem)); + else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 0, Py_None); + } + if (item->mask & TVIF_STATE) { + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(item->state)); + PyTuple_SET_ITEM(ret, 2, PyInt_FromLong(item->stateMask)); + } else { + Py_INCREF(Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 1, Py_None); + PyTuple_SET_ITEM(ret, 2, Py_None); + } + if ((item->mask & TVIF_TEXT) && (item->pszText != NULL)) { + PyTuple_SET_ITEM(ret, 3, PyString_FromString(item->pszText)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 3, Py_None); + } + if (item->mask & TVIF_IMAGE) { + PyTuple_SET_ITEM(ret, 4, PyInt_FromLong(item->iImage)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 4, Py_None); + } + if (item->mask & TVIF_SELECTEDIMAGE) { + PyTuple_SET_ITEM(ret, 5, PyInt_FromLong(item->iSelectedImage)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 5, Py_None); + } + + if (item->mask & TVIF_CHILDREN) { + PyTuple_SET_ITEM(ret, 6, PyInt_FromLong(item->cChildren)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 6, Py_None); + } + if (item->mask & TVIF_PARAM && item->lParam) { + // assume lParam is an object + PyTuple_SET_ITEM(ret, 7, PyInt_FromLong(item->lParam)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 7, Py_None); + } + return ret; +} + +// @pymethod |PyCTreeCtrl|TV_ITEM tuple|Describes a TV_ITEM tuple, used by the object. +// A tuple of 8 items: +// When passed to Python, will always be a tuple of size 8, and items may be None if not available. +// When passed from Python, the tuple may be any length up to 8, and any item may be None. +BOOL ParseTV_ITEMTuple( PyObject *args, TV_ITEM *pItem) +{ + PyObject *ob; + PyObject *ob2; + pItem->mask = 0; + int len = PyTuple_Size(args); + if (len > 8) { + PyErr_SetString(PyExc_TypeError, "TV_ITEM tuple has invalid size"); + return FALSE; + } + PyErr_Clear(); // clear any errors, so I can detect my own. + // 0 - hItem + if (len<1) return TRUE; + if ((ob=PyTuple_GetItem(args, 0))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|hItem||Item handle + pItem->hItem = (HTREEITEM)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + pItem->mask |= TVIF_HANDLE; + } + // 1,2 - state/stateMask + if (len<2) return TRUE; + if (len<3) { + PyErr_SetString(PyExc_TypeError, "TV_ITEM - state and stateMask must be provided"); + return FALSE; + } + if ((ob=PyTuple_GetItem(args, 1))==NULL) + return FALSE; + if ((ob2=PyTuple_GetItem(args, 2))==NULL) + return FALSE; + if (ob==Py_None && ob2==Py_None) + ; + else if (ob==Py_None) { + PyErr_SetString(PyExc_TypeError, "TV_ITEM - state and stateMask must both be None, or both not Noned"); + return FALSE; + } else { + // @pyparm int|state||Item state + // @pyparm int|stateMask||Item state mask + pItem->state = (int)PyInt_AsLong(ob); + pItem->stateMask = (int)PyInt_AsLong(ob2); + if (PyErr_Occurred()) return FALSE; + pItem->mask |= TVIF_STATE; + } + // 3 - text + if (len<4) return TRUE; + if ((ob=PyTuple_GetItem(args, 3))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm string|text||Item text + if (!PyString_Check(ob)) RETURN_TYPE_ERR("The text item must be a string or None"); + pItem->mask |= TVIF_TEXT; + pItem->pszText = PyString_AsString(ob); + pItem->cchTextMax = strlen(pItem->pszText)+1; + } + // 4 - image + if (len<5) return TRUE; + if ((ob=PyTuple_GetItem(args, 4))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->mask |= TVIF_IMAGE; + // @pyparm int|iImage||Offset of items image. + pItem->iImage = (int)PyInt_AsLong(ob); + } + // 5 - imageSelected + if (len<6) return TRUE; + if ((ob=PyTuple_GetItem(args, 5))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|iSelectedImage||Offset of items selected image. + pItem->mask |= TVIF_SELECTEDIMAGE; + pItem->iSelectedImage = (int)PyInt_AsLong(ob); + } + // 6 - cChildren + if (len<7) return TRUE; + if ((ob=PyTuple_GetItem(args, 6))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|cChildren||Number of child items. + pItem->mask |= TVIF_CHILDREN; + pItem->cChildren = (int)PyInt_AsLong(ob); + } + // 7 - object + if (len<8) return TRUE; + if ((ob=PyTuple_GetItem(args, 7))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|lParam||User defined integer param. + pItem->mask |= LVIF_PARAM; + pItem->lParam = PyInt_AsLong(ob); + } + return !PyErr_Occurred(); +} + +///////////////////////////////////////////////////////////////////// +// +// Header Control conversion utilities +// +// +// HD_ITEM +//HDI_BITMAP, HDI_FORMAT, HDI_HEIGHT, HDI_LPARAM, HDI_TEXT, HDI_WIDTH +//fmt is HDF_CENTER, HDF_LEFT, HDF_RIGHT, HDF_BITMAP, HDF_OWNERDRAW, HDF_STRING +PyObject *MakeHD_ITEMTuple(HD_ITEM *item) +{ + PyObject *ret = PyTuple_New(5); + if (ret==NULL) return NULL; + if (item->mask & HDI_HEIGHT) + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong((long)0)); + else if (item->mask & HDI_WIDTH) + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong((long)1)); + if ((item->mask & HDI_HEIGHT) || (item->mask & HDI_WIDTH)) + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong((long)item->cxy)); + else { + Py_INCREF(Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 0, Py_None); + PyTuple_SET_ITEM(ret, 1, Py_None); + } + if ((item->mask & HDI_TEXT) && (item->pszText != NULL) ) { + PyTuple_SET_ITEM(ret, 2, PyString_FromString(item->pszText)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 2, Py_None); + } + if (item->mask & HDI_BITMAP) { + // Should this support a bitmap object? + PyTuple_SET_ITEM(ret, 3, PyInt_FromLong((long)item->hbm)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 3, Py_None); + } + if (item->mask & HDI_FORMAT) { + PyTuple_SET_ITEM(ret, 4, PyInt_FromLong(item->fmt)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 4, Py_None); + } + if (item->mask & TVIF_PARAM && item->lParam) { + // assume lParam is an object + PyTuple_SET_ITEM(ret, 5, PyInt_FromLong(item->lParam)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 5, Py_None); + } + return ret; +} + +// *** When PyCHeaderCtrl is implemented, return the '@' to the next line _and_ the parm! +// pymethod |PyCHeaderCtrl|HD_ITEM tuple|Describes a HD_ITEM tuple, used by the object. +// A tuple of 6 items: +// When passed to Python, will always be a tuple of size 6, and items may be None if not available. +// When passed from Python, the tuple may be any length up to 6, and any item may be None. +BOOL ParseHD_ITEMTuple( PyObject *args, HD_ITEM *pItem) +{ + PyObject *ob; + pItem->mask = 0; + int len = PyTuple_Size(args); + if (len > 6) { + PyErr_SetString(PyExc_TypeError, "HD_ITEM tuple has invalid size"); + return FALSE; + } + PyErr_Clear(); // clear any errors, so I can detect my own. + // 0 - mask + if (len<1) return TRUE; + if ((ob=PyTuple_GetItem(args, 0))==NULL) + return FALSE; + if (ob != Py_None) { + // pyparm int|||Specifies if cxy is width (0) or height (1) + if (ob) + pItem->mask |= HDI_HEIGHT; + else + pItem->mask |= HDI_WIDTH; + } + // 1 - is cxy width or height of item + if (len<2) return TRUE; + if ((ob=PyTuple_GetItem(args, 1))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|cxy||Width or height of item + pItem->cxy = (int)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + //mask updated above + } + // 2 - cxy (measurement of width or height depending on previous arg) + + // 3 - pszText address of item string + if (len<3) return TRUE; + if ((ob=PyTuple_GetItem(args, 2))==NULL) + return FALSE; + if (ob !=Py_None) { + // @pyparm string|pszText||Item text + if (!PyString_Check(ob)) RETURN_TYPE_ERR("The text item must be a string or None"); + pItem->pszText = PyString_AsString(ob); + if (PyErr_Occurred()) return FALSE; + pItem->cchTextMax = strlen(pItem->pszText)+1; + pItem->mask |= HDI_TEXT; + } + // 3 - hbm handle of item bitmap + if (len<4) return TRUE; + if ((ob=PyTuple_GetItem(args, 3))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm string|text||Item text + pItem->mask |= HDI_BITMAP; + pItem->hbm = (HBITMAP)PyInt_AsLong(ob); + if (PyErr_Occurred()) return FALSE; + } + // 4 - fmt of item string + if (len<5) return TRUE; + if ((ob=PyTuple_GetItem(args, 4))==NULL) + return FALSE; + if (ob != Py_None) { + pItem->mask |= HDI_FORMAT; + // @pyparm int|fmt||code for centering etc of string + pItem->fmt = (int)PyInt_AsLong(ob); + } + // 5 - object + if (len<6) return TRUE; + if ((ob=PyTuple_GetItem(args, 5))==NULL) + return FALSE; + if (ob != Py_None) { + // @pyparm int|lParam||User defined integer param. + pItem->mask |= LVIF_PARAM; + pItem->lParam = PyInt_AsLong(ob); + } + return !PyErr_Occurred(); +} + + +///////////////////////////////////////////////////////////////////// +// +// CHARFORMAT and PARAFORMAT conversion utilities +// +// +// @pymethod |win32ui|CHARFORMAT tuple|Describes a CHARFORMAT tuple +BOOL ParseCharFormatTuple( PyObject *args, CHARFORMAT *pFmt) +{ + if (!PyTuple_Check(args)) + RETURN_TYPE_ERR("CHARFORMAT must be a tuple"); + int len = PyTuple_Size(args); + if (len>0) pFmt->dwMask = PyInt_AsLong(PyTuple_GET_ITEM(args, 0)); + if (len>1) pFmt->dwEffects = PyInt_AsLong(PyTuple_GET_ITEM(args, 1)); + if (len>2) pFmt->yHeight = PyInt_AsLong(PyTuple_GET_ITEM(args, 2)); + if (len>3) pFmt->yOffset = PyInt_AsLong(PyTuple_GET_ITEM(args, 3)); + if (len>4) pFmt->crTextColor = PyInt_AsLong(PyTuple_GET_ITEM(args, 4)); + if (len>5) pFmt->bCharSet = (BYTE)PyInt_AsLong(PyTuple_GET_ITEM(args, 5)); + if (len>6) pFmt->bPitchAndFamily = (BYTE)PyInt_AsLong(PyTuple_GET_ITEM(args, 6)); + if (len>7) { + PyObject *obFont = PyTuple_GET_ITEM(args, 7); + if (obFont != Py_None) { + if (!PyString_Check(obFont)) + RETURN_TYPE_ERR("Font name must be None or a string"); + strncpy(pFmt->szFaceName, PyString_AsString(obFont), sizeof(pFmt->szFaceName)); + } + } + // @pyparm int|mask|0|The mask to use. Bits in this mask indicate which of the following paramaters are interpreted. Must be a combination the win32con.CFM_* constants. + // @pyparm int|effects|None|The effects to use. Must be a combination the win32con.CFE_* constants. + // @pyparm int|yHeight|None|The y height. + // @pyparm int|yOffset|None|Character offset from the baseline. If this member is positive, the character is a superscript; if it is negative, the character is a subscript. + // @pyparm int|colorText|None|The color to use. + // @pyparm int|bCharSet|None|The charset. See the LOGFONT structure for details. + // @pyparm int|bPitchAndFamily|None|The charset. See the LOGFONT structure for details. + // @pyparm string|faceName|None|The font name. + + // @comm Executing d=win32ui.CreateFontDialog(); d.DoModal(); print d.GetCharFormat() + // will print a valid CHARFORMAT tuple. + return !PyErr_Occurred(); +} + +PyObject *MakeCharFormatTuple(CHARFORMAT *pFmt) +{ + return Py_BuildValue("iillibbs", + pFmt->dwMask, + pFmt->dwEffects, + pFmt->yHeight, + pFmt->yOffset, + pFmt->crTextColor, + pFmt->bCharSet, + pFmt->bPitchAndFamily, + pFmt->szFaceName); +} + +// @pymethod|win32ui|PARAFORMAT tuple|Describes a PARAFORMAT tuple +BOOL ParseParaFormatTuple( PyObject *args, PARAFORMAT *pFmt) +{ + PyObject *obTabStops = Py_None; + pFmt->cTabCount = 0; + BOOL rc = PyArg_ParseTuple(args, "|iiiiiiiO:PARAFORMAT tuple", + &pFmt->dwMask, // @pyparm int|mask|0|The mask to use. Bits in this mask indicate which of the following paramaters are interpreted. Must be a combination the win32con.PFM_* constants. + &pFmt->wNumbering, // @pyparm int|numbering|None|The numbering style to use. + &pFmt->wReserved, // @pyparm int|yHeight|None|Reserved + &pFmt->dxStartIndent, // @pyparm int|dxStartIndext|None|Indentation of the first line. + &pFmt->dxRightIndent,// @pyparm int|dxRightIndent|None|Indentation from the right. + &pFmt->dxOffset,// @pyparm int|dxOffset|None|The indent of second and subsequent lines. + &pFmt->wAlignment,// @pyparm int|wAlignment|None|The alignment of the paragraph. + &obTabStops); // @pyparm sequence of ints|tabStops|None|The tabstops to use. + if (rc && obTabStops != Py_None) { + if (!PySequence_Check(obTabStops)) + RETURN_ERR("tabStops object must be None or a sequence"); + int tabCount = PyObject_Length(obTabStops); + tabCount = min(MAX_TAB_STOPS, tabCount); + for (int i=0;rc && irgxTabs[i] = PyInt_AsLong( PySequence_GetItem(obTabStops, i) ); + rc = PyErr_Occurred()==FALSE; + if (!rc) break; + pFmt->cTabCount++; + } + } + return rc; +} + +PyObject *MakeParaFormatTuple(PARAFORMAT *pFmt) +{ + PyObject *obTabs; + if (pFmt->cTabCount==0) { + Py_INCREF(Py_None); + obTabs = Py_None; + } else { + obTabs = PyTuple_New(pFmt->cTabCount); + for (int i=0;icTabCount;i++) + PyTuple_SetItem( obTabs, i, PyInt_FromLong(pFmt->rgxTabs[i])); + } + PyObject *ret = Py_BuildValue("iiiiiiiO", + pFmt->dwMask, + pFmt->wNumbering, + pFmt->wReserved, + pFmt->dxStartIndent, + pFmt->dxRightIndent, + pFmt->dxOffset, + pFmt->wAlignment, + obTabs); + Py_DECREF(obTabs); // ref added by BuildValue +// Py_DECREF(obTabs); // reference I created. + return ret; +} + +///////////////////////////////////////////////////////////////////// +// +// Other utilities +// +// +// Given a long that holds a pointer, return +// a Python object. Used by listboxes and tree +// controls etc that keep a pointer to a Python object, +// but due to difficulties managing the lifetimes, +// does not keep a Python reference. This function +// effectvly is just a cast with a fairly solid check +// that the object is still a valid PyObject * (ie, +// has not been destroyed since we copied the pointer). +// DOES NOT add a reference to the returned object. +PyObject *PyWin_GetPythonObjectFromLong(long val) +{ + PyObject *ret = (PyObject *)val; + if (ret==NULL) + return Py_None; + BOOL ok; + __try { + ok = ret->ob_refcnt != 0; + ok = ok && ret->ob_type->tp_name[0] != 0; + } + __except (EXCEPTION_ACCESS_VIOLATION) { + ok = FALSE; + } + if (!ok) + RETURN_ERR("The object is invalid"); + return ret; +} + +CString GetAPIErrorString(char *fnName) +{ + CString csBuf = fnName + CString(" failed - "); + DWORD errorCode = GetLastError(); + if (errorCode) { + CString message = GetAPIErrorString(errorCode); + if (message.GetLength()>0) + csBuf += message; + else { + char buf[80]; + sprintf(buf,"error code was %d - no error message is available",errorCode); + csBuf += buf; + } + } + else + csBuf += "no error code is available"; + return csBuf; +} + +CString GetAPIErrorString(DWORD errCode) +{ + CString csBuf; + const int bufSize = 512; + char *buf = csBuf.GetBuffer( bufSize ); + ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, 0, buf, bufSize, NULL ); + csBuf.ReleaseBuffer (-1); + return csBuf; +} + +ui_type_CObject *UITypeFromName( const char *name ) +{ + ASSERT(ui_type_CObject::typemap); + POSITION pos = ui_type_CObject::typemap->GetStartPosition(); + while (pos) { + CRuntimeClass *key; + ui_type_CObject *value; + ui_type_CObject::typemap->GetNextAssoc(pos, key, value); + if (strcmp(value->tp_name, name)==0) + return value; + } + return NULL; +} + +ui_type_CObject &UITypeFromCObject( CObject *ob ) +{ + ui_type_CObject *ret; + CRuntimeClass *prtCWnd = RUNTIME_CLASS(CWnd); + + // loop down the base class list. + ASSERT(ui_type_CObject::typemap); + ASSERT(ob); + if(ui_type_CObject::typemap==NULL || ob==NULL) return ui_assoc_CObject::type; + + CRuntimeClass *prt = ob->GetRuntimeClass(); + while (prt) { + // If we get here, and we only have a CWnd, then we use other tricks!! + if (prt==prtCWnd) + return UITypeFromHWnd(((CWnd *)ob)->GetSafeHwnd()); + if (ui_type_CObject::typemap->Lookup(prt, ret)) + return *ret; + prt = (*prt->m_pfnGetBaseClass)(); + } + TRACE("Warning - unknown class type in UITypeFromCObject"); + ret = &ui_assoc_CObject::type; // will have no methods - really an error + return *ret; +} + +ui_type_CObject &UITypeFromHWnd( HWND hwnd ) +{ + ui_type_CObject *ret; + // generic window - see if class name can help us. + char szClassName[64]; + ::GetClassName( hwnd, szClassName, sizeof(szClassName)); + // getting really lazy here. + if (strcmp(szClassName, "ListBox")==0) + ret = &PyCListBox::type; + else if (strcmp(szClassName, "ComboBox")==0) + ret = &PyCComboBox::type; + else if (strcmp(szClassName, "Button")==0) + ret = &PyCButton::type; + else if (strcmp(szClassName, "Edit")==0) + ret = &PyCEdit::type; + else if (strcmp(szClassName, "RICHEDIT")==0) + ret = &PyCRichEditCtrl::type; + else if (strcmp(szClassName, "SysListView32")==0) + ret = &PyCListCtrl::type; + else if (strcmp(szClassName, "SysTreeView32")==0) + ret = &PyCTreeCtrl::type; + else if (strcmp(szClassName, "msctls_progress32")==0) + ret = &PyCProgressCtrl::type; + else if (strcmp(szClassName, "msctls_trackbar32")==0) + ret = &PyCSliderCtrl::type; + else if (strcmp(szClassName, "msctls_updown32")==0) + ret = &PyCSpinButtonCtrl::type; + // now handle some special cases to avoid warning below! + else if (strcmp(szClassName, "MDIClient")==0 || + strcmp(szClassName, "ConsoleWindowClass")==0) + ret = &PyCWnd::type; + else { +// TRACE("Generic window returned for class name '%s'\n", szClassName); + ret = &PyCWnd::type; + } + return *ret; +} + +// utility to get a nice printable string from any object. +// reference neutral. +CString GetReprText( PyObject *objectUse ) +{ + // special case for integers first. + if (PyInt_Check(objectUse)) + return (int)PyInt_AsLong(objectUse); + PyObject *s; + if (PyString_Check(objectUse)) // if it is a string, then no need to repr it + s = objectUse; // and repr on a string may mangle it (eg + else { // turn '\t' into a literal "\t" + s = PyObject_Str(objectUse); + if (s==NULL) { + PyErr_Clear(); + s = PyObject_Repr(objectUse); + } + if (s==NULL) { + PyErr_Clear(); + return CString("No str() or repr()?!"); + } + } + const char *szRepr = PyString_AsString(s); + int len=strlen(szRepr); + if (len && strchr("\"'[(", *szRepr)) { + if (szRepr[len-1]==*szRepr) { + ++szRepr; + len-=2; // drop first and last chars. + } + } + CString csRes = CString( szRepr, len ); + return csRes; +} + diff --git a/Pythonwin/win32view.cpp b/Pythonwin/win32view.cpp new file mode 100644 index 0000000000..23b2ba4f38 --- /dev/null +++ b/Pythonwin/win32view.cpp @@ -0,0 +1,1123 @@ +/* + + win32 view data type + + Created Dec 1995, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32dc.h" +#include "win32prinfo.h" +#include "win32control.h" +#include "win32ctrlList.h" +#include "win32ctrlTree.h" + +CPythonEditView *GetEditViewPtr(PyObject *self) +{ + // need to only rtti check on CView, as CPythonEditView is not derived from CPythonView. + return (CPythonEditView *)PyCWnd::GetGoodCppObject( self, &PyCView::type ); +} + +CView *PyCView::GetViewPtr(PyObject *self) +{ + // ditto! + return (CView *)PyCWnd::GetGoodCppObject( self, &PyCView::type ); +} + +CScrollView *PyCScrollView::GetViewPtr(PyObject *self) +{ + // ditto! + return (CScrollView *)PyCWnd::GetGoodCppObject( self, &PyCScrollView::type ); +} + +CListView *GetListViewPtr(PyObject *self) +{ + // ditto! + return (CListView *)PyCWnd::GetGoodCppObject( self, &PyCListView::type ); +} + +CTreeView *GetTreeViewPtr(PyObject *self) +{ + // ditto! + return (CTreeView *)PyCWnd::GetGoodCppObject( self, &PyCTreeView::type ); +} + +// +// Hack to get around protected members +// +class CProtectedView : public CView +{ +public: + void SetDocument(CDocument *pDoc) {m_pDocument = pDoc;} + void BaseOnActivateView( BOOL bActivate, CView* pActivateView, CView* pDeactiveView ) \ + {CView::OnActivateView(bActivate,pActivateView,pDeactiveView );} + BOOL BasePreCreateWindow(CREATESTRUCT &cs) {return CView::PreCreateWindow(cs);} + void BaseOnFilePrint() {CView::OnFilePrint();} + void BaseOnFilePrintPreview() {CView::OnFilePrintPreview();} + void BaseOnBeginPrinting(CDC *dc, CPrintInfo *pInfo) {CView::OnBeginPrinting(dc, pInfo);} + void BaseOnEndPrinting(CDC *dc, CPrintInfo *pInfo) {CView::OnEndPrinting(dc, pInfo);} + BOOL DoPreparePrinting(CPrintInfo *pInfo) {return CView::DoPreparePrinting(pInfo);} + BOOL BaseOnPreparePrinting(CPrintInfo *pInfo) {return CView::OnPreparePrinting(pInfo);} + int BaseOnMouseActivate( CWnd *pWnd, UINT ht, UINT msg) { return CView::OnMouseActivate(pWnd, ht, msg);} +}; + +// And an extra hack for splitters! +void PyWin_SetViewDocument(CView *pView, CDocument *pDoc) +{ + ((CProtectedView *)pView)->SetDocument(pDoc); +} + +// Hacks to get around protected members. +class CProtectedScrollView : public CScrollView +{ +public: + void BaseUpdateBars() {CScrollView::UpdateBars();} +}; + + +// @pymethod |PyCView|CreateWindow|Creates the window for a view. +static PyObject * +PyCView_create_window(PyObject *self, PyObject *args) +{ + CView *pView = PyCView::GetViewPtr(self); + if (!pView) { + // PyCView::GetViewPtr will trace to the debug device - just so I know it is an OK one! + TRACE(" ignore warning - RTTI Error detected and handled!"); + PyErr_Clear(); + pView = GetEditViewPtr(self); + } + if (!pView) + return NULL; + + if (pView->m_hWnd!=NULL) + RETURN_ERR("The view already has a window"); + + PyObject *parent; + int id= AFX_IDW_PANE_FIRST; + int style= AFX_WS_DEFAULT_VIEW; + CRect rect(0,0,0,0); + if (!PyArg_ParseTuple(args, "O|ii(iiii):CreateWindow", + &parent, // @pyparm |parent||The parent window (usually a frame) + &id, // @pyparm int|id|win32ui.AFX_IDW_PANE_FIRST|The child ID for the view + &style, // @pyparm int|style|win32ui.AFX_WS_DEFAULT_VIEW|The style for the view + // @pyparm (left, top, right, bottom)|rect|(0,0,0,0)|The default position of the window. + &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + + if (!ui_base_class::is_uiobject(parent, &PyCWnd::type)) + RETURN_TYPE_ERR("Argument must be a PyCWnd"); + CWnd *pWnd = GetWndPtr( parent ); + if (pWnd==NULL) + return NULL; + + CCreateContext context; + GUI_BGN_SAVE; + context.m_pCurrentDoc = pView->GetDocument(); +// if (!context.m_pCurrentDoc) +// RETURN_ERR("There is no document attached to the view"); + + // must reset doc to NULL, else MFC asserts all over the place! + // Create() resets this value (via the CreateContext) + ((CProtectedView *)pView)->SetDocument( NULL ); + + BOOL ok; + ok = pView->Create(NULL, NULL, style, rect, pWnd, id, &context); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("Create() view failed\n"); + RETURN_NONE; +} + +// @pymethod |PyCView|GetDocument|Returns the document for a view. +static PyObject * +PyCView_get_document (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CView *view = PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + CDocument *pDoc = view->GetDocument(); + GUI_END_SAVE; + return ui_assoc_object::make(PyCDocument::type, pDoc)->GetGoodRet(); +} + +// @pymethod |PyCView|OnInitialUpdate|Calls the underlying MFC OnInitialUpdate method. +PyObject * +PyCView_on_initial_update(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, OnInitialUpdate); + CView *view = PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + // @xref + GUI_BGN_SAVE; + view->CView::OnInitialUpdate(); + GUI_END_SAVE; + RETURN_NONE; +} + + +// @pymethod int|PyCView|OnActivateView|Calls the underlying MFC OnActivateView method. +PyObject * +PyCView_on_activate_view(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|activate||Indicates whether the view is being activated or deactivated. + // @pyparm |activateView||The view object that is being activated. + // @pyparm |DeactivateView||The view object that is being deactivated. + int activate; + PyObject *obActivate, *obDeactivate; + if (!PyArg_ParseTuple (args, "iOO:OnActivateView", &activate, &obActivate, &obDeactivate)) + return NULL; + CView *view = PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + + PyErr_Clear(); + CView *pActivate = obActivate==Py_None ? NULL : PyCView::GetViewPtr (obActivate); + CView *pDevactive = obDeactivate==Py_None ? NULL : PyCView::GetViewPtr (obDeactivate); + if (PyErr_Occurred()) + return NULL; + GUI_BGN_SAVE; + ((CProtectedView *)view)->BaseOnActivateView(activate, pActivate, pDevactive); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCView|OnMouseActivate|Calls the base MFC OnMouseActivate function. +// @xref +static PyObject * +PyCView_on_mouse_activate( PyObject *self, PyObject *args) +{ + extern CWnd *GetWndPtrFromParam(PyObject *ob, ui_type_CObject &type); + + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + PyObject *obWnd; + int ht, msg; + // @pyparm |wnd|| + // @pyparm int|hittest|| + // @pyparm int|message|| + if (!PyArg_ParseTuple(args, "Oii:OnMouseActivate", &obWnd, &ht, &msg)) + return NULL; + CWnd *pWndArg = GetWndPtrFromParam(obWnd, PyCWnd::type); + if (pWndArg==NULL) return NULL; + GUI_BGN_SAVE; + UINT rc = view->BaseOnMouseActivate(pWndArg, ht, msg); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod tuple|PyCView|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +// @xref +PyObject * +PyCView_pre_create_window(PyObject *self, PyObject *args) +{ + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = view->BasePreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CView::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod |PyCView|OnFilePrint|Calls the underlying MFC OnFilePrint method. +PyObject * +PyCView_on_file_print(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, OnFilePrint); + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + view->BaseOnFilePrint(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCView|OnFilePrintPreview|Calls the underlying MFC OnFilePrintPreview method. +PyObject * +PyCView_on_file_print_preview(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, OnFilePrint); + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + view->BaseOnFilePrintPreview(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCView|DoPreparePrinting|Invoke the Print dialog box and create a printer device context. +PyObject * +PyCView_do_prepare_printing(PyObject *self, PyObject *args) +{ + PyObject *pyInfo; + if (!PyArg_ParseTuple (args, "O:DoPreparePrinting", &pyInfo)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(pyInfo); + if (!pInfo) + return NULL; + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + BOOL ret = view->DoPreparePrinting(pInfo); + GUI_END_SAVE; + return Py_BuildValue("i", ret); + // @comm This function is usually called from +} + +// @pymethod int|PyCView|OnPreparePrinting|Calls the underlying MFC OnPreparePrinting method. +// @xref +PyObject * +PyCView_on_prepare_printing(PyObject *self, PyObject *args) +{ + PyObject *pyInfo; + if (!PyArg_ParseTuple (args, "O:OnPreparePrinting", &pyInfo)) + return NULL; + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(pyInfo); + if (!pInfo) + return NULL; + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + BOOL ret = view->BaseOnPreparePrinting(pInfo); + GUI_END_SAVE; + return Py_BuildValue("i", ret); +} + +// @pymethod |PyCView|OnBeginPrinting|Calls the underlying MFC OnBeginPrinting method. +// @xref +PyObject * +PyCView_on_begin_printing(PyObject *self, PyObject *args) +{ + PyObject *pyDC; + PyObject *pyInfo; + + if (!PyArg_ParseTuple (args, "OO:OnBeginPrinting", &pyDC, &pyInfo)) + return NULL; + if (!ui_base_class::is_uiobject (pyDC, &ui_dc_object::type)) + RETURN_TYPE_ERR("The first param must be a PyCDC object"); + CDC *pDC = ui_dc_object::GetDC(pyDC); + if (!pDC) + RETURN_ERR("The DC is invalid"); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(pyInfo); + if (!pInfo) + return NULL; + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + view->BaseOnBeginPrinting(pDC, pInfo); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCView|OnEndPrinting|Calls the underlying MFC OnEndPrinting method. +// @xref +PyObject * +PyCView_on_end_printing(PyObject *self, PyObject *args) +{ + PyObject *pyDC; + PyObject *pyInfo; + + if (!PyArg_ParseTuple (args, "OO:OnEndPrinting", &pyDC, &pyInfo)) + return NULL; + if (!ui_base_class::is_uiobject (pyDC, &ui_dc_object::type)) + RETURN_TYPE_ERR("The first param must be a PyCDC object"); + CDC *pDC = ui_dc_object::GetDC(pyDC); + if (!pDC) + RETURN_ERR("The DC is invalid"); + CPrintInfo *pInfo = ui_prinfo_object::GetPrintInfo(pyInfo); + if (!pInfo) + return NULL; + CProtectedView *view = (CProtectedView*)PyCView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + view->BaseOnEndPrinting(pDC, pInfo); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCView|A class which implements a generic CView. Derived from a object. +static struct PyMethodDef PyCView_methods[] = { + {"CreateWindow", PyCView_create_window, 1}, // @pymeth CreateWindow|Create the window for a view. + {"GetDocument", PyCView_get_document, 1}, // @pymeth GetDocument|Returns the document for a view. + {"OnActivateView", PyCView_on_activate_view, 1}, // @pymeth OnActivateView|Calls the underlying MFC OnActivateView method. + {"OnInitialUpdate", PyCView_on_initial_update, 1}, // @pymeth OnInitialUpdate|Calls the underlying MFC OnInitialUpdate method. + {"OnMouseActivate", PyCView_on_mouse_activate, 1}, // @pymeth OnMouseActivate|Calls the underlying MFC OnMouseActivate method. + {"PreCreateWindow", PyCView_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"OnFilePrint", PyCView_on_file_print, 1}, // @pymeth OnFilePrint|Calls the underlying MFC OnFilePrint method. + {"OnFilePrintPreview", PyCView_on_file_print_preview, 1}, // @pymeth OnFilePrint|Calls the underlying MFC OnFilePrintPreview method. + {"DoPreparePrinting", PyCView_do_prepare_printing, 1}, // @pymeth DoPreparePrinting|Calls the underlying MFC DoPreparePrinting method. + {"OnBeginPrinting", PyCView_on_begin_printing, 1}, // @pymeth OnBeginPrinting|Calls the underlying MFC OnBeginPrinting method. + {"OnEndPrinting", PyCView_on_end_printing, 1}, // @pymeth OnEndPrinting|Calls the underlying MFC OnEndPrinting method. + {NULL, NULL} +}; + +// View type +ui_type_CObject PyCView::type("PyCView", + &PyCWnd::type, + RUNTIME_CLASS(CView), + sizeof(PyCView), + PyCView_methods, + NULL); + +// @pymethod |win32ui|CreateView|Creates a generic view object. +PyObject * PyCScrollView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + // @pyparm |doc||The document to use with the view. + if (!PyArg_ParseTuple(args, "O:CreateView", &doc)) + return NULL; + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + GUI_BGN_SAVE; + CPythonView *pView = new CPythonView(); + ((CProtectedView *)pView)->SetDocument(pDoc); + GUI_END_SAVE; + return ui_assoc_object::make( PyCScrollView::type, pView ); +} + +// @pymethod |PyCScrollView|GetDC|Gets the view's current DC. +static PyObject * +ui_view_get_dc (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + + // create MFC device context + GUI_BGN_SAVE; + CDC *pDC = view->GetDC(); + if (pDC==NULL) { + GUI_BLOCK_THREADS + RETURN_ERR ("Could not get the DC for the view."); + } + + // update logical 0,0 position based on scroll position + CPoint offset = view->GetDeviceScrollPosition(); + // really should be SetDeviceOrgEx (bad MS names, don'cha know) + SetViewportOrgEx (pDC->GetSafeHdc(), -offset.x, -offset.y, NULL); + GUI_END_SAVE; + + // create Python device context + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC); + return dc; +} + +// @pymethod (x,y)|PyCScrollView|GetDeviceScrollPosition|Returns the positon of the scroll bars in device units. +static PyObject * +ui_view_get_dscroll_pos (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + + GUI_BGN_SAVE; + CPoint pos = view->GetDeviceScrollPosition(); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", pos.x, pos.y); +} + +// @pymethod |PyCScrollView|ScrollToPosition|Scrolls to a given point in the view. +static PyObject * +ui_view_scroll_to_position (PyObject *self, PyObject *args) +{ + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + + POINT position; + // @pyparm (x,y)|position||The position to scroll to. + if (!PyArg_ParseTuple (args, "(ii):ScrollToPosition", &position.x, &position.y)) + return NULL; + + GUI_BGN_SAVE; + view->ScrollToPosition (position); + GUI_END_SAVE; + + RETURN_NONE; +} + +// @pymethod tuple|PyCScrollView|ResizeParentToFit|Lets the size of a view dictate the size of its frame window. +PyObject * +ui_view_resize_parent_to_fit(PyObject *self, PyObject *args) +{ + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + BOOL bShrink = TRUE; + //@pyparm int|bShrinkOnly|1|The kind of resizing to perform. The default value, TRUE, shrinks the frame window if appropriate. + if (!PyArg_ParseTuple(args, "|i:ResizeParentToFit", &bShrink)) + return NULL; + GUI_BGN_SAVE; + view->ResizeParentToFit(bShrink); + GUI_END_SAVE; + // @comm This is recommended only for views in MDI child frame windows. + // Use ResizeParentToFit in the OnInitialUpdate handler function of your View class. + // You must ensure the parent's is called before using this method. + RETURN_NONE; +} + +// @pymethod |PyCScrollView|SetScaleToFitSize|Scales the viewport size to the current window size automatically. +static PyObject * +ui_view_set_scale_to_fit_size(PyObject *self, PyObject *args) +{ + CScrollView *pView = PyCScrollView::GetViewPtr (self); + if (pView == NULL) + return NULL; + SIZE size; + // @pyparm (x,y)|size||The horizontal and vertical sizes to which the view is to be scaled. The scroll view's size is measured in logical units. + if (!PyArg_ParseTuple(args, "(ll)", &size.cx, &size.cy)) + return NULL; + GUI_BGN_SAVE; + pView->SetScaleToFitSize(size); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCScrollView|SetScrollSizes|Sets the sizes of the scroll bars +static PyObject * +ui_view_set_scroll_sizes (PyObject *self, PyObject *args) +{ + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + int map_mode; + CSize total, + page = CScrollView::sizeDefault, + line = CScrollView::sizeDefault; + if (!PyArg_ParseTuple (args, "i(ii)|(ii)(ii):SetScrollSizes", + &map_mode, // @pyparm int|mapMode||The mapping mode for this view. + &total.cx, &total.cy, // @pyparm (x,y)|sizeTotal||The total size of the view. Sizes are in logical units. Both x and y must be greater than zero. + &page.cx, &page.cy, // @pyparm (x,y)|sizePage|win32ui.rectDefault|The number of untils to scroll in response to a page-down command. + &line.cx, &line.cy)) // @pyparm (x,y)|sizePage|win32ui.rectDefault|The number of untils to scroll in response to a line-down command. + return NULL; + + GUI_BGN_SAVE; +// BOOL save = view->SetDynamicScrollBars (TRUE); + view->SetScrollSizes (map_mode, total, page, line); +// view->SetDynamicScrollBars (save); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod (x,y)|PyCScrollView|GetScrollPosition|Returns the current position of the scroll bars (in logical units). +static PyObject * +PyCView_get_scroll_pos (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + CPoint pos = view->GetScrollPosition(); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", pos.x, pos.y); +} +// @pymethod (x,y)|PyCScrollView|GetTotalSize|Returns the total size of the view in logical units. +static PyObject * +PyCView_get_total_size (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + CSize size = view->GetTotalSize(); + GUI_END_SAVE; + return Py_BuildValue ("(ii)", size.cx, size.cy); +} + +// @pymethod |PyCScrollView|UpdateBars|Update the scroll bars state +static PyObject * +ui_view_update_bars (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CScrollView *view = PyCScrollView::GetViewPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + ((CProtectedScrollView *)view)->BaseUpdateBars(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCScrollView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonView, &PyCScrollView::type, self, args); +} + +// @object PyCScrollView|A class which implements a generic CScrollView. Derived from a object. +static struct PyMethodDef PyCScrollView_methods[] = { + {"GetDeviceScrollPosition", ui_view_get_dscroll_pos, 1}, // @pymeth GetDeviceScrollPosition|Return the position of the scroll bars (device units). + {"GetDC", ui_view_get_dc, 1}, // @pymeth GetDC|Get the views current + {"GetScrollPosition", PyCView_get_scroll_pos, 1}, // @pymeth GetScrollPosition|Return the position of the scroll bars (logical units). + {"GetTotalSize", PyCView_get_total_size, 1}, // @pymeth GetTotalSize|Return the total size of the views. + {"OnCommand", PyCView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {"ResizeParentToFit", ui_view_resize_parent_to_fit, 1}, // @pymeth ResizeParentToFit|Call ResizeParentToFit to let the size of your view dictate the size of its frame window. + {"SetScaleToFitSize", ui_view_set_scale_to_fit_size, 1}, // @pymeth SetScaleToFitSize|Scales the viewport size to the current window size automatically. + {"ScrollToPosition", ui_view_scroll_to_position, 1}, // @pymeth ScrollToPosition|Scroll to a specified point. + {"SetScrollSizes", ui_view_set_scroll_sizes, 1}, // @pymeth SetScrollSizes|Set the scrolling sizes. + {"UpdateBars", ui_view_update_bars, 1}, // @pymeth UpdateBars|Update the scroll bar state. + {NULL, NULL} +}; + +// View type +ui_type_CObject PyCScrollView::type("PyCScrollView", + &PyCView::type, + RUNTIME_CLASS(CScrollView), + sizeof(PyCScrollView), + PyCScrollView_methods, + GET_PY_CTOR(PyCScrollView)); + +/////////////////////////////////////// +// +// Control View Methods +// +// inherited from view +// +/////////////////////////////////////// + +// @pymethod |win32ui|CreateCtrlView|Creates a control view object. +PyObject * PyCCtrlView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + char *szClass; + int style=0; + // @pyparm |doc||The document. + // @pyparm string|className||The class name of the control + // @pyparm int|style|0|Additional style bits + if (!PyArg_ParseTuple(args, "Os|i:CreateCtrlView", &doc, &szClass, &style)) + return NULL; + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + CCtrlView *pView; + GUI_BGN_SAVE; + pView = new CPythonCtrlView(szClass, style); + ((CProtectedView *)pView)->SetDocument(pDoc); + GUI_END_SAVE; + return ui_assoc_object::make( PyCCtrlView::type, pView ); +} + +// @pymethod |PyCCtrlView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCCtrlView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonCtrlView, &PyCCtrlView::type, self, args); +} + +// @object PyCCtrlView|A class which implementes a CCtrlView (ie, a view based on a dialog resource. +static struct PyMethodDef PyCCtrlView_methods[] = { + {"OnCommand", PyCCtrlView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {NULL, NULL} +}; + +PyCCtrlView_Type PyCCtrlView::type("PyCCtrlView", + &PyCView::type, + &PyCWnd::type, + RUNTIME_CLASS(CCtrlView), + sizeof(PyCCtrlView), + PyCCtrlView_methods, + GET_PY_CTOR(PyCCtrlView)); + +/* Implement an psuedo-inheritance for ControlView */ +PyObject * +PyCCtrlView::getattr(char *name) +{ + // implement inheritance. + PyObject *retMethod = PyCView::getattr(name); + if (!retMethod) { + PyErr_Clear(); + PyCCtrlView_Type *thisType = (PyCCtrlView_Type *)ob_type; + if (thisType) + retMethod = Py_FindMethod(thisType->control->methods, (PyObject *)this, name); + } + return retMethod; +} + +///////////////////////////////////////////////////////////////////// +// +// Edit View object +// +////////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|CreateEditView|Creates a PyEditView object. +PyObject * PyCEditView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + // @pyparm |doc||The document to use with the view. + if (!PyArg_ParseTuple(args, "O:CreateEditView", &doc)) + return NULL; + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + CPythonEditView *pView = new CPythonEditView(); + ((CProtectedView *)pView)->SetDocument(pDoc); + return ui_assoc_object::make( PyCEditView::type, pView ); +} + + +// @pymethod |PyCEditView|GetEditCtrl|returns the underlying edit control object. +static PyObject * +ui_edit_get_edit_ctrl(PyObject *self, PyObject *args) +{ + CPythonEditView *pView = GetEditViewPtr(self); + if (!pView) + return NULL; + GUI_BGN_SAVE; + CEdit &ed = pView->GetEditCtrl(); + GUI_END_SAVE; + return ui_assoc_object::make( UITypeFromCObject(&ed), &ed )->GetGoodRet(); +} + +// @pymethod |PyCEditView|SetModifiedFlag|Sets the modified flag for the view's document. +static PyObject * +ui_edit_window_set_modified_flag(PyObject *self, PyObject *args) +{ + CPythonEditView *pView = GetEditViewPtr(self); + if (!pView) + return NULL; + BOOL bState = TRUE; + // @pyparm int|bModified|1|The modified state to set. + if (!PyArg_ParseTuple(args, "|i:SetModifiedFlag", &bState)) + return NULL; + GUI_BGN_SAVE; + pView->GetDocument()->SetModifiedFlag(bState); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCEditView|IsModified|Indicates if the view's document has the modified flag set. +static PyObject * +ui_edit_window_is_modified(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CPythonEditView *pView = GetEditViewPtr(self); + if (!pView) + return NULL; + GUI_BGN_SAVE; + int rc = pView->GetDocument()->IsModified(); + GUI_END_SAVE; + return Py_BuildValue("i",rc); +} + +// @pymethod |PyCEditView|LoadFile|Loads a file into the view. +static PyObject * +ui_edit_window_load_file(PyObject *self, PyObject *args) +{ + char *fileName; + // @pyparm string|fileName||The name of the file to be loaded. + if (!PyArg_ParseTuple(args, "s:LoadFile", &fileName)) + return NULL; + + CPythonEditView *pView; + if (!(pView=GetEditViewPtr(self))) + return NULL; + + CFile file; + CFileException fe; + if (!file.Open(fileName, CFile::modeRead | CFile::shareDenyWrite, &fe)) { + long errCode = fe.m_lOsError; + CString csMessage = GetAPIErrorString(errCode); + if (csMessage.GetLength()) + PyErr_SetString(PyExc_IOError, (char *)(const char *)csMessage); + else + PyErr_SetString(PyExc_IOError, "Unknown IO error?"); + return NULL; + } + GUI_BGN_SAVE; + pView->DeleteContents(); + CArchive loadArchive(&file, CArchive::load | CArchive::bNoFlushOnDelete); + TRY + { + pView->BeginWaitCursor(); + pView->SerializeRaw(loadArchive); // load me + loadArchive.Close(); + file.Close(); + } + CATCH_ALL(e) + { + file.Abort(); // will not throw an exception + pView->EndWaitCursor(); + pView->DeleteContents(); // remove failed contents + e->Delete(); + GUI_BLOCK_THREADS; + PyErr_SetString(PyExc_IOError, "File error reading file"); + return NULL; + } + END_CATCH_ALL + + pView->EndWaitCursor(); + CDocument *pDocument = pView->GetDocument(); + if (pDocument) + pDocument->SetModifiedFlag(FALSE); // start off with unmodified + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCEditView|SaveFile|Saves the view to a file. +static PyObject * +ui_edit_window_save_file(PyObject *self, PyObject *args) +{ + char *fileName; + // @pyparm string|fileName||The name of the file to be written. + if (!PyArg_ParseTuple(args, "s:SaveFile", &fileName)) + return NULL; + + CPythonEditView *pView; + if (!(pView=GetEditViewPtr(self))) + return NULL; + + CFile file; + CFileException fe; + + if (!file.Open(fileName, CFile::modeCreate | + CFile::modeReadWrite | CFile::shareExclusive, &fe)) { + long errCode = fe.m_lOsError; + CString csMessage = GetAPIErrorString(errCode); + if (csMessage.GetLength()) + PyErr_SetString(PyExc_IOError, (char *)(const char *)csMessage); + else + PyErr_SetString(PyExc_IOError, "Unknown IO error?"); + return NULL; + } + GUI_BGN_SAVE; + CArchive saveArchive(&file, CArchive::store | CArchive::bNoFlushOnDelete); + TRY + { + pView->BeginWaitCursor(); + pView->SerializeRaw(saveArchive); // save me + saveArchive.Close(); + file.Close(); + } + CATCH_ALL(e) + { + file.Abort(); // will not throw an exception + pView->EndWaitCursor(); + e->Delete(); + GUI_BLOCK_THREADS; + PyErr_SetString(PyExc_IOError, "File error saving file"); + return NULL; + } + END_CATCH_ALL + + pView->EndWaitCursor(); + CDocument *pDocument = pView->GetDocument(); + if (pDocument) + pDocument->SetModifiedFlag(FALSE); // start off with unmodified + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod tuple|PyCEditView|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +PyObject * +PyCEditView_pre_create_window(PyObject *self, PyObject *args) +{ + CPythonEditView *pView; + if (!(pView=GetEditViewPtr(self))) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + ok = pView->PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) // WARNING - If CPythonEditView::PreCreateWindow gets + RETURN_ERR("CEditView::PreCreateWindow failed"); // fixed, this will break. + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod |PyCEditView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCEditView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonEditView, &PyCEditView::type, self, args); +} + +/////////////////////////////////////// +// +// Edit View Methods +// +// inherited from view +// +/////////////////////////////////////// +// @object PyCEditView|A class which implementes a CView of a text file. Derived from and objects. +static struct PyMethodDef ui_edit_window_methods[] = { + {"IsModified", ui_edit_window_is_modified, 1}, // @pymeth IsModified|Indicates if the view's document is modified. + {"LoadFile", ui_edit_window_load_file, 1}, // @pymeth LoadFile|Loads a named file into the view. + {"SetModifiedFlag", ui_edit_window_set_modified_flag, 1}, // @pymeth SetModifiedFlag|Sets the view's document modified flag. + {"GetEditCtrl", ui_edit_get_edit_ctrl, 1}, // @pymeth GetEditCtrl|Returns the underlying object + {"PreCreateWindow", PyCEditView_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"SaveFile", ui_edit_window_save_file, 1}, // @pymeth SaveFile|Saves the view to a named file. + {"OnCommand", PyCEditView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {NULL, NULL} +}; + +PyCCtrlView_Type PyCEditView::type("PyCEditView", &PyCCtrlView::type, &PyCEdit::type, RUNTIME_CLASS(CEditView), sizeof(PyCEditView), ui_edit_window_methods, GET_PY_CTOR(PyCEditView)); + +///////////////////////////////////////////////////////////////////// +// +// List View object +// +////////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|CreateListView|Creates a PyCListView object. +PyObject * PyCListView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + // @pyparm |doc||The document to use with the view. + if (!PyArg_ParseTuple(args, "O:CreateListView", &doc)) + return NULL; + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + GUI_BGN_SAVE; + CListView *pView = new CPythonListView(); + ((CProtectedView *)pView)->SetDocument(pDoc); + GUI_END_SAVE; + return ui_assoc_object::make( PyCListView::type, pView ); +} + +// @pymethod tuple|PyCListView|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +PyObject * +PyCListView_pre_create_window(PyObject *self, PyObject *args) +{ + CListView *pView; + if (!(pView=GetListViewPtr(self))) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pView->CListView::PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CListView::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod |PyCListView|GetListCtrl|Returns the underlying list control object. +static PyObject * +PyCListView_get_list_ctrl(PyObject *self, PyObject *args) +{ + CListView *pView; + if (!(pView=GetListViewPtr(self))) + return NULL; + GUI_BGN_SAVE; + CListCtrl &lc = pView->GetListCtrl(); + GUI_END_SAVE; + return ui_assoc_object::make( UITypeFromCObject(&lc), &lc )->GetGoodRet(); +} + +// @pymethod |PyCListView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCListView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonListView, &PyCListView::type, self, args); +} + +/////////////////////////////////////// +// @object PyCListView|A class which implementes a CListView. Derived from and objects. +static struct PyMethodDef ui_list_view_methods[] = { + {"PreCreateWindow", PyCListView_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"GetListCtrl", PyCListView_get_list_ctrl, 1}, // @pymeth GetListCtrl|Returns the underlying list control object. + {"OnCommand", PyCListView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {NULL, NULL} +}; + +PyCCtrlView_Type PyCListView::type("PyCListView", + &PyCCtrlView::type, + &PyCListCtrl::type, + RUNTIME_CLASS(CListView), + sizeof(PyCListView), + ui_list_view_methods, + GET_PY_CTOR(PyCListView)); + +///////////////////////////////////////////////////////////////////// +// +// Tree View object +// +////////////////////////////////////////////////////////////////////// +// @pymethod |win32ui|CreateTreeView|Creates a PyCTreeView object. +PyObject * PyCTreeView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + // @pyparm |doc||The document to use with the view. + if (!PyArg_ParseTuple(args, "O:CreateTreeView", &doc)) + return NULL; + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + GUI_BGN_SAVE; + CTreeView *pView = new CPythonTreeView(); + ((CProtectedView *)pView)->SetDocument(pDoc); + GUI_END_SAVE; + return ui_assoc_object::make( PyCTreeView::type, pView ); +} +// @pymethod tuple|PyCTreeView|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +PyObject * +PyCTreeView_pre_create_window(PyObject *self, PyObject *args) +{ + CTreeView *pView; + if (!(pView=GetTreeViewPtr(self))) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pView->CTreeView::PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CTreeView::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod |PyCTreeView|GetTreeCtrl|Returns the underlying tree control object. +static PyObject * +PyCTreeView_get_tree_ctrl(PyObject *self, PyObject *args) +{ + CTreeView *pView; + if (!(pView=GetTreeViewPtr(self))) + return NULL; + GUI_BGN_SAVE; + CTreeCtrl &lc = pView->GetTreeCtrl(); + GUI_END_SAVE; + return ui_assoc_object::make( UITypeFromCObject(&lc), &lc )->GetGoodRet(); +} + +// @pymethod |PyCTreeView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCTreeView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonTreeView, &PyCTreeView::type, self, args); +} + +/////////////////////////////////////// +// @object PyCTreeView|A class which implementes a CTreeView. Derived from and objects. +static struct PyMethodDef ui_tree_view_methods[] = { + {"PreCreateWindow", PyCTreeView_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"GetTreeCtrl", PyCTreeView_get_tree_ctrl, 1}, // @pymeth GetTreeCtrl|Returns the underlying tree control object. + {"OnCommand", PyCTreeView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {NULL, NULL} +}; + +PyCCtrlView_Type PyCTreeView::type("PyCTreeView", + &PyCCtrlView::type, + &PyCTreeCtrl::type, + RUNTIME_CLASS(CTreeView), + sizeof(PyCTreeView), + ui_tree_view_methods, + GET_PY_CTOR(PyCTreeView)); + + +/////////////////////////////////////// +// +// Form View Methods +// +// inherited from view +// +/////////////////////////////////////// + +// @pymethod |win32ui|CreateFormView|Creates a form view object. +PyObject * PyCFormView::create(PyObject *self, PyObject *args) +{ + PyObject *doc; + char *szTemplate = NULL; + int iTemplate; + // @pyparm |doc||The document to use with the view. + // @pyparm int|iTemplate||The ID of the dialog control. + if (!PyArg_ParseTuple(args, "Oi:CreateFormView", &doc, &iTemplate)) { + PyErr_Clear(); + // @pyparmalt1 |doc||The document to use with the view. + // @pyparmalt1 string|template||The ID of the dialog control. + if (!PyArg_ParseTuple(args, "Os:CreateFormView", &doc, &szTemplate)) + return NULL; + } + if (!ui_base_class::is_uiobject(doc, &PyCDocument::type)) + RETURN_TYPE_ERR("Argument must be a PyCDocument"); + CDocument *pDoc = PyCDocument::GetDoc( doc ); + CFormView *pView; + GUI_BGN_SAVE; + + if (szTemplate) + pView = new CPythonFormView(szTemplate); + else + pView = new CPythonFormView(iTemplate); + ((CProtectedView *)pView)->SetDocument(pDoc); + GUI_END_SAVE; + return ui_assoc_object::make( PyCFormView::type, pView ); +} + +// @pymethod |PyCFormView|OnCommand|Calls the standard Python framework OnCommand handler +PyObject * +PyCFormView_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonFormView, &PyCFormView::type, self, args); +} + + +// @object PyCFormView|A class which implementes a CFormView (ie, a view based on a dialog resource. +static struct PyMethodDef PyCFormView_methods[] = { + {"OnCommand", PyCFormView_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {NULL, NULL} +}; + +ui_type_CObject PyCFormView::type("PyCFormView", + &PyCView::type, + RUNTIME_CLASS(CFormView), + sizeof(PyCFormView), + PyCFormView_methods, + GET_PY_CTOR(PyCFormView)); + diff --git a/Pythonwin/win32virt.cpp b/Pythonwin/win32virt.cpp new file mode 100644 index 0000000000..395f4ccd23 --- /dev/null +++ b/Pythonwin/win32virt.cpp @@ -0,0 +1,524 @@ +/* + + win32 virtuals manager/helper + + Created August 1994, Mark Hammond (MHammond@skippinet.com.au) + +*/ +#include "stdafx.h" +#include "win32win.h" +#include "win32dc.h" +#include "win32prinfo.h" +#include "win32doc.h" +////////////////////////////////////////////////////////////////////// +// +// virtuals helper +// +////////////////////////////////////////////////////////////////////// +extern BOOL DisplayPythonTraceback(PyObject *exc_type, PyObject *exc_val, PyObject *exc_tb, const char *extraTitleMsg = NULL); + +extern BOOL bInFatalShutdown; + +CVirtualHelper::CVirtualHelper(const char *iname, const void *iassoc, EnumVirtualErrorHandling veh/* = VEH_PRINT_ERROR */) +{ + handler=NULL; + py_ob = NULL; + retVal=NULL; + csHandlerName = iname; + vehErrorHandling = veh; + ui_assoc_object *py_bob = ui_assoc_object::handleMgr.GetAssocObject( iassoc ); + if (bInFatalShutdown || py_bob==NULL) + return; + if (!py_bob->is_uiobject( &ui_assoc_object::type)) { + TRACE("CVirtualHelper::CVirtualHelper Error: Call object is not of required type\n"); + return; + } + // ok - have the python data type - now see if it has an override. + if (py_bob->virtualInst) { + CEnterLeavePython _celp; + PyObject *t, *v, *tb; + PyErr_Fetch(&t,&v,&tb); + handler = PyObject_GetAttrString(py_bob->virtualInst, (char *)iname); + if (handler) { + // explicitely check a method returned, else the classes + // delegation may cause a circular call chain. + if (!PyMethod_Check(handler)) { + if (!PyCFunction_Check(handler)) { + TRACE("Handler for object is not a method!\n"); + } + DODECREF(handler); + handler = NULL; + } + } + PyErr_Restore(t,v,tb); + } + py_ob = py_bob; + Py_INCREF(py_ob); +// Py_XINCREF(handler); +} +CVirtualHelper::~CVirtualHelper() +{ + // XXX - Gross hack for speed. This is called for eachh window message + // so only grabs the Python lock if the objects need Python, + if ((retVal && retVal->ob_refcnt==1) || + (handler && handler->ob_refcnt==1) || + (py_ob && py_ob->ob_refcnt==1)) { + CEnterLeavePython _celp; + XDODECREF(retVal); + XDODECREF(handler); + XDODECREF(py_ob); + } else { + XDODECREF(retVal); + XDODECREF(handler); + XDODECREF(py_ob); + } +} +PyObject *CVirtualHelper::GetHandler() +{ + return handler; +} +BOOL CVirtualHelper::do_call(PyObject *args) +{ + CEnterLeavePython _celp; + XDODECREF(retVal); // our old one. + retVal = NULL; + ASSERT(handler); // caller must trap this. + ASSERT(args); + PyObject *result = gui_call_object(handler,args); + DODECREF(args); + if (result==NULL) { + if (vehErrorHandling==VEH_PRINT_ERROR) { + char msg[256]; + TRACE("CallVirtual : callback failed with exception\n"); + gui_print_error(); + PyObject *obRepr = PyObject_Repr(handler); + char *szRepr = PyString_AsString(obRepr); + wsprintf(msg, "%s() virtual handler (%s) raised an exception", (const char *)csHandlerName, szRepr); + Py_XDECREF(obRepr); + PyErr_SetString(ui_module_error, msg); + // send to the debugger + TRACE(msg); + TRACE("\n"); + // send to the app. + gui_print_error(); + } else { + // Error dialog. + CString csAddnMsg = " when executing "; + csAddnMsg += csHandlerName; + csAddnMsg += " handler"; + + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + DisplayPythonTraceback(type, value, traceback, csAddnMsg); + PyErr_Restore(type, value, traceback); + } + return FALSE; + } + retVal = result; + return TRUE; +} + +BOOL CVirtualHelper::call_args(PyObject *arglst) +{ + if (!handler) return FALSE; + return do_call(arglst); +} + +BOOL CVirtualHelper::call() +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("()"); + return do_call(arglst); +} +BOOL CVirtualHelper::call(int val) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(i)",val); + return do_call(arglst); +} +BOOL CVirtualHelper::call(int val, int val2) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(ii)",val, val2); + return do_call(arglst); +} +BOOL CVirtualHelper::call(int val1, int val2, int val3) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(iii)",val1, val2, val3); + return do_call(arglst); +} +BOOL CVirtualHelper::call(long val) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(l)",val); + return do_call(arglst); +} + +BOOL CVirtualHelper::call(const char *val) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(z)",val); + return do_call(arglst); +} +BOOL CVirtualHelper::call(const char *val, int ival) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("(zi)",val,ival); + return do_call(arglst); +} +BOOL CVirtualHelper::call(PyObject *ob) +{ + if (!handler) return FALSE; + if (!ob) + ob=Py_None; + PyObject *arglst = Py_BuildValue("(O)",ob); + return do_call(arglst); +} +BOOL CVirtualHelper::call(PyObject *ob, PyObject *ob2) +{ + if (!handler) return FALSE; + if (!ob) + ob=Py_None; + if (!ob2) + ob2=Py_None; + PyObject *arglst = Py_BuildValue("(OO)",ob, ob2); + return do_call(arglst); +} +BOOL CVirtualHelper::call(PyObject *ob, PyObject *ob2, int i) +{ + if (!handler) return FALSE; + if (!ob) + ob=Py_None; + if (!ob2) + ob2=Py_None; + PyObject *arglst = Py_BuildValue("(OOi)",ob, ob2, i); + return do_call(arglst); +} + +BOOL CVirtualHelper::call(CDC *pDC) +{ + if (!handler) return FALSE; + PyObject *dc = (PyObject *) ui_assoc_object::make (ui_dc_object::type, + pDC)->GetGoodRet(); + if (!dc) return FALSE; + PyObject *arglst = Py_BuildValue("(O)",dc); + BOOL ret = do_call(arglst); + DODECREF (dc); // the reference I created. + return ret; +} +BOOL CVirtualHelper::call(CDC *pDC, CPrintInfo *pInfo) +{ + if (!handler) return FALSE; + PyObject *dc = (PyObject *) ui_assoc_object::make (ui_dc_object::type, + pDC)->GetGoodRet(); + if (!dc) return FALSE; + BOOL ret; + PyObject *info = NULL; + PyObject *arglst = NULL; + if (pInfo != NULL) { + info = (PyObject *) ui_assoc_object::make (ui_prinfo_object::type, + pInfo)->GetGoodRet(); + if (!info) return FALSE; + arglst = Py_BuildValue("(OO)",dc, info); + } else { + arglst = Py_BuildValue("(Oz)",dc, NULL); + } + ret = do_call(arglst); + DODECREF (dc); // the reference I created. + if (pInfo != NULL) { + DODECREF (info); // the reference I created. + } + return ret; +} +BOOL CVirtualHelper::call(CPrintInfo *pInfo) +{ + if (!handler) return FALSE; + PyObject *info = NULL; + PyObject *arglst; + if (pInfo) { + info = (PyObject *) ui_assoc_object::make (ui_prinfo_object::type, + pInfo)->GetGoodRet(); + if (!info) return FALSE; + arglst = Py_BuildValue("(O)",info); + } else { + arglst = Py_BuildValue("(z)",NULL); + } + BOOL ret = do_call(arglst); + DODECREF (info); // the reference I created. + return ret; +} +BOOL CVirtualHelper::call(CWnd *pWnd) +{ + if (!handler) return FALSE; + PyObject *wnd = PyWinObject_FromCWnd(pWnd); + if (!wnd) return FALSE; + PyObject *arglst = Py_BuildValue("(O)",wnd); + BOOL ret = do_call(arglst); + DODECREF (wnd); // the reference I created. + return ret; +} + +BOOL CVirtualHelper::call(CWnd *pWnd, int i) +{ + if (!handler) return FALSE; + PyObject *wnd = PyWinObject_FromCWnd(pWnd); + if (!wnd) return FALSE; + PyObject *arglst = Py_BuildValue("(Oi)",wnd, i); + BOOL ret = do_call(arglst); + DODECREF (wnd); // the reference I created. + return ret; +} + +BOOL CVirtualHelper::call(CWnd *pWnd, int i, int i2) +{ + if (!handler) return FALSE; + PyObject *wnd = PyWinObject_FromCWnd(pWnd); + if (!wnd) return FALSE; + PyObject *arglst = Py_BuildValue("(Oii)",wnd, i, i2); + BOOL ret = do_call(arglst); + DODECREF (wnd); // the reference I created. + return ret; +} + +BOOL CVirtualHelper::call(CDC *pDC, CWnd *pWnd, int i) +{ + PyObject *wnd ; + if (pWnd==NULL) { + wnd = Py_None; + DOINCREF(wnd); + } else { + wnd = PyWinObject_FromCWnd(pWnd); + if (!wnd) return FALSE; + } + PyObject *dc = (PyObject *) ui_assoc_object::make (ui_dc_object::type, + pDC)->GetGoodRet(); + if (!dc) { + Py_DECREF(wnd); + return FALSE; + } + PyObject *arglst = Py_BuildValue("(OOi)",dc, wnd, i); + BOOL ret = do_call(arglst); + Py_DECREF(wnd); + Py_DECREF(dc); + return ret; + +} +BOOL CVirtualHelper::call(CView *pWnd, PyObject *ob) +{ + if (!handler) return FALSE; + if (!ob) + ob=Py_None; + PyObject *wnd ; + if (pWnd==NULL) { + wnd = Py_None; + DOINCREF(wnd); + } else { + wnd = PyWinObject_FromCWnd(pWnd); + if (!wnd) return FALSE; + } + PyObject *arglst = Py_BuildValue("(OO)",wnd,ob); + BOOL ret = do_call(arglst); + DODECREF (wnd); // the reference I created. + return ret; +} + +BOOL CVirtualHelper::call(BOOL boolVal, CWnd *pWnd1, CWnd *pWnd2) +{ + if (!handler) return FALSE; + PyObject *wnd1; + if (pWnd1) { + wnd1 = PyWinObject_FromCWnd(pWnd1); + if (!wnd1) return FALSE; + } else { + Py_INCREF(Py_None); + wnd1 = Py_None; + } + PyObject *wnd2; + if (pWnd2) { + wnd2 = PyWinObject_FromCWnd(pWnd2); + if (!wnd2) return FALSE; + } else { + Py_INCREF(Py_None); + wnd2 = Py_None; + } + PyObject *arglst = Py_BuildValue("(iOO)",boolVal, wnd1, wnd2); + BOOL ret = do_call(arglst); + DODECREF (wnd1); // the reference I created. + DODECREF (wnd2); // the reference I created. + return ret; +} + +BOOL CVirtualHelper::call(CDocument *pDoc) +{ + if (!handler) return FALSE; + PyObject *doc = (PyObject *) ui_assoc_object::make (PyCDocument::type, + pDoc)->GetGoodRet(); + if (!doc) return FALSE; + PyObject *arglst = Py_BuildValue("(O)",doc); + BOOL ret = do_call(arglst); + DODECREF (doc); // ref I created. + return ret; +} +BOOL CVirtualHelper::call(LPCREATESTRUCT lpcs, PyObject *ob) +{ + if (!handler) return FALSE; + PyObject *cs = PyObjectFromCreateStruct(lpcs); + if (!cs) return FALSE; + if (ob==NULL) ob = Py_None; + PyObject *arglst = Py_BuildValue("(O,O)", cs, ob ); + DODECREF(cs); // ref I created. + BOOL ret = do_call(arglst); + return ret; +} +BOOL CVirtualHelper::call(LPCREATESTRUCT lpcs) +{ + if (!handler) return FALSE; + PyObject *cs = PyObjectFromCreateStruct(lpcs); + if (!cs) return FALSE; + PyObject *arglst = Py_BuildValue("(O)", cs); + BOOL ret = do_call(arglst); + DODECREF(cs); // my reference. + return ret; +} + +BOOL CVirtualHelper::call(const MSG *msg) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("((iiiii(ii)))",msg->hwnd,msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); + BOOL ret = do_call(arglst); + DODECREF(arglst); // my reference. + return ret; +} + +BOOL CVirtualHelper::call(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo) +{ + if (!handler) return FALSE; + PyObject *arglst = Py_BuildValue("iill",nID, nCode, (long)pExtra, (long)pHandlerInfo); + BOOL ret = do_call(arglst); + DODECREF(arglst); // my reference. + return ret; +} + +BOOL CVirtualHelper::retnone() +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + return (retVal==Py_None); +} + +BOOL CVirtualHelper::retval( MSG *msg ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + CEnterLeavePython _celp; + if (!PyArg_ParseTuple(retVal, "(iiiii(ii))", &msg->hwnd,&msg->message,&msg->wParam,&msg->lParam,&msg->time,&msg->pt.x,&msg->pt.y)) { + PyErr_Clear(); + return FALSE; + } + return TRUE; +} +BOOL CVirtualHelper::retval( int &ret ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + if (retVal==Py_None) { + ret = 0; + return TRUE; + } + CEnterLeavePython _celp; + ret = PyInt_AsLong(retVal); + return !PyErr_Occurred(); +} +BOOL CVirtualHelper::retval( long &ret ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + if (retVal==Py_None) { + ret = 0; + return TRUE; + } + CEnterLeavePython _celp; + ret = PyInt_AsLong(retVal); + if (PyErr_Occurred()) { + PyErr_Clear(); + return FALSE; + } + return TRUE; +} + +BOOL CVirtualHelper::retval( char *&ret ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + if (retVal==Py_None) { + ret = NULL; + return TRUE; + } + CEnterLeavePython _celp; + ret = PyString_AsString(retVal); + if (PyErr_Occurred()) { + PyErr_Clear(); + return FALSE; + } + return TRUE; +} + +BOOL CVirtualHelper::retval( CString &ret ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + if (retVal==Py_None) { + ret.Empty(); + return TRUE; + } + CEnterLeavePython _celp; + ret = PyString_AsString(retVal); + if (PyErr_Occurred()) { + PyErr_Clear(); + return FALSE; + } + return TRUE; +} + +BOOL CVirtualHelper::retval( _object* &ret ) +{ + ASSERT(retVal); + if (!retVal) + return FALSE; // failed - assume didnt work in non debug + ret = retVal; +/** what was I thinking? + CEnterLeavePython _celp; + if (!PyArg_Parse(retVal, "O",&ret)) { + PyErr_Clear(); + return FALSE; + } +**/ + return TRUE; +} + +BOOL CVirtualHelper::retval( CREATESTRUCT &cs ) +{ + ASSERT(retVal); + if (!retVal || retVal==Py_None) + return FALSE; // failed - assume didnt work in non debug + CEnterLeavePython _celp; + if (!CreateStructFromPyObject(&cs, retVal)) { + char msgBuf[128]; + + gui_print_error(); + wsprintf(msgBuf, "virtual %s: The return value can not be converted from a CREATESTRUCT tuple", + (const char *)csHandlerName ); + PyErr_SetString(PyExc_TypeError, msgBuf); + gui_print_error(); + return FALSE; + } + return TRUE; +} + diff --git a/Pythonwin/win32win.cpp b/Pythonwin/win32win.cpp new file mode 100644 index 0000000000..1aa5d7c068 --- /dev/null +++ b/Pythonwin/win32win.cpp @@ -0,0 +1,4094 @@ +/* + + win32 window data type + + Created July 1994, Mark Hammond (MHammond@skippinet.com.au) + +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 "win32win.h" +#include "win32doc.h" +#include "win32dc.h" +#include "win32control.h" +#include "win32toolbar.h" +#include "win32menu.h" +#include "win32gdi.h" +#include "win32font.h" +#include "win32cmdui.h" +#include "win32rgn.h" +#include "reswin32ui.h" +#include "AFXSTAT_.H" + +static char *szErrMsgBadHandle = "The window handle does not specify a valid window"; +#define CHECK_HWND_VALID(pWnd) if (!::IsWindow((pWnd)->m_hWnd)) RETURN_ERR(szErrMsgBadHandle); + +extern BOOL bInFatalShutdown; + +IMPLEMENT_DYNAMIC(CPythonFrameWnd, CFrameWnd); +IMPLEMENT_DYNAMIC(CPythonMDIChildWnd, CMDIChildWnd); +IMPLEMENT_DYNAMIC(CPythonMDIFrameWnd, CMDIFrameWnd); + +// Beat protected members! +class WndHack : public CWnd { +public: + LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) + { return CWnd::DefWindowProc(message, wParam, lParam);} + BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) + { return CWnd::OnWndMsg(message, wParam, lParam, pResult);} + BOOL OnEraseBkgnd(CDC *pDC) + { return CWnd::OnEraseBkgnd(pDC);} + void OnPaint() + { CWnd::OnPaint();} + HCURSOR OnQueryDragIcon() + { return CWnd::OnQueryDragIcon(); } + HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) + { return CWnd::OnCtlColor(pDC, pWnd, nCtlColor); } + void OnClose() + { CWnd::OnClose(); } + UINT OnNcHitTest(CPoint point) + { return CWnd::OnNcHitTest(point);} + BOOL OnSetCursor( CWnd *pWnd, UINT ht, UINT msg) + { return CWnd::OnSetCursor(pWnd, ht, msg);} + int OnMouseActivate( CWnd *pWnd, UINT ht, UINT msg) + { return CWnd::OnMouseActivate(pWnd, ht, msg);} + BOOL PreCreateWindow(CREATESTRUCT &cs) { + return CWnd::PreCreateWindow(cs); + } + BOOL OnQueryNewPalette() \ + {return CWnd::OnQueryNewPalette();} +}; + + +BOOL Python_check_message(const MSG *msg) // TRUE if fully processed. +{ + + ui_assoc_object *pObj; + PyObject *method; + CWnd *pWnd = bInFatalShutdown ? NULL : CWnd::FromHandlePermanent(msg->hwnd); + if (pWnd && + (pObj=ui_assoc_object::GetPyObject(pWnd)) && + pObj->is_uiobject( &PyCWnd::type ) && + ((PyCWnd *)pObj)->pMessageHookList && + ((PyCWnd *)pObj)->pMessageHookList->Lookup(msg->message,(void *&)method)) { + +#ifdef TRACE_CALLBACKS + TRACE("Message callback: message %04X, object %s (hwnd %p) (%p)\n",msg->message, (const char *)GetReprText(pObj), pWnd, pWnd->GetSafeHwnd()); +#endif + // Our Python convention is TRUE means "pass it on" + CEnterLeavePython _celp; + return Python_callback(method, msg)==0; + } + return FALSE; // dont want it. +} + +BOOL Python_check_key_message(const MSG *msg) +{ + CEnterLeavePython _celp; + ui_assoc_object *pObj; + BOOL bPassOn = TRUE; + CWnd *pWnd = msg->hwnd ? CWnd::FromHandlePermanent(msg->hwnd) : NULL; + if (pWnd && (pObj=ui_assoc_object::GetPyObject(pWnd)) && + pObj->is_uiobject( &PyCWnd::type )) + bPassOn = ((PyCWnd*)pObj)->check_key_stroke(msg->wParam); + return !bPassOn; +} + +// WARNING - the return ptr may be temporary. +CWnd *GetWndPtrFromParam(PyObject *ob, ui_type_CObject &type) +{ + if (PyInt_Check(ob)) { + HWND hwnd = (HWND)PyInt_AsLong(ob); + if (!IsWindow(hwnd)) + RETURN_ERR(szErrMsgBadHandle); + CWnd *ret = CWnd::FromHandle(hwnd); + if (ret==NULL) + RETURN_ERR("The handle could not be converted to a window (CWnd::FromHandle() failed!)"); + return ret; + } else if (ui_base_class::is_uiobject(ob, &type)) { + return (CWnd *)PyCWnd::GetPythonGenericWnd(ob, &type); + } else { + char buf[128]; + wsprintf(buf,"Argument must be a %s object, or integer containing a HWND", type.tp_name); + RETURN_ERR(buf); + } +} + +PyObject *PyWinObject_FromCWnd(CWnd *pWnd) +{ + return (PyObject *) ui_assoc_object::make (UITypeFromCObject(pWnd), + pWnd)->GetGoodRet(); + +} +CWnd *GetWndPtr(PyObject *self) +{ + return (CWnd *)PyCWnd::GetPythonGenericWnd(self); +} +CWnd *GetWndPtrGoodHWnd(PyObject *self) +{ + CWnd *ret = PyCWnd::GetPythonGenericWnd(self); + if (ret /*&& ret->m_hWnd*/ && !::IsWindow(ret->m_hWnd)) { + RETURN_ERR(szErrMsgBadHandle); + } + return ret; +} + +CFrameWnd *GetFramePtr(PyObject *self) +{ + return (CFrameWnd *)PyCWnd::GetPythonGenericWnd(self, &PyCFrameWnd::type); +} +CPythonMDIChildWnd *GetPythonFrame(PyObject *self) +{ + return (CPythonMDIChildWnd *)PyCWnd::GetPythonGenericWnd(self, &PyCFrameWnd::type); +} +CMDIFrameWnd *GetMDIFrame(PyObject *self) +{ + return (CMDIFrameWnd *)PyCWnd::GetPythonGenericWnd(self, &PyCMDIFrameWnd::type); +} + +// @pymethod |PyCWnd|SCROLLINFO tuple|Describes a SCROLLINFO tuple +// @pyparm int|addnMask||Additional mask information. Python automatically fills the mask for valid items, so currently the only valid values are zero, and win32con.SIF_DISABLENOSCROLL. +// @pyparm int|min||The minimum scrolling position. Both min and max, or neither, must be provided. +// @pyparm int|max||The maximum scrolling position. Both min and max, or neither, must be provided. +// @pyparm int|page||Specifies the page size. A scroll bar uses this value to determine the appropriate size of the proportional scroll box. +// @pyparm int|pos||Specifies the position of the scroll box. +// @pyparm int|trackPos||Specifies the immediate position of a scroll box that the user +// is dragging. An application can retrieve this value while processing +// the SB_THUMBTRACK notification message. An application cannot set +// the immediate scroll position; the function ignores +// this member. +// @comm When passed to Python, will always be a tuple of size 6, and items may be None if not available. +// @comm When passed from Python, it must have the addn mask attribute, but all other items may be None, or not exist. +// userob is any Python object at all, but no reference count is kept, so you must ensure the object remains referenced throught the lists life. +BOOL ParseSCROLLINFOTuple( PyObject *args, SCROLLINFO *pInfo) +{ + PyObject *ob; + int len = PyTuple_Size(args); + if (len<1 || len > 5) { + PyErr_SetString(PyExc_TypeError, "SCROLLINFO tuple has invalid size"); + return FALSE; + } + PyErr_Clear(); // clear any errors, so I can detect my own. + // 0 - mask. + if ((ob=PyTuple_GetItem(args, 0))==NULL) + return FALSE; + pInfo->fMask = (UINT)PyInt_AsLong(ob); + // 1/2 - nMin/nMax + if (len==2) { + PyErr_SetString(PyExc_TypeError, "SCROLLINFO - Both min and max, or neither, must be provided."); + return FALSE; + } + if (len<3) return TRUE; + if ((ob=PyTuple_GetItem(args, 1))==NULL) + return FALSE; + if (ob != Py_None) { + pInfo->fMask |= SIF_RANGE; + pInfo->nMin = PyInt_AsLong(ob); + if ((ob=PyTuple_GetItem(args, 2))==NULL) + return FALSE; + pInfo->nMax = PyInt_AsLong(ob); + } + // 3 == nPage. + if (len<4) return TRUE; + if ((ob=PyTuple_GetItem(args, 3))==NULL) + return FALSE; + if (ob != Py_None) { + pInfo->fMask |=SIF_PAGE; + pInfo->nPage = PyInt_AsLong(ob); + } + // 4 == nPos + if (len<5) return TRUE; + if ((ob=PyTuple_GetItem(args, 4))==NULL) + return FALSE; + if (ob != Py_None) { + pInfo->fMask |=SIF_POS; + pInfo->nPos = PyInt_AsLong(ob); + } + // 5 == trackpos + if (len<6) return TRUE; + if ((ob=PyTuple_GetItem(args, 5))==NULL) + return FALSE; + if (ob != Py_None) { + pInfo->nTrackPos = PyInt_AsLong(ob); + } + return TRUE; +} + +PyObject *MakeSCROLLINFOTuple(SCROLLINFO *pInfo) +{ + PyObject *ret = PyTuple_New(6); + if (ret==NULL) return NULL; + PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(0)); + if (pInfo->fMask & SIF_RANGE) { + PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(pInfo->nMin)); + PyTuple_SET_ITEM(ret, 2, PyInt_FromLong(pInfo->nMax)); + } else { + Py_INCREF(Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 1, Py_None); + PyTuple_SET_ITEM(ret, 2, Py_None); + } + if (pInfo->fMask & SIF_PAGE) { + PyTuple_SET_ITEM(ret, 3, PyInt_FromLong(pInfo->nPage)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 3, Py_None); + } + if (pInfo->fMask & SIF_POS) { + PyTuple_SET_ITEM(ret, 4, PyInt_FromLong(pInfo->nPos)); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(ret, 4, Py_None); + } + PyTuple_SET_ITEM(ret, 5, PyInt_FromLong(pInfo->nTrackPos)); + return ret; +} + +///////////////////////////////////////////////////////////////////// +// +// win32ui methods that deal with windows. +// +////////////////////////////////////////////////////////////////////// + +// @pymethod |win32ui|CreateWnd|Creates an unitialized +PyObject * +ui_window_create(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = new CPythonWndFramework< CWnd >(); + return ui_assoc_object::make( PyCWnd::type, pWnd ); +} +// @pymethod |PyCWnd|CreateWindow|Creates the actual window +PyObject * +ui_window_create_window(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + const char *szClass, *szWndName; + CCreateContext *pCCPass = NULL; + PythonCreateContext cc; + PyObject *contextObject = Py_None; + if (!PyArg_ParseTuple(args, "zzi(iiii)Oi|O:CreateWindow", + &szClass, // @pyparm string|classId||The class ID for the window, or None + &szWndName, // @pyparm string|windowName||The title for the window, or None + &style, // @pyparm int|style||The style for the window. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the window. + &obParent, // @pyparm |parent||The parent window of the new window.. + &id, // @pyparm int|id||The control's ID. + &contextObject)) // @pyparm object|context||A CreateContext object. + return NULL; + + CWnd *pParent; + if (obParent==Py_None) + pParent = NULL; + else if (ui_base_class::is_uiobject(obParent, &PyCWnd::type)) { + pParent = GetWndPtr( obParent ); + if (pParent==NULL) + return NULL; + } else + RETURN_TYPE_ERR("parent argument must be a window object, or None"); + if (contextObject != Py_None) { + cc.SetPythonObject(contextObject); + pCCPass = &cc; + } + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + // @pyseemfc CWnd|Create + ok = pWnd->Create(szClass, szWndName, style, rect, pParent, id, pCCPass); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CWnd::Create"); + RETURN_NONE; +} + +// @pymethod |PyCWnd|CreateWindowEx|Creates the actual window using extended capabilities. +PyObject * +ui_window_create_window_ex(PyObject *self, PyObject *args) +{ + int style, id; + PyObject *obParent; + RECT rect; + const char *szClass, *szWndName; + DWORD dwStyleEx; + PyObject *csObject = Py_None; + if (!PyArg_ParseTuple(args, "iszi(iiii)Oi|O:CreateWindowEx", + &dwStyleEx, // @pyparm int|styleEx||The extended style of the window being created. + &szClass, // @pyparm string|classId||The class ID for the window. May not be None. + &szWndName, // @pyparm string|windowName||The title for the window, or None + &style, // @pyparm int|style||The style for the window. + &rect.left,&rect.top,&rect.right,&rect.bottom, + // @pyparm (left, top, right, bottom)|rect||The size and position of the window. + &obParent, // @pyparm |parent||The parent window of the new window.. + &id, // @pyparm int|id||The control's ID. + &csObject)) // @pyparm |createStruct||A CreateStruct object (ie, a tuple) + return NULL; + + CREATESTRUCT cs; + CREATESTRUCT *pcs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (csObject==Py_None) + pcs = NULL; + else { + if (!CreateStructFromPyObject( &cs, csObject, "CreateEx", FALSE)) + return NULL; + pcs = &cs; + } + + CWnd *pParent; + if (obParent==Py_None) + pParent = NULL; + else if (ui_base_class::is_uiobject(obParent, &PyCWnd::type)) + pParent = GetWndPtr( obParent ); + else + RETURN_TYPE_ERR("parent argument must be a window object, or None"); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + + BOOL ok; + GUI_BGN_SAVE; + // @pyseemfc CWnd|CreateEx + ok = pWnd->CreateEx(dwStyleEx, szClass, szWndName, style, rect, pParent, id, pcs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CWnd::CreateEx"); + RETURN_NONE; +} + +// @pymethod |win32ui|CreateWindowFromHandle|Creates a from an integer containing a HWND +PyObject * +PyCWnd::CreateWindowFromHandle(PyObject *self, PyObject *args) +{ + int hwnd; + if (!PyArg_ParseTuple(args, "i:CreateWindowFromHandle", + &hwnd)) // @pyparm int|hwnd||The window handle. + return NULL; + CWnd *pWnd = CWnd::FromHandle((HWND)hwnd); + if (pWnd==NULL) + RETURN_ERR("The window handle is invalid."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod |win32ui|CreateControl|Creates an OLE control. +PyObject * +PyCWnd::CreateControl(PyObject *self, PyObject *args) +{ + USES_CONVERSION; + PyObject *parent = Py_None; + int id; + int style; + CRect rect(0,0,0,0); + PyObject *obPersist = Py_None; + int bStorage = FALSE; + const char *szClass, *szWndName, *szLicKey = NULL; + if (!PyArg_ParseTuple(args, "szi(iiii)Oi|Oiz", + &szClass, // @pyparm string|classId||The class ID for the window. + &szWndName, // @pyparm string|windowName||The title for the window. + &style, // @pyparm int|style||The style for the control. + // @pyparm (left, top, right, bottom)|rect||The default position of the window. + &rect.left, &rect.top, &rect.right, &rect.bottom, + &parent, // @pyparm |parent||The parent window + &id, // @pyparm int|id||The child ID for the view + &obPersist, // @pyparm object|obPersist|None|Place holder for future support. + &bStorage, // @pyparm int|bStorage|FALSE|Not used. + &szLicKey ))// @pyparm string|licKey|None|The licence key for the control. + return NULL; + + CLSID clsid; + HRESULT hr = AfxGetClassIDFromString(szClass, &clsid); + if (FAILED(hr)) + RETURN_ERR("The CLSID is invalid"); + + CWnd *pWnd = new CWnd; + if (!ui_base_class::is_uiobject(parent, &PyCWnd::type)) + RETURN_TYPE_ERR("Argument must be a PyCWnd"); + CWnd *pWndParent = GetWndPtr( parent ); + if (pWnd==NULL || pWndParent==NULL) + return NULL; + // This will cause MFC to die after dumping a message to the debugget! + if (afxOccManager == NULL) + RETURN_ERR("win32ui.EnableControlContainer() has not been called yet."); + BOOL ok; + GUI_BGN_SAVE; + ok = pWnd->CreateControl(clsid, szWndName, style, rect, pWndParent, id, NULL, bStorage, T2OLE(szLicKey)); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CreateControl failed"); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + + +// @pymethod |win32ui|GetActiveWindow|Retrieves the active window. +PyObject * +PyCWnd::GetActiveWindow(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, GetActiveWindow); + GUI_BGN_SAVE; + CWnd *pWnd = CWnd::GetActiveWindow(); + GUI_END_SAVE; + if (pWnd==NULL) + RETURN_ERR("No window is active."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod |win32ui|GetForegroundWindow|Retrieves the foreground window. +PyObject * +PyCWnd::GetForegroundWindow(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, GetForegroundWindow); + GUI_BGN_SAVE; + CWnd *pWnd = CWnd::GetForegroundWindow(); + GUI_END_SAVE; + if (pWnd==NULL) + RETURN_ERR("No window is is in the foreground."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod |win32ui|GetFocus|Retrieves the window with the focus. +PyObject * +PyCWnd::GetFocus(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, GetFocus); + GUI_BGN_SAVE; + CWnd *pWnd = CWnd::GetFocus(); + GUI_END_SAVE; + if (pWnd==NULL) + RETURN_ERR("No window has focus."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod |win32ui|FindWindow|Searches for the specified top-level window +PyObject * +PyCWnd::FindWindow(PyObject *self, PyObject *args) +{ + char *szClassName; + char *szWndName; + if (!PyArg_ParseTuple(args, "zz:FindWindow", + &szClassName, // @pyparm string|className||The window class name to find, else None + &szWndName)) // @pyparm string|windowName||The window name (ie, title) to find, else None + return NULL; + GUI_BGN_SAVE; + CWnd *pWnd = CWnd::FindWindow( szClassName, szWndName ); + GUI_END_SAVE; + if (pWnd==NULL) + RETURN_ERR("No window can be found."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod |win32ui|FindWindowEx|Searches for the specified top-level or child window +PyObject * +PyCWnd::FindWindowEx(PyObject *self, PyObject *args) +{ + char *szClassName; + char *szWndName; + PyObject *obParent; + PyObject *obChildAfter; + if (!PyArg_ParseTuple(args, "OOzz:FindWindowEx", + &obParent, // @pyparm |parentWindow||The parent whose children will be searched. If None, the desktops window will be used. + &obChildAfter, // @pyparm |childAfter||The search begins with the next window in the Z order. If None, all children are searched. + &szClassName, // @pyparm string|className||The window class name to find, else None + &szWndName)) // @pyparm string|windowName||The window name (ie, title) to find, else None + return NULL; + CWnd *pParent = NULL; + if (obParent != Py_None) + if ((pParent=GetWndPtrFromParam(obParent, PyCWnd::type))==NULL) + return NULL; + CWnd *pChildAfter = NULL; + if (obChildAfter != Py_None) + if ((pChildAfter=GetWndPtrFromParam(obChildAfter, PyCWnd::type))==NULL) + return NULL; + GUI_BGN_SAVE; + HWND hwnd = ::FindWindowEx(pParent->GetSafeHwnd(), pChildAfter->GetSafeHwnd(), + szClassName, szWndName); + GUI_END_SAVE; + if (hwnd==NULL) + RETURN_ERR("No window can be found."); + return PyCWnd::make( PyCWnd::type, NULL, hwnd)->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +///////////////////////////////////////////////////////////////////// +// +// Window object +// +////////////////////////////////////////////////////////////////////// +PyCWnd::PyCWnd() +{ + pMessageHookList=NULL; + pKeyHookList = NULL; + obKeyStrokeHandler = NULL; + bDidSubclass = FALSE; +} +PyCWnd::~PyCWnd() +{ + DoKillAssoc(TRUE); +} + +BOOL PyCWnd::check_key_stroke(UINT ch) +{ + PyObject *pythonObject; + BOOL bCallBase = TRUE; + if (obKeyStrokeHandler!= NULL) + bCallBase = Python_callback(obKeyStrokeHandler, (int)ch); + + if (bCallBase && pKeyHookList && pKeyHookList->Lookup((WORD)ch, (void *&)pythonObject)) + bCallBase = Python_callback(pythonObject,(int)ch); + return bCallBase; +} + +CWnd *PyCWnd::GetPythonGenericWnd(PyObject *self, ui_type_CObject *pType) +{ + // Damn it - only pass PyCWnd::type so the RTTI check wont fail + // for builtin controls. + return (CWnd *)GetGoodCppObject( self, &type ); +} + +void PyCWnd::DoKillAssoc( BOOL bDestructing /*= FALSE*/ ) +{ + free_hook_list(this,&pMessageHookList); + free_hook_list(this,&pKeyHookList); + Py_XDECREF(obKeyStrokeHandler); + obKeyStrokeHandler = NULL; + PyCCmdTarget::DoKillAssoc(bDestructing); + if (bManualDelete || bDidSubclass) { + CWnd *pWnd = GetWndPtr(this); // get pointer before killing it. + if (bDidSubclass) { +// pWnd->Detach(); + pWnd->UnsubclassWindow(); + bDidSubclass = FALSE; + } +// DONT detach - bDidSubclass is only logic needed. +// if (pWnd->GetSafeHwnd()) { +// TRACE("Warning - DoKillAssoc detaching from existing window\n"); +// pWnd->Detach(); +// } + if (bManualDelete) { + // Release the lock while we destroy the object. + GUI_BGN_SAVE; + delete pWnd; + GUI_END_SAVE; + bManualDelete = FALSE; + } + } +} +/*static*/ PyCWnd *PyCWnd::make( ui_type_CObject &makeType, CWnd *pSearch, HWND wnd /*=NULL*/ ) +{ + BOOL bManualDelete = FALSE; + BOOL bDidSubclass = FALSE; + ASSERT(pSearch || wnd); + if (pSearch) + wnd = pSearch->GetSafeHwnd(); + // must have a permanent object for this window. + BOOL bMadeNew = FALSE; + pSearch = CWnd::FromHandlePermanent(wnd); + // FromHandlePerm is thread specific! + if (pSearch==NULL && GetWindowLong(wnd, GWL_WNDPROC)==(LONG)AfxGetAfxWndProc()) { + +/******* + Windows are per thread. This gross hack lets me get windows across + threads, but there must be a good reason for the restriction, and this + hack only works with the main thread, rather than any thread. + + #include "D:\Program Files\DevStudio\VC\mfc\src\WINHAND_.H" + extern AFX_MODULE_THREAD_STATE * PyWin_MainModuleThreadState; + + // Lets see if it is in the main thread state + if (PyWin_MainModuleThreadState->m_pmapHWND && + (AfxGetModuleThreadState() != PyWin_MainModuleThreadState)) { + // Gross hack - look it up in the internal map structure. + pSearch = (CWnd*)(PyWin_MainModuleThreadState->m_pmapHWND->LookupPermanent(wnd)); + ASSERT(pSearch == NULL || pSearch->m_hWnd == wnd); + } + + if (pSearch==NULL) +*****/ + RETURN_ERR("The window was created in a different thread and can not be mapped."); + } + if (pSearch==NULL) { + if (!IsWindow(wnd)) + RETURN_ERR("The window can not be created as it has an invalid handle"); + CWnd *pWnd; + ASSERT(makeType.pCObjectClass); // we want to know the exact type to create! + if (makeType.pCObjectClass && makeType.pCObjectClass->m_pfnCreateObject) { + pWnd = (CWnd *)makeType.pCObjectClass->CreateObject(); + if (pWnd==NULL) { + PyErr_SetString(PyExc_MemoryError, "Cant create the window object"); + return NULL; + } + ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CWnd))); // Must be a window object we just created! + } else + pWnd = new CWnd(); // this will except, rather than return NULL! + +// pWnd->Attach(wnd); + pWnd->SubclassWindow(wnd); + pSearch = pWnd; // this is now the object we use, and will get in the future from GetWindow() + bManualDelete = bDidSubclass = bMadeNew = TRUE; + } + PyCWnd *obj = (PyCWnd *)ui_assoc_object::make( makeType, pSearch ); + if (obj && bMadeNew) { + obj->bManualDelete = bManualDelete; + obj->bDidSubclass = bDidSubclass; + } + return obj; +} +/////////////////////////////////// +// Python methods +// + +// @pymethod |PyCWnd|ActivateFrame|Searches upwards for a parent window which has +// a frame, and activates it. +static PyObject * +ui_window_activate_frame(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int cmdShow = SW_SHOW; + if (!PyArg_ParseTuple(args,"|i:ActivateFrame", &cmdShow)) // @pyparm int|cmdShow|SW_SHOW| + // The param passed to . See also . + return NULL; + while (pWnd && !pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) + pWnd = pWnd->GetParent(); + if (!pWnd) + RETURN_ERR("The specified window does not have a parent frame window"); + GUI_BGN_SAVE; + ((CFrameWnd *)pWnd)->ActivateFrame(cmdShow); // @pyseemfc CFrameWnd|ActivateFrame + + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|BringWindowToTop|Brings the window to the top of a stack of overlapping windows. +static PyObject * +ui_window_bring_window_to_top(PyObject *self, PyObject *args) +{ + // @comm This method activates pop-up, top-level, and MDI child windows. + // The BringWindowToTop member function should be used to uncover any window that is partially or + // completely obscured by any overlapping windows. + // Calling this method is similar to calling the method to + // change a window's position in the Z order. The BringWindowToTop method + // does not change the window style to make it a top-level window of the desktop. + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->BringWindowToTop(); // @pyseemfc CWnd|BringWindowToTop + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod (left, top, right, bottom)|PyCWnd|CalcWindowRect| +// Computes the size of the window rectangle based on the desired client +// rectangle size. The resulting size can then be used as the initial +// size for the window object. +static PyObject * +ui_window_calc_window_rect(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CRect rect; + UINT nAdjustType = CWnd::adjustBorder; + // @pyparm (left, top, right, bottom)|rect||The size to calculate from + // @pyparm int|nAdjustType|adjustBorder|An enumerated type used for in-place editing. It can have the following values: CWnd::adjustBorder = 0, which + // means that scrollbar sizes are ignored in calculation; and CWnd::adjustOutside = 1, which means that they are added into the final + // measurements of the rectangle. + if (!PyArg_ParseTuple(args,"(iiii)|i:CalcWindowRect", &rect.left, &rect.top, &rect.right, &rect.bottom, &nAdjustType)) + return NULL; + GUI_BGN_SAVE; + pWnd->CalcWindowRect( &rect, nAdjustType ); // @pyseemfc CWnd|CalcWindowRect + GUI_END_SAVE; + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); +} +// @pymethod |PyCWnd|CheckRadioButton|Selects the specified radio button, and clears +// all others in the group. +static PyObject * +ui_window_check_radio_button(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int idFirst, idLast, idCheck; + if (!PyArg_ParseTuple(args,"iii:CheckRadioButton", + &idFirst, // @pyparm int|idFirst||The identifier of the first radio button in the group. + &idLast, // @pyparm int|idLast||The identifier of the last radio button in the group. + &idCheck))// @pyparm int|idCheck||The identifier of the radio button to be checked. + return NULL; + GUI_BGN_SAVE; + pWnd->CheckRadioButton(idFirst, idLast, idCheck); // @pyseemfc CWnd|CheckRadioButton + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod PyCWnd|PyCWnd|ChildWindowFromPoint|Returns the child window that contains the point +static PyObject * +ui_child_window_from_point(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CPoint pnt; + int flag=0; + if (!PyArg_ParseTuple(args,"(ii)|i:ChildWindowFromPoint", + &pnt.x, // @pyparm int|x||x coordinate of point + &pnt.y, // @pyparm int|y||y coordinate of point + &flag))// @pyparm int|flag|0|Specifies which child windows to skip + return NULL; + GUI_BGN_SAVE; + CWnd *pChildWnd = pWnd->ChildWindowFromPoint(pnt, flag); // @pyseemfc CWnd|ChildWindowFromPoint + GUI_END_SAVE; + return PyCWnd::make( UITypeFromCObject(pChildWnd), pChildWnd)->GetGoodRet(); +} + +// @pymethod int|PyCWnd|DefWindowProc|Calls the default message handler. +static PyObject * +ui_window_def_window_proc(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int message, wparam, lparam; + if (!PyArg_ParseTuple(args,"iii:DefWindowProc", + &message, // @pyparm int|message||The Windows message. + &wparam, // @pyparm int|idLast||The lParam for the message. + &lparam))// @pyparm int|idCheck||The wParam for the message. + return NULL; + GUI_BGN_SAVE; + long rc = ((WndHack *)pWnd)->DefWindowProc(message, (WPARAM)wparam, (LPARAM)lparam); + GUI_END_SAVE; + return Py_BuildValue("l", rc); // @pyseemfc CWnd|DefWindowProc +} + +// @pymethod |PyCWnd|DlgDirList|Fill a list box with a file or directory listing. +static PyObject * +ui_window_dlg_dir_list(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + char *defPath; + int nIDListBox, nIDStaticPath, nFileType; + if (!PyArg_ParseTuple(args,"siii:DlgDirList", + &defPath, // @pyparm string|defPath||The file spec to fill the list box with + &nIDListBox, // @pyparm int|idListbox||The Id of the listbox control to fill. + &nIDStaticPath, // @pyparm int|idStaticPath||The Id of the static control used to display the current drive and directory. If idStaticPath is 0, it is assumed that no such control exists. + &nFileType)) // @pyparm int|fileType||Specifies the attributes of the files to be displayed. + // It can be any combination of DDL_READWRITE, DDL_READONLY, DDL_HIDDEN, DDL_SYSTEM, DDL_DIRECTORY, DDL_ARCHIVE, DDL_POSTMSGS, DDL_DRIVES or DDL_EXCLUSIVE + return NULL; + char pathBuf[MAX_PATH+1]; + strncpy(pathBuf, defPath, MAX_PATH); + pathBuf[MAX_PATH] = '\0'; + int rc; + GUI_BGN_SAVE; + // @pyseemfc CWnd|DlgDirList + rc = pWnd->DlgDirList( pathBuf, nIDListBox, nIDStaticPath, nFileType); + GUI_END_SAVE; + if (!rc) + RETURN_ERR("DlgDirList failed"); + RETURN_NONE; +} +// @pymethod |PyCWnd|DlgDirListComboBox|Fill a combo with a file or directory listing. See for details. +static PyObject * +ui_window_dlg_dir_list_combo(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + char *defPath; + int nIDListBox, nIDStaticPath, nFileType; + if (!PyArg_ParseTuple(args,"siii:DlgDirListComboBox", &defPath, &nIDListBox, &nIDStaticPath, &nFileType)) + return NULL; + char pathBuf[MAX_PATH+1]; + strncpy(pathBuf, defPath, MAX_PATH); + pathBuf[MAX_PATH] = '\0'; + int rc; + GUI_BGN_SAVE; + rc = pWnd->DlgDirListComboBox( pathBuf, nIDListBox, nIDStaticPath, nFileType); + // @pyseemfc CWnd|DlgDirListComboBox + + GUI_END_SAVE; + if (!rc) + RETURN_ERR("DlgDirListComboBox failed"); + RETURN_NONE; +} +// @pymethod string|PyCWnd|DlgDirSelect| +// Retrieves the current selection from a list box. It assumes that the list box has been filled by the member function and that the selection is a drive letter, a file, or a directory name. +static PyObject * +ui_window_dlg_dir_select(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int nIDListBox; + // @pyparm int|idListbox||The Id of the listbox. + if (!PyArg_ParseTuple(args,"i:DlgDirSelect", &nIDListBox)) + return NULL; + int rc; + char buf[MAX_PATH]; + GUI_BGN_SAVE; + rc = pWnd->DlgDirSelect( buf, nIDListBox); + // @pyseemfc CWnd|DlgDirSelect + GUI_END_SAVE; + if (!rc) + RETURN_ERR("DlgDirSelect failed"); + return Py_BuildValue("s", buf); +} +// @pymethod string|PyCWnd|DlgDirSelectComboBox| +// Retrieves the current selection from the list box of a combo box. It assumes that the list box has been filled by the member function and that the selection is a drive letter, a file, or a directory name. +static PyObject * +ui_window_dlg_dir_select_combo(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int nIDListBox; + // @pyparm int|idListbox||The Id of the combobox. + if (!PyArg_ParseTuple(args,"i:DlgDirSelectComboBox", &nIDListBox)) + return NULL; + int rc; + char buf[MAX_PATH]; + GUI_BGN_SAVE; + // @pyseemfc CWnd|DlgDirSelectComboBox + rc = pWnd->DlgDirSelectComboBox( buf, nIDListBox); + GUI_END_SAVE; + if (!rc) + RETURN_ERR("DlgDirSelectComboBox failed"); + return Py_BuildValue("s", buf); +} + +// @pymethod |PyCWnd|DragAcceptFiles|Indicates that the window and children supports files dropped from file manager +static PyObject * +ui_window_drag_accept_files(PyObject *self, PyObject *args) +{ + BOOL accept = TRUE; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyparm int|bAccept|1|A flag indicating if files are accepted. + if (!PyArg_ParseTuple(args,"|i:DragAcceptFiles", &accept)) + return NULL; + GUI_BGN_SAVE; + pWnd->DragAcceptFiles(accept); + // @pyseemfc CWnd|DragAcceptFiles + + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCWnd|DestroyWindow|Destroy the window attached to the object. +static PyObject * +ui_window_destroy_window(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL rc; + GUI_BGN_SAVE; + rc=pWnd->DestroyWindow(); + GUI_END_SAVE; + if (!rc) + RETURN_ERR("DestroyWindow could not destroy the window"); + RETURN_NONE; + // @comm The DestroyWindow member function sends appropriate messages + // to the window to deactivate it and remove the input focus. + // It also destroys the window's menu, flushes the application queue, + // destroys outstanding timers, removes Clipboard ownership, and breaks the + // Clipboard-viewer chain if CWnd is at the top of the viewer chain. + // It sends WM_DESTROY and WM_NCDESTROY messages to the window. +} + +// @pymethod |PyCWnd|DrawMenuBar|Redraws the menu bar. Can be called if the menu changes. +static PyObject * +ui_window_draw_menu_bar(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->DrawMenuBar(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCWnd|EnableWindow|Enables or disables the window. Typically used for dialog controls. +static PyObject * +ui_window_enable_window(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL bEnable = TRUE; + // @pyparm int|bEnable|1|A flag indicating if the window is to be enabled or disabled. + if (!PyArg_ParseTuple(args,"|i:EnableWindow", &bEnable)) + return NULL; + int rc; + GUI_BGN_SAVE; + rc = pWnd->EnableWindow(bEnable); + // @pyseemfc CWnd|EnableWindow + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCWnd|GetCheckedRadioButton|Returns the ID of the checked radio button, or 0 if none is selected. +static PyObject * +ui_window_get_checked_rb(PyObject *self, PyObject *args) +{ + int idFirst, idLast; + if (!PyArg_ParseTuple(args, "ii:GetCheckedRadioButton", + &idFirst, // @pyparm int|idFirst||The Id of the first radio button in the group. + &idLast ))// @pyparm int|idLast||The Id of the last radio button in the group. + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetCheckedRadioButton + + GUI_BGN_SAVE; + int rc = pWnd->GetCheckedRadioButton(idFirst, idLast); + GUI_END_SAVE; + return Py_BuildValue( "i", rc); +} +// @pymethod (left, top, right, bottom)|PyCWnd|GetClientRect|Returns the client coordinates of the window. left and top will be zero. +static PyObject * +ui_window_get_client_rect(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + CRect rect; + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->GetClientRect( &rect ); + GUI_END_SAVE; + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); +} + +// @pymethod |PyCWnd|SetDlgItemText|Sets the text for the child window or control with the specified ID. +static PyObject * +ui_window_set_dlg_item_text(PyObject *self, PyObject *args) +{ + int id; + char *szText; + // @pyparm int|idControl||The Id of the control + // @pyparm string|text||The new text + if (!PyArg_ParseTuple(args, "is:SetDlgItemText", &id, &szText )) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->SetDlgItemText(id, szText); + GUI_END_SAVE; + RETURN_NONE; + // @pyseemfc CWnd|SetDlgItemText +} + +// @pymethod |PyCWnd|GetDlgItem|Returns a window object for the child window or control with the specified ID. +// The type of the return object will be as specific as possible, but will always +// be derived from an object. +static PyObject * +ui_window_get_dlg_item(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i:GetDlgItem", &id )) // @pyparm int|idControl||The Id of the control to be retrieved. + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + CWnd *pChild = pWnd->GetDlgItem(id); + GUI_END_SAVE; + // @pyseemfc CWnd|GetDlgItem + + if (!pChild) + RETURN_ERR("No dialog control with that ID"); + return PyCWnd::make( UITypeFromCObject(pChild), pChild )->GetGoodRet(); + // @rdesc The result is a (or derived) object, or a win32ui.error exception is raised. +} + +// @pymethod string|PyCWnd|GetDlgItemText|Returns the text of child window or control with the specified ID. +static PyObject * +ui_window_get_dlg_item_text(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i:GetDlgItemText", &id )) // @pyparm int|idControl||The Id of the control to be retrieved. + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CString csRet; + GUI_BGN_SAVE; + pWnd->GetDlgItemText(id, csRet); + GUI_END_SAVE; + return PyString_FromString((char *)(const char *)csRet); + // @pyseemfc CWnd|GetDlgItemText +} + +// @pymethod int|PyCWnd|GetDlgItemInt|Returns the integer value of a child window or control with the specified ID. +static PyObject * +ui_window_get_dlg_item_int(PyObject *self, PyObject *args) +{ + + int id; + BOOL bUnsigned = TRUE; + // @pyparm int|idControl||The Id of the control to be retrieved. + // @pyparm int|bUnsigned|1|Should the function check for a minus sign + if (!PyArg_ParseTuple(args, "i|i:GetDlgItemInt", &id, &bUnsigned )) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL bWorked; + GUI_BGN_SAVE; + int res = (int)pWnd->GetDlgItemInt(id, &bWorked, bUnsigned); + GUI_END_SAVE; + if (!bWorked) + RETURN_VALUE_ERR("The dialog item could not be converted to an integer"); + // @rdesc If the value can not be converted, a ValueError is raised. + return PyInt_FromLong(res); + // @pyseemfc CWnd|GetDlgItemInt +} + + +// @pymethod int|PyCWnd|GetDlgCtrlID|Returns the ID of this child window. +static PyObject * +ui_window_get_dlg_ctrl_id(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetDlgCtrlId + GUI_BGN_SAVE; + int rc = pWnd->GetDlgCtrlID(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod |PyCWnd|GetMenu|Returns the menu object for the window's menu. +static PyObject * +ui_window_get_menu(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetMenu + GUI_BGN_SAVE; + HMENU hMenu = ::GetMenu(pWnd->m_hWnd); + GUI_END_SAVE; + if (hMenu==NULL) + RETURN_ERR("The window has no menu"); + return ui_assoc_object::make(PyCMenu::type, hMenu)->GetGoodRet(); + // @rdesc The result is a object, or an exception is thrown. +} +// @pymethod |PyCWnd|GetParent|Returns the window's parent. +static PyObject * +ui_window_get_parent(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetParent + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetParent(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetParentFrame|Returns the window's frame. +static PyObject * +ui_window_get_parent_frame(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetParentFrame + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetParentFrame(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetParentOwner|Returns the child window's parent window or owner window. +static PyObject * +ui_window_get_parent_owner(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetParentOwner + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetParentOwner(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetLastActivePopup|Returns the last active popup Window, or the Window itself. +static PyObject * +ui_window_get_last_active_popup(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetLastActivePopup + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetLastActivePopup(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetTopLevelParent|Returns the top-level parent of the window. +static PyObject * +ui_window_get_top_level_parent(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetTopLevelParent + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetTopLevelParent(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetTopLevelFrame|Returns the top-level frame of the window. +static PyObject * +ui_window_get_top_level_frame(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetTopLevelFrame + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetTopLevelFrame(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod |PyCWnd|GetTopLevelOwner|Returns the top-level owner of the window. +static PyObject * +ui_window_get_top_level_owner(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetTopLevelOwner + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + CWnd *pParent = pWnd->GetTopLevelOwner(); + GUI_END_SAVE; + if (!pParent) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pParent), pParent )->GetGoodRet(); + // @rdesc The result is a object, or None if no Window can be found. +} + +// @pymethod int|PyCWnd|GetSafeHwnd|Returns the HWnd of this window. +static PyObject * +ui_window_get_safe_hwnd(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetSafeHwnd + GUI_BGN_SAVE; + HWND hwnd = pWnd->GetSafeHwnd(); + GUI_END_SAVE; + return Py_BuildValue("l", (long)hwnd); +} + +// @pymethod int|PyCWnd|GetScrollInfo|Returns information about a scroll bar +static PyObject * +ui_window_get_scroll_info (PyObject *self, PyObject *args) +{ + int nBar; + UINT nMask = SIF_ALL; + // @pyparm int|nBar||The scroll bar to examine. Can be one of win32con.SB_BOTH, win32con.SB_VERT or win32con.SB_HORZ + // @pyparm int|mask|SIF_ALL|The mask for attributes to retrieve. + if (!PyArg_ParseTuple(args, "i|i:GetScrollInfo", &nBar, &nMask)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = nMask; // Is this necessary? + GUI_BGN_SAVE; + BOOL ok = pWnd->GetScrollInfo(nBar, &info, nMask); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("GetScrollInfo failed"); + return MakeSCROLLINFOTuple(&info); +} + +// @pymethod int|PyCWnd|GetScrollPos|Retrieves the current position of the scroll box of a scroll bar. +static PyObject * +ui_window_get_scroll_pos (PyObject *self, PyObject *args) +{ + int nBar; + // @pyparm int|nBar||The scroll bar to examine. Can be one of win32con.SB_VERT or win32con.SB_HORZ + if (!PyArg_ParseTuple(args, "i:GetScrollPos", &nBar)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + long pos = pWnd->GetScrollPos(nBar); + GUI_END_SAVE; + return PyInt_FromLong(pos); + +} + +// @pymethod int|PyCWnd|GetStyle|Retrieves the window style +static PyObject * +ui_window_get_style(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetStyle")) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + DWORD ret = pWnd->GetStyle(); + GUI_END_SAVE; + return PyInt_FromLong(ret); +} + +// @pymethod int|PyCWnd|GetExStyle|Retrieves the window's extended style +static PyObject * +ui_window_get_ex_style(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetExStyle")) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + DWORD ret = pWnd->GetExStyle(); + GUI_END_SAVE; + return PyInt_FromLong(ret); + +} + +// @pymethod |PyCWnd|GetSystemMenu|Returns the menu object for the window's system menu. +static PyObject * +ui_window_get_system_menu(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + // @pyseemfc CWnd|GetSystemMenu + GUI_BGN_SAVE; + HMENU hMenu = ::GetSystemMenu(pWnd->m_hWnd,FALSE); + GUI_END_SAVE; + return ui_assoc_object::make( PyCMenu::type, hMenu)->GetGoodRet(); +} +// @pymethod |PyCWnd|GetTopWindow|Identifies the top-level child window in a linked list of child windows. +PyObject * +PyCWnd::get_top_window(PyObject *self, PyObject *args) +{ + // @comm Searches for the top-level child window that belongs to this window. If this window has no children, this function returns None + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + CWnd *pRel = pWnd->GetTopWindow(); + GUI_END_SAVE; + if (!pRel) + RETURN_NONE; + // @pyseemfc CWnd|GetTopWindow + return PyCWnd::make( UITypeFromCObject(pRel), pRel)->GetGoodRet(); + // @rdesc If no child windows exist, the value is None. +} + +// @pymethod |PyCWnd|GetWindow|Returns a window, with the specified relationship to this window. +PyObject * +PyCWnd::get_window(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + int type; + // @pyparm int|type|| + // Specifies the relationship between the current and the returned window. It can take one of the following values: + // GW_CHILD, GW_HWNDFIRST, GW_HWNDLAST, GW_HWNDNEXT, GW_HWNDPREV or GW_OWNER + if (!PyArg_ParseTuple(args,"i:GetWindow",&type)) + return NULL; + // @pyseemfc CWnd|GetWindow + GUI_BGN_SAVE; + CWnd *pRel = pWnd->GetWindow(type); + GUI_END_SAVE; + if (!pRel) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pRel), pRel)->GetGoodRet(); + // @rdesc The result is a or None if no Window can be found. +} + +// @pymethod tuple|PyCWnd|GetWindowPlacement|Returns placement information about the current window. +static PyObject * +ui_window_get_window_placement(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + WINDOWPLACEMENT pment; + pment.length=sizeof(pment); + // @pyseemfc CWnd|GetWindowPlacement + GUI_BGN_SAVE; + pWnd->GetWindowPlacement( &pment ); + GUI_END_SAVE; + // @rdesc The result is a tuple of + // (flags, showCmd, (minposX, minposY), (maxposX, maxposY), (normalposX, normalposY)) + // @flagh Item|Description + // @flag flags|One of the WPF_* constants + // @flag showCmd|Current state - one of the SW_* constants. + // @flag minpos|Specifies the coordinates of the window's upper-left corner when the window is minimized. + // @flag maxpos|Specifies the coordinates of the window's upper-left corner when the window is maximized. + // @flag normalpos|Specifies the window's coordinates when the window is in the restored position. + return Py_BuildValue("(ii(ii)(ii)(iiii))",pment.flags, pment.showCmd, + pment.ptMinPosition.x,pment.ptMinPosition.y, + pment.ptMaxPosition.x,pment.ptMaxPosition.y, + pment.rcNormalPosition.left, pment.rcNormalPosition.top, + pment.rcNormalPosition.right, pment.rcNormalPosition.bottom); +} + +// @pymethod (left, top, right, bottom)|PyCWnd|GetWindowRect|Returns the screen coordinates of the windows upper left corner +static PyObject * +ui_window_get_window_rect(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + CRect rect; + GUI_BGN_SAVE; + pWnd->GetWindowRect( &rect ); + GUI_END_SAVE; + // @pyseemfc CWnd|GetWindowRect + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); +} +// @pymethod string|PyCWnd|GetWindowText|Returns the windows text. +static PyObject * +ui_window_get_window_text(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + CString csText; + // @pyseemfc CWnd|Py_BuildValue + GUI_BGN_SAVE; + pWnd->GetWindowText( csText ); + GUI_END_SAVE; + return Py_BuildValue("s",(const char *)csText); +} +// @pymethod object|PyCWnd|HookKeyStroke|Hook a key stroke handler +static PyObject * +ui_window_hook_key_stroke(PyObject *self, PyObject *args) +{ + // @comm The handler object passed will be called as the application receives WM_CHAR message for the specified character code. + // The handler will be called with 2 arguments + // * The handler object (as per all hook functions) + // * The keystroke being handled. + // If the handler returns TRUE, then the keystroke will be passed on to the + // default handler, otherwise the keystroke will be consumed. + // Note: This handler will not be called if a hook is in place. + + // @pyparm object|obHandler||The handler of the keystroke. This must be a callable object. + // @pyparm int|ch||The ID for the keystroke to be handled. + // This may be an ascii code, or a virtual key code. + // @rdesc The return value is the previous handler, or None. + PyCWnd *s = (PyCWnd *)self; + return add_hook_list(s, args,&s->pKeyHookList); +} + +// @pymethod |PyCWnd|HookAllKeyStrokes|Hook a key stroke handler for all key strokes. +static PyObject * +ui_window_hook_all_key_strokes(PyObject *self, PyObject *args) +{ + // @comm The handler object passed will be called as the application receives WM_CHAR messages. + // The handler will be called with 2 arguments + // * The handler object (as per all hook functions). + // * The keystroke being handled. + // If the handler returns TRUE, then the keystroke will be passed on to the + // default handler, otherwise it will be consumed. + // Note: This handler will prevent any hooks from being called. + PyCWnd *s = (PyCWnd *)self; + PyObject *obHandler; + + // @pyparm object|obHandler||The handler for the keystrokes. This must be a callable object. + if (!PyArg_ParseTuple(args,"O:HookAllKeyStrokes", &obHandler)) + return NULL; + if (!PyCallable_Check(obHandler)) + RETURN_ERR("The parameter must be a callable object"); + Py_XDECREF(s->obKeyStrokeHandler); + s->obKeyStrokeHandler = obHandler; + Py_INCREF(s->obKeyStrokeHandler); + RETURN_NONE; +} + +// @pymethod object|PyCWnd|HookMessage|Hook a message notification handler +static PyObject * +ui_window_hook_message(PyObject *self, PyObject *args) +{ + // @comm The handler object passed will be called as the application receives messages with the specified ID. + // Note that it is not possible for PythonWin to consume a message - it is always passed on to the default handler. + // The handler will be called with 2 arguments + // * The handler object (as per all hook functions). + // * A tuple representing the message. + // The message tuple is built with the following code: + // Py_BuildValue("O(iiiii(ii))",meth,msg->hwnd,msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); + + // @pyparm object|obHandler||The handler for the message notification. This must be a callable object. + // @pyparm int|message||The ID of the message to be handled. + // @rdesc The return value is the previous handler, or None. + PyCWnd *s = (PyCWnd *)self; + return add_hook_list(s, args,&s->pMessageHookList); +} +// @pymethod int|PyCWnd|IsChild|Determines if a given window is a child of this window. +PyObject * +ui_window_is_child(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + PyObject *ob; + // @pyparm |obWnd||The window to be checked + if (!PyArg_ParseTuple(args, "O:IsChild", &ob )) + return NULL; + CWnd *pTest = GetWndPtrFromParam(ob, PyCWnd::type); + if (pTest==NULL) + return NULL; + // @pyseemfc CWnd|IsChild + GUI_BGN_SAVE; + int isChild = pWnd->IsChild(pTest); + GUI_END_SAVE; + return Py_BuildValue("i", isChild); +} + +// @pymethod int|PyCWnd|IsDlgButtonChecked|Determines if a dialog button is checked. +PyObject * +ui_window_is_dlg_button_checked(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int id; + // @pyparm int|idCtl||The ID of the button to check. + if (!PyArg_ParseTuple(args, "i:IsDlgButtonChecked", &id )) + return NULL; + // @pyseemfc CWnd|IsDlgButtonChecked + GUI_BGN_SAVE; + int rc = pWnd->IsDlgButtonChecked(id); + GUI_END_SAVE; + return Py_BuildValue("i", rc ); +} +// @pymethod int|PyCWnd|IsIconic|Determines if the window is currently displayed as an icon. +static PyObject * +ui_window_is_iconic(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,IsIconic); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + int rc = pWnd->IsIconic(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} +// @pymethod int|PyCWnd|IsZoomed|Determines if the window is currently maximised. +static PyObject * +ui_window_is_zoomed(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,IsZoomed); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + int rc = pWnd->IsZoomed(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWnd|IsWindowVisible|Determines if the window is currently visible. +static PyObject * +ui_window_is_window_visible(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,IsWindowVisible); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) return NULL; + GUI_BGN_SAVE; + long rc = pWnd->IsWindowVisible(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWnd|IsWindowEnabled|Determines if the window is currently enabled. +static PyObject * +ui_window_is_window_enabled(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,IsWindowEnabled); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) return NULL; + GUI_BGN_SAVE; + int rc = pWnd->IsWindowEnabled(); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod |PyCWnd|MessageBox|Display a message box. +static PyObject * +ui_window_message_box(PyObject * self, PyObject * args) +{ + char *message; + long style = MB_OK; + const char *title = NULL; + + + if (!PyArg_ParseTuple(args, "s|zl:MessageBox", + &message, // @pyparm string|message||The message to be displayed in the message box. + &title, // @pyparm string/None|title|None|The title for the message box. If None, the applications title will be used. + &style)) // @pyparm int|style|win32con.MB_OK|The style of the message box. + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int rc; + GUI_BGN_SAVE; + // @pyseemfc CWnd|MessageBox + + rc = pWnd->MessageBox(message, title, style); + GUI_END_SAVE; + return Py_BuildValue("i",rc); + // @rdesc An integer identifying the button pressed to dismiss the dialog. +} + +// @pymethod int|PyCWnd|ModifyStyle|Modifies the style of a window. +// @rdesc The result is true if the style was changed, or false if the style +// is already the same as requested and no change was made. +static PyObject * +ui_window_modify_style(PyObject *self, PyObject *args) +{ + unsigned flags = 0; + int add; + int remove; + if (!PyArg_ParseTuple(args, "ii|i:ModifyStyle", + &remove, // @pyparm int|remove||Specifies window styles to be removed during style modification. + &add,// @pyparm int|add||Specifies window styles to be added during style modification. + &flags)) // @pyparm int|flags|0|Flags to be passed to SetWindowPos, or zero if SetWindowPos should not be called. The default is zero. + return NULL; + + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL rc; + GUI_BGN_SAVE; + rc = pWnd->ModifyStyle(remove, add, flags ); + // @pyseemfc CWnd|ModifyStyle + GUI_END_SAVE; + return PyInt_FromLong(rc); + // @comm If nFlags is nonzero, ModifyStyle calls the Windows API function ::SetWindowPos and redraws the window by combining nFlags with the following four preset flags: + // * SWP_NOSIZE Retains the current size. + // * SWP_NOMOVE Retains the current position. + // * SWP_NOZORDER Retains the current Z order. + // * SWP_NOACTIVATE Does not activate the window. + // See also +} + +// @pymethod int|PyCWnd|ModifyStyleEx|Modifies the extended style of a window. +// @rdesc The result is true if the style was changed, or false if the style +// is already the same as requested and no change was made. +static PyObject * +ui_window_modify_style_ex(PyObject *self, PyObject *args) +{ + unsigned flags = 0; + int add; + int remove; + if (!PyArg_ParseTuple(args, "ii|i:ModifyStyleEx", + &remove, // @pyparm int|remove||Specifies extended window styles to be removed during style modification. + &add,// @pyparm int|add||Specifies extended extended window styles to be added during style modification. + &flags)) // @pyparm int|flags|0|Flags to be passed to SetWindowPos, or zero if SetWindowPos should not be called. The default is zero. + return NULL; + + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL rc; + GUI_BGN_SAVE; + rc = pWnd->ModifyStyleEx(remove, add, flags ); + // @pyseemfc CWnd|ModifyStyleEx + GUI_END_SAVE; + return PyInt_FromLong(rc); + // @comm If nFlags is nonzero, ModifyStyleEx calls the Windows API function ::SetWindowPos and redraws the window by combining nFlags with the following four preset flags: + // * SWP_NOSIZE Retains the current size. + // * SWP_NOMOVE Retains the current position. + // * SWP_NOZORDER Retains the current Z order. + // * SWP_NOACTIVATE Does not activate the window. + // See also +} + + +// @pymethod |PyCWnd|MoveWindow|Move a window to a new location. +static PyObject * +ui_window_move_window(PyObject *self, PyObject *args) +{ + CRect rect; + BOOL bRepaint= TRUE; + if (!PyArg_ParseTuple(args, "(iiii)|i:MoveWindow", + // @pyparm (left, top, right, bottom)|rect||The new location of the window, relative to the parent. + &rect.left, &rect.top, &rect.right, &rect.bottom, + &bRepaint)) // @pyparm int|bRepaint|1|Indicates if the window should be repainted after the move. + return NULL; + + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->MoveWindow(rect, bRepaint); + // @pyseemfc CWnd|MoveWindow + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCWnd|OnCtlColor|Calls the default MFC OnCtlColor handler. +// @xref +static PyObject * +ui_window_on_ctl_color(PyObject *self, PyObject *args) +{ + PyObject *obDC; + PyObject *obControl; + int nCtlColor; + if (!PyArg_ParseTuple(args, "OOi:OnCtlColor", + &obDC, // @pyparm |dc||The dc + &obControl, // @pyparm |control||The control that want's it's color changed + &nCtlColor)) // @pyparm int|type||Type of control + return NULL; + + CDC *pDC; + if (!(pDC=ui_dc_object::GetDC(obDC))) + return NULL; + + CWnd *pCtl = GetWndPtr(obControl); + + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + HBRUSH brush = pWnd->WndHack::OnCtlColor(pDC, pCtl, nCtlColor); + GUI_END_SAVE; + // @pyseemfc CWnd|OnCtlColor + return Py_BuildValue("i", (int)brush); +} + +// @pymethod int|PyCWnd|OnEraseBkgnd|Calls the default MFC OnEraseBkgnd handler. +// @xref +static PyObject * +ui_window_on_erase_bkgnd(PyObject *self, PyObject *args) +{ + PyObject *obDC; + if (!PyArg_ParseTuple(args, "O:OnEraseBkgnd", + &obDC)) // @pyparm |dc||The dc + return NULL; + + CDC *pDC; + if (!(pDC=ui_dc_object::GetDC(obDC))) + return NULL; + + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pWnd->WndHack::OnEraseBkgnd(pDC); + GUI_END_SAVE; + // @pyseemfc CWnd|OnEraseBkgnd + return Py_BuildValue("i", rc); +} + +// @pymethod int|PyCWnd|OnQueryDragIcon|Calls the default MFC OnQueryDragIcon handler. +static PyObject *ui_window_on_query_drag_icon( PyObject *self, PyObject *args ) +{ + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + CHECK_NO_ARGS2(args, OnQueryDragIcon ); + // @xref + GUI_BGN_SAVE; + HICON rc = pWnd->OnQueryDragIcon(); + GUI_END_SAVE; + return Py_BuildValue("i", (int)rc); +} + + +// @pymethod int|PyCWnd|OnPaint|Calls the default MFC OnPaint handler. +static PyObject * +ui_window_on_paint(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":OnPaint")) + return NULL; + + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->WndHack::OnPaint(); + GUI_END_SAVE; + // @pyseemfc CWnd|OnEraseBkgnd + // @xref + RETURN_NONE; +} + +// @pymethod int|PyCWnd|OnClose|Calls the default MFC OnClose handler. +// @xref +static PyObject * +ui_window_on_close(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":OnPaint")) + return NULL; + + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->WndHack::OnClose(); + GUI_END_SAVE; + // @pyseemfc CWnd|OnClose + RETURN_NONE; +} + +// @pymethod (int,int)|PyCWnd|OnWndMsg|Calls the default MFC Window Message handler. +static PyObject * +ui_window_on_wnd_msg(PyObject *self, PyObject *args) +{ + LRESULT res; + int msg, wParam, lParam; + CRect rect; + BOOL bRepaint= TRUE; + if (!PyArg_ParseTuple(args, "iii:OnWndMsg", + &msg, // @pyparm int|msg||The message + (int *)&wParam, // @pyparm int|wParam||The wParam for the message + (int *)&lParam)) // @pyparm int|lParam||The lParam for the message + return NULL; + + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pWnd->WndHack::OnWndMsg(msg, wParam, lParam, &res ); + GUI_END_SAVE; + // @pyseemfc CWnd|OnWndMsg + // @rdesc The return value is a tuple of (int, int), being the + // return value from the MFC function call, and the value of the + // lResult param. Please see the MFC documentation for more details. + return Py_BuildValue("ii", rc, (int)res); + RETURN_NONE; +} + +// @pymethod |PyCWnd|PostMessage|Post a message to the window. +PyObject * +ui_window_post_message(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + UINT message; + WPARAM wParam=0; + LPARAM lParam=0; + if (!PyArg_ParseTuple(args, "i|ii:PostMessage", + &message, // @pyparm int|idMessage||The ID of the message to post. + &wParam, // @pyparm int|wParam||The wParam for the message + &lParam)) // @pyparm int|lParam||The lParam for the message + return NULL; + // @pyseemfc CWnd|PostMessage + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + BOOL ok = pWnd->PostMessage(message, wParam, lParam); + GUI_END_SAVE; + if (!ok) + RETURN_API_ERR("CWnd::PostMessage"); + RETURN_NONE; +} + +// @pymethod |PyCWnd|RedrawWindow|Updates the specified rectangle or region in the given window's client area. +static PyObject * +ui_window_redraw_window(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + // @pyparm (left, top, right, bottom)|rect|None|A rect, or None + // @pyparm PyCRgn|object|PyCRgn or None|A region + // @pyparm int|flags|RDW_INVALIDATE \| RDW_UPDATENOW \| RDW_ERASE | + int flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE; + PyObject *obRect = Py_None; + PyObject *obRgn = Py_None; + if (!PyArg_ParseTuple(args,"|OOi:RedrawWindow", &obRect, &obRgn, &flags)) + return NULL; + + CRgn *pRgn=NULL; + if (obRgn != Py_None) { + pRgn = PyCRgn::GetRgn(obRgn); + if (!pRgn) + RETURN_TYPE_ERR("obRegion invalid"); + } + RECT rect; + RECT *pRect; + if (obRect==Py_None) + pRect = NULL; + else { + if (!PyArg_ParseTuple(obRect, "(iiii)", &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + pRect = ▭ + } + // @pyseemfc CWnd|RedrawWindow + GUI_BGN_SAVE; + BOOL ok = pWnd->RedrawWindow( pRect,pRgn, flags ); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("RedrawWindow failed"); + RETURN_NONE; +} + +// @pymethod (x,y) or (l, t, r, b)|PyCWnd|ClientToScreen|Converts the client coordinates of a given point on the display to screen coordinates. +static PyObject * +ui_window_client_to_screen(PyObject *self, PyObject *args) +{ + // @comm The new screen coordinates are relative to the upper-left corner of the system display. + // This function assumes that the given pointis in client coordinates. + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CPoint pnt; + CRect rect; + BOOL bHaveRect = TRUE; + // @pyparm (x,y)|point||The client coordinates. + // @pyparmalt1 (left, top, right, bottom)|rect||The client coordinates. + if (!PyArg_ParseTuple(args,"(iiii):ClientToScreen", + &rect.left, &rect.top, &rect.right, &rect.bottom)) + { + PyErr_Clear(); + bHaveRect = FALSE; + if (!PyArg_ParseTuple(args,"(ii):ClientToScreen", + &pnt.x, &pnt.y)) + { + return NULL; + } + } + + // @pyseemfc CWnd|ClientToScreen + GUI_BGN_SAVE; + if (bHaveRect) + pWnd->ClientToScreen( &rect ); + else + pWnd->ClientToScreen( &pnt ); + GUI_END_SAVE; + if (bHaveRect) + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); + else + return Py_BuildValue("(ii)",pnt.x, pnt.y); +} + +// @pymethod (left, top, right, bottom) or (x, y)|PyCWnd|ScreenToClient|Converts the screen coordinates of a given point or rectangle on the display to client coordinates. +static PyObject * +ui_window_screen_to_client(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CRect rect; + CPoint pnt; + BOOL bHaveRect = TRUE; + // @pyparm (left, top, right, bottom) or (x,y)|rect||The coordinates to convert. + // @pyparmalt1 (x,y)|pnt||The coordinates to convert. + if (!PyArg_ParseTuple(args,"(iiii):ScreenToClient", + &rect.left, &rect.top, &rect.right, &rect.bottom)) + { + PyErr_Clear(); + bHaveRect = FALSE; + if (!PyArg_ParseTuple(args,"(ii):ScreenToClient", + &pnt.x, &pnt.y)) + { + return NULL; + } + } + // @pyseemfc CWnd|ScreenToClient + GUI_BGN_SAVE; + if (bHaveRect) { + pWnd->ScreenToClient( &rect ); + } else { + pWnd->ScreenToClient( &pnt ); + } + GUI_END_SAVE; + // @rdesc The result is the same size as the input argument. + if (bHaveRect) { + return Py_BuildValue("(iiii)",rect.left, rect.top, rect.right, rect.bottom); + } else { + return Py_BuildValue("(ii)",pnt.x, pnt.y); + } +} + +// @pymethod |PyCWnd|SetActiveWindow|Sets the window active. Returns the previously active window, or None. +static PyObject * +ui_window_set_active_window(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + + CHECK_NO_ARGS(args); + CWnd *pRel; + GUI_BGN_SAVE; + pRel = pWnd->SetActiveWindow(); + GUI_END_SAVE; + if (!pRel) + RETURN_NONE; + return PyCWnd::make( UITypeFromCObject(pRel), pRel)->GetGoodRet(); + // @rdesc The result is the previous window with focus, or None. +} +// @pymethod |PyCWnd|SetRedraw|Allows changes to be redrawn or to prevent changes from being redrawn. +static PyObject * +ui_window_set_redraw(PyObject *self, PyObject *args) +{ + CWnd *pView = GetWndPtr(self); + if (!pView) + return NULL; + BOOL bState = TRUE; + // @pyparm int|bState|1|Specifies the state of the redraw flag. + if (!PyArg_ParseTuple(args, "i:SetRedraw", &bState)) + return NULL; + GUI_BGN_SAVE; + // @pyseemfc CWnd|SetRedraw + pView->SetRedraw(bState); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCWnd|SetScrollInfo|Set information about a scroll bar +static PyObject * +ui_window_set_scroll_info (PyObject *self, PyObject *args) +{ + int nBar; + BOOL bRedraw = TRUE; + PyObject *obInfo; + // @pyparm int|nBar||The scroll bar to examine. Can be one of win32con.SB_BOTH, win32con.SB_VERT or win32con.SB_HORZ + // @pyparm int|redraw|1|A flag indicating if the scrollbar should be re-drawn. + if (!PyArg_ParseTuple(args, "iO|i:SetScrollInfo", &nBar, &obInfo, &bRedraw)) + return NULL; + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + if (ParseSCROLLINFOTuple(obInfo, &info)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + BOOL ok = pWnd->SetScrollInfo(nBar, &info, bRedraw); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetScrollInfo failed"); + RETURN_NONE; +} + +// @pymethod int|PyCWnd|SetScrollPos|Sets the current position of the scroll box of a scroll bar. +static PyObject * +ui_window_set_scroll_pos (PyObject *self, PyObject *args) +{ + int nBar; + BOOL bRedraw = TRUE; + int nPos; + // @pyparm int|nBar||The scroll bar to set. Can be one of win32con.SB_VERT or win32con.SB_HORZ + // @pyparm int|nPos||The new position + // @pyparm int|redraw|1|A flag indicating if the scrollbar should be redrawn. + if (!PyArg_ParseTuple(args, "ii|i:SetScrollPos", &nBar, &nPos, &bRedraw)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + long rc = pWnd->SetScrollPos(nBar, nPos, bRedraw); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + + + +// @pymethod |PyCWnd|SetWindowPlacement|Sets the windows placement +static PyObject * +ui_window_set_window_placement(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + WINDOWPLACEMENT pment; + pment.length=sizeof(pment); + // @pyparm (tuple)|placement||A tuple representing the WINDOWPLACEMENT structure. + if (!PyArg_ParseTuple(args,"ii(ii)(ii)(iiii):SetWindowPlacement",&pment.flags, &pment.showCmd, + &pment.ptMinPosition.x,&pment.ptMinPosition.y, + &pment.ptMaxPosition.x,&pment.ptMaxPosition.y, + &pment.rcNormalPosition.left, &pment.rcNormalPosition.top, + &pment.rcNormalPosition.right, &pment.rcNormalPosition.bottom)) + return NULL; + int rc; + GUI_BGN_SAVE; + // @pyseemfc CWnd|SetWindowPlacement + rc=pWnd->SetWindowPlacement( &pment ); + GUI_END_SAVE; + if (!rc) + RETURN_API_ERR("CWnd::SetWindowPlacement"); + RETURN_NONE; +} +// @pymethod |PyCWnd|SetWindowPos|Sets the windows position information +static PyObject * +ui_window_set_window_pos(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + + HWND insertAfter; + int x,y,cx,cy; + int flags; + // @pyparm int|hWndInsertAfter||A hwnd, else one of the win32con.HWND_* constants. + // @pyparm (x,y,cx,cy)|position||The new position of the window. + // @pyparm int|flags||Window positioning flags. + if (!PyArg_ParseTuple(args,"i(iiii)i:SetWindowPos", + (int *)(&insertAfter), &x, &y, &cx, &cy, &flags )) + return NULL; + GUI_BGN_SAVE; + // @pyseemfc CWnd|SetWindowPos + BOOL ok = ::SetWindowPos( pWnd->GetSafeHwnd(), insertAfter, x, y, cx, cy, flags); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("SetWindowPos failed"); + RETURN_NONE; +} + +// @pymethod |PyCWnd|SetWindowText|Sets the window's text. +static PyObject * +ui_window_set_window_text(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + char *msg; + // @pyparm string|text||The windows text. + if (!PyArg_ParseTuple(args, "s:SetWindowText", &msg)) + return NULL; + // @pyseemfc CWnd|SetWindowText + GUI_BGN_SAVE; + pWnd->SetWindowText(msg); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|SendMessage|Send a message to the window. +PyObject * +ui_window_send_message(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtrGoodHWnd(self); + if (!pWnd) + return NULL; + int message; + int wParam=0; + int lParam=0; + if (!PyArg_ParseTuple(args, "i|ii:SendMessage", + &message, // @pyparm int|idMessage||The ID of the message to send. + &wParam, // @pyparm int|wParam||The wParam for the message + &lParam)) // @pyparm int|lParam||The lParam for the message + + return NULL; + int rc; + GUI_BGN_SAVE; + // @pyseemfc CWnd|SendMessage + rc = pWnd->SendMessage(message, wParam, lParam); + GUI_END_SAVE; + return Py_BuildValue("i",rc); +} +// @pymethod |PyCWnd|SendMessageToDescendants|Send a message to all descendant windows. +PyObject * +ui_window_send_message_to_desc(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + UINT message; + WPARAM wParam=0; + LPARAM lParam=0; + BOOL bDeep = TRUE; + if (!PyArg_ParseTuple(args, "i|iii:SendMessageToDescendants", + &message, // @pyparm int|idMessage||The ID of the message to send. + &wParam, // @pyparm int|wParam||The wParam for the message + &lParam, // @pyparm int|lParam||The lParam for the message + &bDeep)) // @pyparm int|bDeep|1|Indicates if the message should be recursively sent to all children + return NULL; + GUI_BGN_SAVE; + // @pyseemfc CWnd|SendMessageToDescendants + pWnd->SendMessageToDescendants(message, wParam, lParam, bDeep); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|ShowScrollBar|Shows or hides a scroll bar. +// An application should not call ShowScrollBar to hide a scroll bar while processing a scroll-bar notification message. +static PyObject * +ui_window_show_scrollbar(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int bar; + BOOL bShow = TRUE; + if (!PyArg_ParseTuple(args, "i|i:ShowScrollBar", + &bar, // @pyparm int|nBar||Specifies whether the scroll bar is a control or part of a window's nonclient area. + // If it is part of the nonclient area, nBar also indicates whether the scroll bar is positioned horizontally, vertically, or both. + // It must be one of win32con.SB_BOTH, win32con.SB_HORZ or win32con.SB_VERT. + &bShow))// @pyparm int|bShow|1|Indicates if the scroll bar should be shown or hidden. + + return NULL; + GUI_BGN_SAVE; + // @pyseemfc CWnd|ShowScrollBar + pWnd->ShowScrollBar(bar, bShow); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod int|PyCWnd|ShowWindow|Sets the visibility state of the window. +PyObject * +ui_window_show_window(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + int style = SW_SHOWNORMAL; + // @pyparm int|style|win32con.SW_SHOWNORMAL|Specifies how the window is to be shown. + // It must be one of win32con.SW_HIDE, win32con.SW_MINIMIZE, win32con.SW_RESTORE, win32con.SW_SHOW, win32con.SW_SHOWMAXIMIZED + // win32con.SW_SHOWMINIMIZED, win32con.SW_SHOWMINNOACTIVE, win32con.SW_SHOWNA, win32con.SW_SHOWNOACTIVATE, or win32con.SW_SHOWNORMAL + if (!PyArg_ParseTuple(args, "|i:ShowWindow", &style)) + return NULL; + int rc; + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + rc = pWnd->ShowWindow(style); + // @pyseemfc CWnd|ShowWindow + GUI_END_SAVE; + return Py_BuildValue("i",rc); + // @rdesc Returns TRUE is the window was previously visible. +} +// @pymethod int|PyCWnd|UpdateData|Initialises data in a dialog box, or to retrieves and validates dialog data. +// Returns nonzero if the operation is successful; otherwise 0. If bSaveAndValidate is TRUE, then a return value of nonzero means that the data is successfully validated. +static PyObject * +ui_window_update_data(PyObject *self, PyObject *args) +{ + int bSAV = TRUE; + // @pyparm int|bSaveAndValidate|1|Flag that indicates whether dialog box is being initialized (FALSE) or data is being retrieved (TRUE). + if (!PyArg_ParseTuple(args, "|i:UpdateData", &bSAV)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL rc = FALSE; + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + // @pyseemfc CWnd|UpdateData + rc = pWnd->UpdateData(bSAV); + GUI_END_SAVE; + return Py_BuildValue("i", rc ); +} +// @pymethod |PyCWnd|UpdateWindow|Updates a window. This forces a paint message to be sent to the window, if any part of the window is marked as invalid. +static PyObject * +ui_window_update_window(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + pWnd->UpdateWindow(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|InvalidateRect|Invalidates an area of a window. +static PyObject * +ui_window_invalidate_rect(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + BOOL erase=TRUE; + + if (!pWnd) + return NULL; + CRect rect (CFrameWnd::rectDefault), *r; + // @pyparm (left, top, right, bottom)|rect|(0,0,0,0)|Rectangle to be + // updated. If default param is used, the entire window is invalidated. + // @pyparm int|bErase|1|Specifies whether the background within the update region is to be erased. + if (!PyArg_ParseTuple (args, + "|(iiii)i:InvalidateRect", + &rect.left, &rect.top, + &rect.right, &rect.bottom, + &erase)) { + return NULL; + } + if (rect==CFrameWnd::rectDefault) + r = NULL; + else + r = ▭ + CHECK_HWND_VALID(pWnd); + GUI_BGN_SAVE; + pWnd->InvalidateRect (r, erase); + // @pyseemfc CWnd|InvalidateRect + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|GetDC|Gets the windows current DC object. +static PyObject * +ui_window_get_dc (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = (CWnd *) GetWndPtr (self); + if (pWnd == NULL) + return NULL; + + // create MFC device context + CDC *pDC = pWnd->GetDC(); + if (pDC==NULL) + RETURN_ERR ("Could not get the DC for the window."); + + + // create Python device context + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC)->GetGoodRet(); + return dc; + // @rdesc The result is a , or a win32ui.error exception is raised. +} + +// @pymethod |PyCWnd|SetCapture|Causes all subsequent mouse input to be sent to the window object regardless of the position of the cursor. +static PyObject * +ui_window_set_capture (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + + GUI_BGN_SAVE; + pWnd->SetCapture(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCWnd|SetFocus|Claims the input focus. The object that previously had the focus loses it. +static PyObject * +ui_window_set_focus (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + pWnd->SetFocus(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|SetFont|Sets the window's current font to the specified font. +static PyObject * +ui_window_set_font (PyObject * self, PyObject *args) +{ + PyObject * pfont; + int redraw=1; + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) { + return NULL; + // @pyparm |font||The new font to use. + // @pyparm int|bRedraw|1|If TRUE, redraw the window. + } else if (!PyArg_ParseTuple (args, "O|i", &pfont, &redraw)) { + return NULL; + } else if (!ui_base_class::is_uiobject (pfont, &PyCFont::type)) { + RETURN_ERR ("First argument must be a font object."); + } else { + GUI_BGN_SAVE; + pWnd->SetFont (((PyCFont *)pfont)->GetFont(), redraw); + GUI_END_SAVE; + RETURN_NONE; + } +} + + +// @pymethod |PyCWnd|SetForegroundWindow|Puts the window into the foreground and activates the window. +static PyObject * +ui_window_set_foreground_window(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + BOOL rc; + GUI_BGN_SAVE; + rc=pWnd->SetForegroundWindow(); + GUI_END_SAVE; + if (!rc) + RETURN_API_ERR("SetForegroundWindow"); + RETURN_NONE; +} + +// @pymethod |PyCWnd|SetMenu|Sets the menu for a window. +PyObject * +ui_window_set_menu(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr (self); + if (!pWnd) + return NULL; + PyObject *menuObject; + //@pyparm PyCMenu|menuObj||The menu object to set, or None to remove the window. + if (!PyArg_ParseTuple(args, "O:SetMenu", &menuObject)) + return NULL; + CMenu *pMenu = NULL; + if (menuObject!=Py_None) { + if (!ui_base_class::is_uiobject(menuObject, &PyCMenu::type)) + RETURN_TYPE_ERR("passed object must None or be a PyCMenu"); + HMENU hMenu = PyCMenu::GetMenu(menuObject); + if (hMenu==NULL) + return NULL; + pMenu=CMenu::FromHandle(hMenu); + if (pMenu==NULL) + RETURN_TYPE_ERR("The menu object is invalid"); + } + GUI_BGN_SAVE; + BOOL ok = pWnd->SetMenu(pMenu); + GUI_END_SAVE; + if (!ok) + RETURN_API_ERR("CWnd::SetMenu"); + RETURN_NONE; +} + +// @pymethod |PyCWnd|ReleaseCapture|Releases the mouse capture for this window. See . +static PyObject * +ui_window_release_capture (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *view = GetWndPtr (self); + if (view == NULL) + return NULL; + + GUI_BGN_SAVE; + if (view->GetCapture() == view) + ReleaseCapture(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|ReleaseDC|Releases a device context, freeing it for use by other applications. +static PyObject * +ui_window_release_dc (PyObject *self, PyObject *args) +{ + PyObject *obDC; + CWnd *view = GetWndPtr (self); + if (view == NULL) + return NULL; + + // @pyparm |dc||The DC to be released. + if (!PyArg_ParseTuple(args, "O:ReleaseDC", &obDC)) + return NULL; + + // Get the MFC device context + CDC *pDC; + if (!(pDC=ui_dc_object::GetDC(obDC))) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = view->ReleaseDC(pDC); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("ReleaseDC failed"); + RETURN_NONE; +} + +// @pymethod int|PyCWnd|MouseCaptured|Returns 1 if the window has the mouse capture, else 0 +static PyObject * +ui_window_mouse_captured (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *view = GetWndPtr (self); + if (view == NULL) + return NULL; + + // Returns true if this win has the mouse captured + GUI_BGN_SAVE; + BOOL is = view->GetCapture() == view; + GUI_END_SAVE; + return Py_BuildValue ("i", is); +} + +// @pymethod int|PyCWnd|UpdateDialogControls|Updates the state of dialog buttons and other controls in a dialog box or window that uses the callback mechanism. +static PyObject * +ui_window_udc (PyObject *self, PyObject *args) +{ + PyObject *obTarget; + BOOL bDisable; + // @pyparm |pTarget||The main frame window of the application, and is used for routing update messages. + // @pyparm int|disableIfNoHandler||Flag that indicates whether a control that has no update handler should be automatically displayed as disabled. + if (!PyArg_ParseTuple(args, "Oi:UpdateDialogControls", &obTarget, &bDisable)) + return NULL; + + extern CCmdTarget *GetCCmdTargetPtr(PyObject *self); + CCmdTarget *pTarget; + if (!(pTarget=GetCCmdTargetPtr(obTarget))) + return NULL; + CWnd *view = GetWndPtr (self); + if (view == NULL) + return NULL; + GUI_BGN_SAVE; + view->UpdateDialogControls(pTarget, bDisable); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|ShowCaret|Shows the caret +PyObject * +ui_window_show_caret(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, ShowCaret); + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + pWnd->ShowCaret(); + GUI_END_SAVE; + RETURN_NONE; + // @comm See also +} + +// @pymethod |PyCWnd|HideCaret|Hides the caret +PyObject * +ui_window_hide_caret(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, HideCaret); + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + pWnd->HideCaret(); + GUI_END_SAVE; + RETURN_NONE; + // @comm See also +} + +// @pymethod , |PyCWnd|BeginPaint|Prepares a window for painting +// @rdesc You must pass the PAINTSTRUCT param to the method. +PyObject *ui_window_begin_paint(PyObject *self, PyObject *args) +{ + PAINTSTRUCT ps; + if (!PyArg_ParseTuple(args, ":BeginPaint")) + return NULL; + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + GUI_BGN_SAVE; + CDC *pTemp = pWnd->BeginPaint(&ps); + GUI_END_SAVE; + if (pTemp==NULL) + RETURN_ERR("BeginPaint failed"); + PyObject *obDC = ui_assoc_object::make (ui_dc_object::type, pTemp)->GetGoodRet(); + PyObject *obRet = Py_BuildValue("O(ii(iiii)iis#)", obDC, + ps.hdc, + ps.fErase, + ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom, + ps.fRestore, + ps.fIncUpdate, + ps.rgbReserved, sizeof(ps.rgbReserved)); + Py_XDECREF(obDC); + return obRet; +} + +// @pymethod |PyCWnd|EndPaint|Ends painting +PyObject *ui_window_end_paint(PyObject *self, PyObject *args) +{ + PAINTSTRUCT ps; + PyObject *obString; + // @pyparm |paintStruct||The object returned from + if (!PyArg_ParseTuple(args, "(ii(iiii)iiO)", + &ps.hdc, + &ps.fErase, + &ps.rcPaint.left, &ps.rcPaint.top, &ps.rcPaint.right, &ps.rcPaint.bottom, + &ps.fRestore, + &ps.fIncUpdate, + &obString)) + return NULL; + + if (!PyString_Check(obString) || PyString_Size(obString) != sizeof(ps.rgbReserved)) + RETURN_TYPE_ERR("Last tuple must be a string of a specific size!"); + memcpy(ps.rgbReserved, PyString_AsString(obString), sizeof(ps.rgbReserved)); + + CWnd *pWnd = GetWndPtr (self); + if (pWnd == NULL) + return NULL; + + GUI_BGN_SAVE; + pWnd->EndPaint(&ps); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|CenterWindow|Centers a window relative to its parent. +static PyObject * +ui_window_center_window (PyObject *self, PyObject *args) +{ + PyObject *obAltWnd = NULL; + CWnd *view = GetWndPtr (self); + if (view == NULL) + return NULL; + + // @pyparm |altwin|None|alternate window relative to which it will be centered (other than the parent window). + if (!PyArg_ParseTuple(args, "|O:CenterWindow", &obAltWnd)) + return NULL; + + // Get the MFC device context + CWnd *pAltWnd = NULL; + if (obAltWnd) { + if (obAltWnd != Py_None) { + if (!(pAltWnd = GetWndPtr (obAltWnd))) + RETURN_TYPE_ERR ("Argument must be a PyCWnd or None"); + } + } + GUI_BGN_SAVE; + view->CenterWindow (pAltWnd); // @pyseemfc CWnd|CenterWindow + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|PumpWaitingMessages|Pump messages associate with a window. +PyObject * +ui_window_pump_waiting_messages(PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + UINT firstMsg; + UINT lastMsg; + if (!PyArg_ParseTuple(args, "ii:PumpWaitingMessages", + &firstMsg, // @pyparm int|firstMsg||First message ID to process + &lastMsg)) // @pyparm int|lastMsg||First message ID to process + return NULL; + // @pyseemfc CWnd|PeekMessage and DispatchMessage + CHECK_HWND_VALID(pWnd); + MSG msg; + GUI_BGN_SAVE; + if (::PeekMessage(&msg, pWnd->m_hWnd, firstMsg, lastMsg, PM_REMOVE)) + { + ::DispatchMessage(&msg); + } + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|LockWindowUpdate|Disables drawing in the given window +static PyObject * +ui_lock_window_update(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->LockWindowUpdate(); // @pyseemfc CWnd|LockWindowUpdate + GUI_END_SAVE; + RETURN_NONE; +} + + +// @pymethod |PyCWnd|UnlockWindowUpdate|Unlocks a window that was locked with LockWindowUpdate +static PyObject * +ui_unlock_window_update(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + + GUI_BGN_SAVE; + pWnd->UnlockWindowUpdate(); // @pyseemfc CWnd|UnLockWindowUpdate + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCWnd|GetWindowDC|Gets the windows current DC object. +static PyObject * +ui_window_get_window_dc (PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS(args); + CWnd *pWnd = (CWnd *) GetWndPtr (self); + if (pWnd == NULL) + return NULL; + + // create MFC device context + CDC *pDC = pWnd->GetWindowDC(); + if (pDC==NULL) + RETURN_ERR ("Could not get the DC for the window."); + + // create Python device context + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC)->GetGoodRet(); + return dc; +} + +// @pymethod |PyCWnd|GetDCEx|Gets the windows current DC object with extended caps. +static PyObject * +ui_window_get_dc_ex(PyObject *self, PyObject *args) + { + CWnd *pWnd = (CWnd *) GetWndPtr (self); + if (pWnd == NULL)return NULL; + + PyObject *objRgn; + DWORD flags; + if (!PyArg_ParseTuple(args,"Oi:GetDCEx",&objRgn,&flags)) + return NULL; + + CRgn *prgnClip; + if (objRgn==Py_None) + prgnClip = NULL; + else { + prgnClip = PyCRgn::GetRgn(objRgn); + if (!prgnClip) return NULL; + } + + // create MFC device context + CDC *pDC = pWnd->GetDCEx(prgnClip,flags); + if (pDC==NULL) + RETURN_ERR ("Could not get the DC for the window."); + + // create Python device context + ui_dc_object *dc = + (ui_dc_object *) ui_assoc_object::make (ui_dc_object::type, pDC)->GetGoodRet(); + return dc; + } +// @pymethod int|PyCWnd|IsWindow|determines whether the specified window handle identifies an existing window +static PyObject * +ui_window_is_window(PyObject *self, PyObject *args) + { + CHECK_NO_ARGS(args); + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + + BOOL bAns=::IsWindow(pWnd->m_hWnd); + return Py_BuildValue("i",bAns); + } + + +// @pymethod |PyCWnd|MapWindowPoints|Converts (maps) a set of points from the coordinate space of a window to the coordinate space of another window. +static PyObject * +ui_window_map_window_points(PyObject *self, PyObject *args) + { + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + + PyObject *pPyCWnd; + PyObject *point_list; + // @pyparm |wnd|| + // @pyparm [ (x,y), ...]|points||The points to map + + if (!PyArg_ParseTuple(args,"OO:MapWindowPoints",&pPyCWnd,&point_list)) + return NULL; + + CWnd *pWndTo; + if (pPyCWnd==Py_None) + pWndTo = NULL; // i.e screen coordinates conversion + else if (ui_base_class::is_uiobject(pPyCWnd, &PyCWnd::type)) + pWndTo = GetWndPtr(pPyCWnd); + else + RETURN_TYPE_ERR("1st argument must be a window object, or None"); + + if(!PyList_Check(point_list)) + return NULL; + + // Convert the list of point tuples into an array of POINT structs + int num = PyList_Size (point_list); + POINT * point_array = new POINT[num]; + for (int i=0; i < num; i++) + { + PyObject * point_tuple = PyList_GetItem (point_list, i); + if (!PyTuple_Check (point_tuple) || PyTuple_Size (point_tuple) != 2) + { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } + else + { + long x, y; + PyObject *px, *py; + px = PyTuple_GetItem (point_tuple, 0); + py = PyTuple_GetItem (point_tuple, 1); + if ((!PyInt_Check(px)) || (!PyInt_Check(py))) + { + PyErr_SetString (PyExc_ValueError, + "point list must be a list of (x,y) tuples"); + delete[] point_array; + return NULL; + } + else + { + x = PyInt_AsLong (px); + y = PyInt_AsLong (py); + point_array[i].x = x; + point_array[i].y = y; + } + } + } + // we have an array of POINT structs, now we + // can finally call the mfc function. + GUI_BGN_SAVE; + pWnd->MapWindowPoints(pWndTo,point_array, num); + GUI_END_SAVE; + + // create a list + // copy mapped points + // return list of points + PyObject *list = PyList_New(num); + for (i=0;iSetTimer(nIDEvent,nElapse,NULL); + GUI_END_SAVE; + return Py_BuildValue("i", id); + } + +// @pymethod int|PyCWnd|KillTimer|Kills a system timer +static PyObject * +ui_window_kill_timer(PyObject *self, PyObject *args) + { + UINT nID; + if (!PyArg_ParseTuple(args, "i:KillTimer",&nID)) + return NULL; + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + + // @pyseemfc CWnd|KillTimer + GUI_BGN_SAVE; + BOOL br = pWnd->KillTimer(nID); + GUI_END_SAVE; + return Py_BuildValue("i", br); + } + + +// @pymethod |PyCWnd|InvalidateRgn|Invalidates a region of the window +PyObject * +ui_window_invalidate_rgn(PyObject *self, PyObject *args) + { + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + PyObject *objRgn; + BOOL bErase = TRUE; + // @pyparm |region||The region to erase. + // @pyparm int|bErase|1|Indicates if the region should be erased. + if (!PyArg_ParseTuple(args,"O|i:InvalidateRgn",&objRgn,&bErase)) + return NULL; + CRgn *pRgn = PyCRgn::GetRgn(objRgn); + if (!pRgn) return NULL; + GUI_BGN_SAVE; + pWnd->InvalidateRgn(pRgn,bErase); + GUI_END_SAVE; + RETURN_NONE; + } + +// @pymethod int|PyCWnd|RunModalLoop|Begins a modal loop for the window. +PyObject * +ui_window_run_modal_loop(PyObject *self, PyObject *args) + { + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + DWORD dwFlags; + // @pyparm int|flags|| + if (!PyArg_ParseTuple(args,"i:RunModalLoop",&dwFlags)) + return NULL; + GUI_BGN_SAVE; + int nResult=pWnd->RunModalLoop(dwFlags); + GUI_END_SAVE; + return Py_BuildValue("i",nResult); + } +// @pymethod |PyCWnd|EndModalLoop|Ends a modal loop. +PyObject * +ui_window_end_modal_loop(PyObject *self, PyObject *args) + { + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + int nResult; + // @pyparm int|result||The result as returned to RunModalLoop + if (!PyArg_ParseTuple(args,"i:EndModalLoop",&nResult)) + return NULL; + GUI_BGN_SAVE; + pWnd->EndModalLoop(nResult); + GUI_END_SAVE; + RETURN_NONE; + } + +// @pymethod |PyCWnd|RepositionBars|Repositions the windows control bars.( UINT nIDFirst, UINT nIDLast, UINT nIDLeftOver, UINT nFlag = CWnd::reposDefault, LPRECT lpRectParam = NULL, LPCRECT lpRectClient = NULL, BOOL bStretch = TRUE ); +PyObject * +ui_window_reposition_bars(PyObject *self, PyObject *args) + { + CWnd *pWnd = GetWndPtr(self); + if (!pWnd)return NULL; + UINT nIDFirst,nIDLast, nIDLeftOver; + // @pyparm int|idFirst||The ID of the first control to reposition. + // @pyparm int|idLast||The ID of the last control to reposition. + // @pyparm int|idLeftOver|| + if (!PyArg_ParseTuple(args,"iii:RepositionBars",&nIDFirst,&nIDLast,&nIDLeftOver)) + return NULL; + GUI_BGN_SAVE; + pWnd->RepositionBars(nIDFirst,nIDLast,nIDLeftOver); + GUI_END_SAVE; + RETURN_NONE; + } + +// @pymethod int|PyCWnd|OnNcHitTest|Calls the base MFC OnNcHitTest function. +// @xref +static PyObject * +ui_window_on_nc_hit_test( PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + CPoint p; + if (!PyArg_ParseTuple(args, "(ii):OnNcHitTest", &p.x, &p.y)) // @pyparm int, int|x, y||The point + return NULL; + GUI_BGN_SAVE; + UINT rc = ((WndHack *)pWnd)->OnNcHitTest(p); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWnd|OnSetCursor|Calls the base MFC OnSetCursor function. +// @xref +static PyObject * +ui_window_on_set_cursor( PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + PyObject *obWnd; + int ht, msg; + // @pyparm |wnd|| + // @pyparm int|hittest|| + // @pyparm int|message|| + if (!PyArg_ParseTuple(args, "Oii:OnSetCursor", &obWnd, &ht, &msg)) + return NULL; + CWnd *pWndArg = GetWndPtrFromParam(obWnd, PyCWnd::type); + if (pWndArg==NULL) return NULL; + GUI_BGN_SAVE; + UINT rc = ((WndHack *)pWnd)->OnSetCursor(pWndArg, ht, msg); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCWnd|OnMouseActivate|Calls the base MFC OnMouseActivate function. +// @xref +static PyObject * +ui_window_on_mouse_activate( PyObject *self, PyObject *args) +{ + CWnd *pWnd = GetWndPtr(self); + if (!pWnd) + return NULL; + PyObject *obWnd; + int ht, msg; + // @pyparm |wnd|| + // @pyparm int|hittest|| + // @pyparm int|message|| + if (!PyArg_ParseTuple(args, "Oii:OnMouseActivate", &obWnd, &ht, &msg)) + return NULL; + CWnd *pWndArg = GetWndPtrFromParam(obWnd, PyCWnd::type); + if (pWndArg==NULL) return NULL; + GUI_BGN_SAVE; + UINT rc = ((WndHack *)pWnd)->OnMouseActivate(pWndArg, ht, msg); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod tuple|PyCWnd|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +// @xref +static PyObject * +ui_window_pre_create_window(PyObject *self, PyObject *args) +{ + WndHack *pWnd = (WndHack *)GetWndPtr(self); + if (!pWnd) + return NULL; + + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = pWnd->WndHack::PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok)RETURN_ERR("CWnd::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod int|PyCWnd|OnQueryNewPalette|Calls the underlying MFC OnQueryNewPalette method. +// @xref +PyObject * +ui_window_on_query_new_palette(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args, OnQueryNewPalette); + WndHack *wnd = (WndHack *)GetWndPtr(self); + if (wnd == NULL) + return NULL; + GUI_BGN_SAVE; + int rc = wnd->WndHack::OnQueryNewPalette(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +/////////////////////////////////////// +// +// Window Methods +// +/////////////////////////////////////// +// @object PyCWnd|A base window class. Encapsulates an MFC class +static struct PyMethodDef PyCWnd_methods[] = { + // Window Methods + {"ActivateFrame", ui_window_activate_frame, 1}, // @pymeth ActivateFrame|Searches upwards for a parent window which has a frame, and activates it. + {"BringWindowToTop", ui_window_bring_window_to_top, 1}, // @pymeth BringWindowToTop|Brings the window to the top of a stack of overlapping windows. + {"BeginPaint", ui_window_begin_paint, 1}, // @pymeth BeginPaint|Prepares the window for painting. + {"CalcWindowRect", ui_window_calc_window_rect, 1}, // @pymeth CalcWindowRect|Computes the size of the window rectangle based on the desired client rectangle size. + {"CenterWindow", ui_window_center_window, 1}, // @pymeth CenterWindow|Centers a window relative to its parent. + {"CheckRadioButton", ui_window_check_radio_button, 1}, // @pymeth CheckRadioButton|Selects a specified radio button + {"ChildWindowFromPoint",ui_child_window_from_point, 1}, // @pymeth ChildWindowFromPoint|Identifies the child window that contains the point + {"ClientToScreen", ui_window_client_to_screen, 1}, // @pymeth ClientToScreen|Convert coordinates from Client to Screen + {"CreateWindow", ui_window_create_window, 1}, // @pymeth CreateWindow|Create the underlying window object + {"CreateWindowEx", ui_window_create_window_ex, 1}, // @pymeth CreateWindowEx|Creates the actual window for the PyCWnd object using extended attributes. + {"DefWindowProc", ui_window_def_window_proc, 1}, // @pymeth DefWindowProc|Calls the default message handler. + {"DestroyWindow", ui_window_destroy_window, 1}, // @pymeth DestroyWindow|Destroys the window attached to the object. + {"DlgDirList", ui_window_dlg_dir_list, 1}, // @pymeth DlgDirList|Fill a listbox control with a file specification. + {"DlgDirListComboBox", ui_window_dlg_dir_list_combo, 1}, // @pymeth DlgDirListComboBox|Fill a combobox control with a file specification. + {"DlgDirSelect", ui_window_dlg_dir_select, 1}, // @pymeth DlgDirSelect|Retrieves the current selection from a list box. + {"DlgDirSelectComboBox",ui_window_dlg_dir_select_combo, 1}, // @pymeth DlgDirSelectComboBox|Retrieves the current selection from a combo box. + {"DragAcceptFiles", ui_window_drag_accept_files, 1}, // @pymeth DragAcceptFiles|Indicate the window can accept files dragges from file manager. + {"DrawMenuBar", ui_window_draw_menu_bar, 1}, // @pymeth DrawMenuBar|Redraw the windows menu bar. + {"EnableWindow", ui_window_enable_window, 1}, // @pymeth EnableWindow|Enable or disable the window. + {"EndModalLoop", ui_window_end_modal_loop, 1}, // @pymeth EndModalLoop|Ends a modal loop. + {"EndPaint", ui_window_end_paint, 1}, // @pymeth EndPaint|Ends painting in a window + {"GetCheckedRadioButton",ui_window_get_checked_rb, 1}, // @pymeth GetCheckedRadioButton|Get the ID of the checked a radio button in a group. + {"GetClientRect", ui_window_get_client_rect, 1}, // @pymeth GetClientRect|Gets the client rectangle for thewindow. + {"GetDC", ui_window_get_dc, 1}, // @pymeth GetDC|Gets the window's current device context. + {"GetDCEx", ui_window_get_dc_ex, 1}, // @pymeth GetDCEx|Gets the window's current device context. + {"GetDlgCtrlID", ui_window_get_dlg_ctrl_id, 1}, // @pymeth GetDlgCtrlID|Get the current window's control id. + {"GetDlgItem", ui_window_get_dlg_item, 1}, // @pymeth GetDlgItem|Get a child control by Id + {"GetDlgItemInt", ui_window_get_dlg_item_int, 1}, // @pymeth GetDlgItemInt|Returns the integer value of a child window or control with the specified ID. + {"GetDlgItemText", ui_window_get_dlg_item_text, 1}, // @pymeth GetDlgItemText|Returns the text of child window or control with the specified ID. + {"GetLastActivePopup", ui_window_get_last_active_popup,1}, // @pymeth GetLastActivePopup|Identifies the most recently active pop-up window + {"GetMenu", ui_window_get_menu, 1}, // @pymeth GetMenu|Get the current menu for a window. + {"GetParent", ui_window_get_parent, 1}, // @pymeth GetParent|Get the parent window. + {"GetParentFrame", ui_window_get_parent_frame, 1}, // @pymeth GetParentFrame|Returns the window's frame. + {"GetParentOwner", ui_window_get_parent_owner, 1}, // @pymeth GetParent|Returns the child window's parent window or owner window. + {"GetSafeHwnd", ui_window_get_safe_hwnd, 1}, // @pymeth GetSafeHwnd|Returns the HWnd of this window. + {"GetScrollInfo", ui_window_get_scroll_info, 1}, // @pymeth GetScrollInfo|Retrieve information about a scroll bar + {"GetScrollPos", ui_window_get_scroll_pos, 1}, // @pymeth GetScrollPos|Retrieves the current position of the scroll box of a scroll bar. + {"GetStyle", ui_window_get_style, 1}, // @pymeth GetStyle|Retrieves the window style + {"GetExStyle", ui_window_get_ex_style, 1}, // @pymeth GetExStyle|Retrieves the window extended style + {"GetSystemMenu", ui_window_get_system_menu, 1}, // @pymeth GetSystemMenu|Get the system menu for the window. + {"GetTopLevelFrame", ui_window_get_top_level_frame, 1}, // @pymeth GetTopLevelFrame|Get the top-level frame window. + {"GetTopLevelOwner", ui_window_get_top_level_owner, 1}, // @pymeth GetTopLevelOwner|Get the top-level owner window. + {"GetTopLevelParent", ui_window_get_top_level_parent, 1}, // @pymeth GetTopLevelParent|Get the top-level parent window. + {"GetTopWindow", PyCWnd::get_top_window,1},// @pymeth GetTopWindow|Get the top level window attached to this window. + {"GetWindow", PyCWnd::get_window, 1}, // @pymeth GetWindow|Get a specified window (eg, parent, child, etc). + {"GetWindowDC", ui_window_get_window_dc, 1}, // @pymeth GetWindowDC|Obtains the for a window. + {"GetWindowPlacement", ui_window_get_window_placement, 1}, // @pymeth GetWindowPlacement|Gets the window's current placement information. + {"GetWindowRect", ui_window_get_window_rect, 1}, // @pymeth GetWindowRect|Get the windows rectangle. + {"GetWindowText", ui_window_get_window_text, 1}, // @pymeth GetWindowText|Get the window's current text. + {"HideCaret", ui_window_hide_caret, 1}, // @pymeth HideCaret|Hides the caret + {"HookAllKeyStrokes", ui_window_hook_all_key_strokes, 1}, // @pymeth HookAllKeyStrokes|Hook a handler for all keystroke messages. + {"HookKeyStroke", ui_window_hook_key_stroke, 1}, // @pymeth HookKeyStroke|Hook a keystroke handler. + {"HookMessage", ui_window_hook_message, 1}, // @pymeth HookMessage|Hook a message notification handler. + {"InvalidateRect", ui_window_invalidate_rect, 1}, // @pymeth InvalidateRect|Invalidate a specified rectangle in a window. + {"InvalidateRgn", ui_window_invalidate_rgn, 1}, // @pymeth InvalidateRgn|Invalidate a specified region of a window. + {"IsChild", ui_window_is_child, 1}, // @pymeth IsChild|Indicates if a window is a child. + {"IsDlgButtonChecked", ui_window_is_dlg_button_checked,1}, // @pymeth IsDlgButtonChecked|Indicates if a dialog botton is checked. + {"IsIconic", ui_window_is_iconic, 1}, // @pymeth IsIconic|Indicates if the window is currently minimised. + {"IsZoomed", ui_window_is_zoomed, 1}, // @pymeth IsZoomed|Indicates if the window is currently maximised. + {"IsWindow", ui_window_is_window, 1}, // @pymeth IsWindow|determines whether the specified window handle identifies an existing window. + {"IsWindowVisible", ui_window_is_window_visible, 1}, // @pymeth IsWindowVisible|Determines if the window is currently visible. + {"IsWindowEnabled", ui_window_is_window_enabled, 1}, // @pymeth IsWindowVisible|Determines if the window is currently enabled. + {"KillTimer", ui_window_kill_timer, 1}, // @pymeth KillTimer|Destroys a system timer + {"LockWindowUpdate", ui_lock_window_update, 1}, // @pymeth LockWindowUpdate|Disables drawing in the given window + {"MapWindowPoints", ui_window_map_window_points, 1}, // @pymeth MapWindowPoints|Converts (maps) a set of points from the coordinate space of the CWnd to the coordinate space of another window. + {"MouseCaptured", ui_window_mouse_captured, 1}, // @pymeth MouseCaptured|Indicates if the window currently has the mouse captured. + {"MessageBox", ui_window_message_box, 1}, // @pymeth MessageBox|Displays a message box. + {"ModifyStyle", ui_window_modify_style, 1}, // @pymeth ModifyStyle|Modifies the style of a window. + {"ModifyStyleEx", ui_window_modify_style_ex, 1}, // @pymeth ModifyStyleEx|Modifies the style of a window. + {"MoveWindow", ui_window_move_window, 1}, // @pymeth MoveWindow|Moves the window to a new location. + {"OnClose", ui_window_on_close, 1}, // @pymeth OnClose|Calls the default MFC OnClose handler. + {"OnCtlColor", ui_window_on_ctl_color, 1}, // @pymeth OnCtlColor|Calls the default MFC OnCtlColor handler. + {"OnEraseBkgnd", ui_window_on_erase_bkgnd, 1}, // @pymeth OnEraseBkgnd|Calls the default MFC OnEraseBkgnd handler. + {"OnNcHitTest", ui_window_on_nc_hit_test, 1}, // @pymeth OnNcHitTest|Calls the base MFC OnNcHitTest function. + {"OnPaint", ui_window_on_paint, 1}, // @pymeth OnPaint|Calls the default MFC OnPaint handler. + {"OnQueryDragIcon", ui_window_on_query_drag_icon, 1}, // @pymeth OnQueryDragIcon|Calls the default MFC OnQueryDragIcon handler. + {"OnQueryNewPalette", ui_window_on_query_new_palette, 1}, // @pymeth OnQueryNewPalette|Calls the underlying MFC OnQueryNewPalette method. + {"OnSetCursor", ui_window_on_set_cursor, 1}, // @pymeth OnSetCursor|Calls the default MFC OnSetCursor message + {"OnMouseActicate", ui_window_on_mouse_activate, 1}, // @pymeth OnMouseActivate|Calls the default MFC OnMouseActicate message + {"OnWndMsg", ui_window_on_wnd_msg, 1}, // @pymeth OnWndMsg|Calls the default MFC Window Message handler. + {"PreCreateWindow", ui_window_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"PumpWaitingMessages", ui_window_pump_waiting_messages,1}, // @pymeth PumpWaitingMessages|Calls the Peek/Dispatch loop on the wnd. + {"RedrawWindow", ui_window_redraw_window, 1}, // @pymeth RedrawWindow|Updates the specified rectangle or region in the given window's client area. + {"ReleaseCapture", ui_window_release_capture, 1}, // @pymeth ReleaseCapture|Releases the mouse capture for the window. + {"ReleaseDC", ui_window_release_dc, 1}, // @pymeth ReleaseDC|Releases a device context, freeing it for use by other applications. + {"RepositionBars", ui_window_reposition_bars, 1}, // @pymeth RepositionBars|Repositions the control bars for the window. + {"RunModalLoop", ui_window_run_modal_loop, 1}, // @pymeth RunModalLoop|Starts a modal loop for the window. + {"PostMessage", ui_window_post_message, 1}, // @pymeth PostMessage|Post a message to the window. + {"SendMessageToDescendants",ui_window_send_message_to_desc, 1}, // @pymeth SendMessageToDescendants|Send a message to a window's children. + {"SendMessage", ui_window_send_message, 1}, // @pymeth SendMessage|Send a message to the window. + {"SetActiveWindow", ui_window_set_active_window, 1}, // @pymeth SetActiveWindow|Sets the window active. + {"SetForegroundWindow", ui_window_set_foreground_window,1}, // @pymeth SetForegroundWindow|Puts the window into the foreground and activates the window. + {"SetWindowPos", ui_window_set_window_pos, 1}, // @pymeth SetWindowPos|Sets the windows position information. + {"ScreenToClient", ui_window_screen_to_client, 1}, // @pymeth ScreenToClient|Converts from screen coordinates to client coordinates. + {"SetCapture", ui_window_set_capture, 1}, // @pymeth SetCapture|Captures the mouse input for thw window. + {"SetDlgItemText", ui_window_set_dlg_item_text, 1}, // @pymeth SetDlgItemText|Sets the text for the child window or control with the specified ID. + {"SetFocus", ui_window_set_focus, 1}, // @pymeth SetFocus|Sets focus to the window. + {"SetFont", ui_window_set_font, 1}, // @pymeth SetFont|Sets the window's current font to the specified font. + {"SetMenu", ui_window_set_menu, 1}, // @pymeth SetMenu|Sets the menu for a window. + {"SetRedraw", ui_window_set_redraw, 1}, // @pymeth SetRedraw|Sets the redraw flag for the window. + {"SetScrollPos", ui_window_set_scroll_pos, 1}, // @pymeth SetScrollPos|Sets the current position of the scroll box of a scroll bar. + {"SetScrollInfo", ui_window_set_scroll_info, 1}, // @pymeth SetScrollInfo|Set information about a scroll bar + {"SetTimer", ui_window_set_timer, 1}, // @pymeth SetTimer|Installs a system timer + {"SetWindowPlacement", ui_window_set_window_placement, 1}, // @pymeth SetWindowPlacement|Sets the window's placement options. + {"SetWindowText", ui_window_set_window_text, 1}, // @pymeth SetWindowText|Sets the window's text. + {"ShowCaret", ui_window_show_caret, 1}, // @pymeth ShowCaret|Shows the caret + {"ShowScrollBar", ui_window_show_scrollbar, 1}, // @pymeth ShowScrollBar|Shows/Hides the window's scroll bars. + {"ShowWindow", ui_window_show_window, 1}, // @pymeth ShowWindow|Shows the window. + {"UnlockWindowUpdate", ui_unlock_window_update, 1}, // @pymeth UnLockWindowUpdate|Unlocks a window that was locked with LockWindowUpdate + {"UpdateData", ui_window_update_data, 1}, // @pymeth UpdateData|Updates a windows dialog data. + {"UpdateDialogControls",ui_window_udc, 1}, // @pymeth UpdateDialogControls|Updates the state of dialog buttons and other controls in a dialog box or window that uses the callback mechanism. + {"UpdateWindow", ui_window_update_window, 1}, // @pymeth UpdateWindow|Updates a window. + {NULL, NULL} +}; + +CString PyCWnd::repr() +{ + CString csRet; + char *buf = csRet.GetBuffer(40); + int numMsg = pMessageHookList ? pMessageHookList->GetCount() : 0; + int numKey = pKeyHookList ? pKeyHookList->GetCount() : 0; + char *hookStr = obKeyStrokeHandler ? " (AllKeys Hook Active)" : ""; + sprintf(buf, ", mh=%d, kh=%d%s", numMsg, numKey, hookStr); + csRet.ReleaseBuffer(-1); + return PyCCmdTarget::repr() + csRet; +} + +ui_type_CObject PyCWnd::type("PyCWnd", + &PyCCmdTarget::type, + RUNTIME_CLASS(CWnd), + sizeof(PyCWnd), + PyCWnd_methods, + GET_PY_CTOR(PyCWnd) ); + + +///////////////////////////////////////////////////////////////////// +// +// Frame Window objects +// +// MDIFrameWindow is the application frame, MDIChildWindow is the child frame. +// +////////////////////////////////////////////////////////////////////// +// @pymethod |PyCFrameWnd|GetActiveDocument|Gets the currently active document, else None +static PyObject * +ui_frame_get_active_document(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS(args); + GUI_BGN_SAVE; + CDocument *pDoc = pFrame->GetActiveDocument(); // @pyseemfc CFrameWnd|GetActiveDocument + GUI_END_SAVE; + if (!pDoc) + RETURN_NONE; + return ui_assoc_object::make( PyCDocument::type, pDoc)->GetGoodRet(); +} + +// @pymethod |win32ui|CreateFrame|Creates a Frame window. +PyObject * +ui_create_frame(PyObject *self, PyObject *args) +{ + CHECK_NO_ARGS2(args,CreateFrame); + GUI_BGN_SAVE; + CPythonFrameWnd* pFrame = new CPythonFrameWnd; + GUI_END_SAVE; + return ui_assoc_object::make(PyCFrameWnd::type, pFrame)->GetGoodRet(); + // @rdesc The window object (not the OS window) created. An exception is raised if an error occurs. +} + +// @pymethod tuple|PyCFrameWnd|CreateWindow|Creates the actual window for the PyCFrameWnd object. +static PyObject * +PyCFrameWnd_CreateWindow(PyObject *self, PyObject *args) + { + CFrameWnd *pFrame=GetFramePtr(self); + if (!pFrame)return NULL; + + PythonCreateContext cc; + RECT rect = CFrameWnd::rectDefault; + PyObject *obRect = Py_None; + PyObject *obParent = Py_None; + PyObject *obContext = Py_None; + char *szClass=NULL, *szTitle=NULL,*szMenuName=NULL; + DWORD styleEx=0; + DWORD style = WS_VISIBLE | WS_OVERLAPPEDWINDOW; + if(!PyArg_ParseTuple(args, "zs|lOOOzl:Create", + &szClass, // @pyparm string|wndClass||The window class name, or None + &szTitle, // @pyparm string|title||The window title + &style, // @pyparm int|style| WS_VISIBLE \| WS_OVERLAPPEDWINDOW|The window style + &obRect, // @pyparm int, int, int, int|rect|None|The default rectangle + &obParent, // @pyparm parent||None|The parent window + &obContext,// @pyparm tuple|createContext|None|A tuple representing a CREATECONTEXT structure. + &szMenuName,// @pyparm string|pszMenuName||The string id for the menu. + &styleEx)) // @pyparm int|styleEx||The extended style of the window being created. + return NULL; + + CCreateContext* pContext = NULL; + if (obContext != Py_None) + { + cc.SetPythonObject(obContext); + pContext = &cc; + } + if (obRect != Py_None) + { + if (!PyArg_ParseTuple(obRect, "iiii", &rect.left, &rect.top, &rect.right,&rect.bottom)) + { + PyErr_Clear(); + RETURN_TYPE_ERR("Rect must be None or a tuple of (iiii)"); + } + } + CFrameWnd *pParent = NULL; + if (obParent != Py_None) + { + pParent = GetFramePtr(obParent); + if (pParent==NULL) + RETURN_TYPE_ERR("The parent window is not a valid PyFrameWnd"); + } + + GUI_BGN_SAVE; + // @pyseemfc CFrameWnd|Create + BOOL ok = pFrame->Create(szClass, szTitle, style, rect,pParent,szMenuName,styleEx,pContext); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CFrameWnd::Create failed"); + RETURN_NONE; +} + +// @pymethod tuple|PyCFrameWnd|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +// @xref +static PyObject * +PyCFrameWnd_pre_create_window(PyObject *self, PyObject *args) +{ + class CProtectedFrameWnd : public CFrameWnd { + public: + BOOL PreCreateWindowBase(CREATESTRUCT &cs) { + return CFrameWnd::PreCreateWindow(cs); + } + }; + + CFrameWnd *pFrame= (CFrameWnd *)GetFramePtr(self); + if (!pFrame)return NULL; + + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + GUI_BGN_SAVE; + BOOL ok = ((CProtectedFrameWnd *)pFrame)->PreCreateWindowBase(cs); + GUI_END_SAVE; + if (!ok)RETURN_ERR("CFrameWnd::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod |PyCFrameWnd|LoadAccelTable|Loads an accelerator table. +static PyObject * +PyCFrameWnd_LoadAccelTable(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *obID; + if (!PyArg_ParseTuple(args, "O", &obID)) + return NULL; + char *res; + if (PyInt_Check(obID)) + res = MAKEINTRESOURCE(PyInt_AsLong(obID)); + else if (PyString_Check(obID)) + res = PyString_AsString(obID); + else + RETURN_TYPE_ERR("The param must be an integer or string"); + GUI_BGN_SAVE; + BOOL ok = pFrame->LoadAccelTable(res); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("LoadAccelTable failed"); + RETURN_NONE; +} +// @pymethod |PyCFrameWnd|LoadFrame|Loads a Windows frame window and associated resources +static PyObject * +ui_frame_load_frame(PyObject *self, PyObject *args) +{ + int idResource = IDR_PYTHONTYPE; + long style = -1; + CPythonMDIChildWnd *pFrame = GetPythonFrame(self); + BOOL bMakeVisible = TRUE; + if (!pFrame) + return NULL; + if (pFrame->m_hWnd!=NULL) + RETURN_ERR("The frame already has a window"); + PythonCreateContext cc; + PyObject *wndParent = Py_None; + PyObject *contextObject = Py_None; + if (!PyArg_ParseTuple(args, "|ilOO:LoadFrame", + &idResource, // @pyparm int|idResource|IDR_PYTHONTYPE|The Id of the resources (menu, icon, etc) for this window + &style, // @pyparm long|style|-1|The window style. Note -1 implies win32con.WS_OVERLAPPEDWINDOW\|win32con.FWS_ADDTOTITLE + &wndParent, // @pyparm |wndParent|None|The parent of the window, or None. + &contextObject)) // @pyparm object|context|None|An object passed to the OnCreateClient for the frame, + return NULL; + if (style==-1) style=WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE; // default frame styles; + CWnd *pParent = NULL; + if (wndParent!=Py_None) { + pParent = GetWndPtrFromParam( wndParent, PyCWnd::type ); + if (pParent==NULL) + return NULL; + } + cc.SetPythonObject(contextObject); +// cc. = idResource + // OnCreateClient will be called during this! + CProtectedWinApp *pApp = GetProtectedApp(); + if (!pApp) return NULL; + BOOL ok; + CWnd *pMain = pApp->GetMainFrame(); + if (pMain==NULL) + RETURN_ERR("There is no main application frame - an MDI child can not be created."); + if (!pMain->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))) + RETURN_ERR("There is no MDI Frame Window available - an MDI child can not be created."); + + GUI_BGN_SAVE; + ok = pFrame->LoadFrame(idResource, style, pParent, &cc); // @pyseemfc CFrameWnd|LoadFrame + GUI_END_SAVE; + if (!ok) { + RETURN_ERR("LoadFrame failed\n"); + // frame will be deleted in PostNcDestroy cleanup + } + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|RecalcLayout|Called by the framework when the standard control bars are toggled on or off or when the frame window is resized. +static PyObject * +ui_frame_recalc_layout(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + BOOL bNotify = TRUE; + if (!PyArg_ParseTuple(args,"|i:RecalcLayout", + &bNotify)) // @pyparm int|bNotify|1|Notify flag + return NULL; + GUI_BGN_SAVE; + pFrame->RecalcLayout(bNotify); // @pyseemfc CFrameWnd|RecalcLayout + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCFrameWnd|EnableDocking|Enable dockable control bars in a frame window +PyObject *PyCFrameWnd_EnableDocking( PyObject *self, PyObject *args ) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + int style; + // @pyparm int|style||Specifies which sides of the frame window can serve as docking sites for control bars. + if (!PyArg_ParseTuple( args, "i:EnableDocking", &style)) + return NULL; + GUI_BGN_SAVE; + pFrame->EnableDocking(style); + GUI_END_SAVE; + RETURN_NONE; + // @comm By default, control bars will be docked to a side of the frame window in the following order: top, bottom, left, right. +} + +// @pymethod |PyCFrameWnd|DockControlBar|Docks a control bar. +static PyObject * +PyCFrameWnd_DockControlBar(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *ob; + int docBarId = 0; + CRect rect(0,0,0,0); + if (!PyArg_ParseTuple(args,"O|i(iiii):DockControlBar", + &ob, // @pyparm |controlBar||The control bar to dock. + &docBarId, // @pyparm int|dockBarId|0|Determines which sides of the frame window to consider for docking. + // @pyparm left, top, right, bottom|int, int, int, int|0,0,0,0|Determines, in screen coordinates, where the control bar will be docked in the nonclient area of the destination frame window. + &rect.left, &rect.top, &rect.right, &rect.bottom)) + return NULL; + CControlBar *pControlBar = PyCControlBar::GetControlBar(ob); + if (pControlBar==NULL) + return NULL; + CRect *pRect = (rect.left==rect.right==0) ? NULL : ▭ + PyObject *rc; + GUI_BGN_SAVE; + __try { + pFrame->DockControlBar(pControlBar, docBarId, pRect); // @pyseemfc CFrameWnd|DockControlBar + rc = Py_None; + Py_INCREF(Py_None); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + rc = NULL; // Cant set Python error till we have the lock back. + } + GUI_END_SAVE; + if (rc==NULL) PyErr_SetString(ui_module_error, "DockControlBar caused exception."); + + return rc; +} + +// @pymethod |PyCFrameWnd|FloatControlBar|Floats a control bar. +static PyObject * +PyCFrameWnd_FloatControlBar(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *ob; + int style = CBRS_ALIGN_TOP; + CPoint pt; + if (!PyArg_ParseTuple(args,"O(ii)|i:FloatControlBar", + &ob, // @pyparm |controlBar||The control bar to dock. + &pt.x, &pt.y, // @pyparm x,y|int, int||he location, in screen coordinates, where the top left corner of the control bar will be placed. + &style)) // @pyparm int|style|CBRS_ALIGN_TOP|Determines which sides of the frame window to consider for docking. + return NULL; + CControlBar *pControlBar = PyCControlBar::GetControlBar(ob); + if (pControlBar==NULL) + return NULL; + GUI_BGN_SAVE; + pFrame->FloatControlBar(pControlBar, pt, style); // @pyseemfc CFrameWnd|FloatControlBar + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCFrameWnd|ShowControlBar|Shows a control bar. +static PyObject * +PyCFrameWnd_ShowControlBar(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *ob; + BOOL bShow, bDelay; + if (!PyArg_ParseTuple(args,"Oii:ShowControlBar", + &ob, // @pyparm |controlBar||The control bar to dock. + &bShow, // @pyparm int|bShow||Show or hide flag. + &bDelay)) // @pyparm int|bDelay||If TRUE, delay showing the control bar. If FALSE, show the control bar immediately. + + return NULL; + CControlBar *pControlBar = PyCControlBar::GetControlBar(ob); + if (pControlBar==NULL) + return NULL; + GUI_BGN_SAVE; + pFrame->ShowControlBar(pControlBar,bShow, bDelay); // @pyseemfc CFrameWnd|ShowControlBar + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|SaveBarState|Saves a control bars settings +static PyObject * +PyCFrameWnd_SaveBarState(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + char *profileName; + if (!PyArg_ParseTuple(args,"s:SaveBarState", + &profileName)) // @pyparm string|profileName||Name of a section in the initialization file or a key in the Windows registry where state information is stored. + return NULL; + GUI_BGN_SAVE; + pFrame->SaveBarState(profileName); // @pyseemfc CFrameWnd|SaveBarState + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|LoadBarState|Loads a control bars settings +static PyObject * +PyCFrameWnd_LoadBarState(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + char *profileName; + if (!PyArg_ParseTuple(args,"s:LoadBarState", + &profileName)) // @pyparm string|profileName||Name of a section in the initialization file or a key in the Windows registry where state information is stored. + return NULL; + GUI_BGN_SAVE; + try { + pFrame->LoadBarState(profileName); // @pyseemfc CFrameWnd|LoadBarState + } + catch (...) { + GUI_BLOCK_THREADS; + RETURN_ERR("LoadBarState failed (with win32 exception!)"); + } + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|BeginModalState|Sets the frame window to modal. +static PyObject * +PyCFrameWnd_BeginModalState( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, BeginModalState ); + GUI_BGN_SAVE; + pFrame->BeginModalState(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod |PyCFrameWnd|EndModalState|Ends the frame window's modal state. Enables all of the windows disabled by . +static PyObject * +PyCFrameWnd_EndModalState( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, EndModalState ); + GUI_BGN_SAVE; + pFrame->EndModalState(); + GUI_END_SAVE; + RETURN_NONE; +} +// @pymethod int|PyCFrameWnd|InModalState|Returns a value indicating whether or not a frame window is in a modal state. +static PyObject * +PyCFrameWnd_InModalState( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, InModalState ); + GUI_BGN_SAVE; + int rc = pFrame->InModalState(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + + +// @pymethod int|PyCFrameWnd|IsTracking|Determines if splitter bar is currently being moved. +static PyObject * +PyCFrameWnd_IsTracking( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, EndModalState ); + GUI_BGN_SAVE; + int rc = pFrame->IsTracking(); + GUI_END_SAVE; + return Py_BuildValue("i", rc); +} + +// @pymethod string|PyCFrameWnd|GetMessageString|Retrieves message corresponding to a command ID. +static PyObject * +PyCFrameWnd_GetMessageString( PyObject *self, PyObject *args) +{ + int id; + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + if (!PyArg_ParseTuple(args, "i", &id )) // @pyparm int|id||The ID to be retrieved + return NULL; + CString csRet; + // @xref + GUI_BGN_SAVE; + pFrame->CFrameWnd::GetMessageString(id, csRet); + GUI_END_SAVE; + return Py_BuildValue("s", (const char *)csRet); +} + +// @pymethod |PyCFrameWnd|GetControlBar|Retrieves the specified control bar. +static PyObject * +PyCFrameWnd_GetControlBar( PyObject *self, PyObject *args) +{ + int id; + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + if (!PyArg_ParseTuple(args, "i", &id )) // @pyparm int|id||The ID of the toolbar to be retrieved + return NULL; + GUI_BGN_SAVE; + CControlBar *pRet = pFrame->GetControlBar(id); + GUI_END_SAVE; + if (pRet==NULL) + RETURN_ERR("There is no control bar with that ID"); + return ui_assoc_object::make(UITypeFromCObject(pRet), pRet)->GetGoodRet(); +} +// @pymethod |PyCFrameWnd|GetMessageBar|Retrieves the message bar for the frame. +static PyObject * +PyCFrameWnd_GetMessageBar( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, GetMessageBar); + GUI_BGN_SAVE; + CWnd *pRet = pFrame->GetMessageBar(); + GUI_END_SAVE; + if (pRet==NULL) + RETURN_ERR("There is no message bar."); + return ui_assoc_object::make(UITypeFromCObject(pRet), pRet)->GetGoodRet(); +} + + +// @pymethod string|PyCFrameWnd|ShowOwnedWindows|Shows all windows that are descendants of the object. +static PyObject * +PyCFrameWnd_ShowOwnedWindows( PyObject *self, PyObject *args) +{ + BOOL bShow = TRUE; + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + if (!PyArg_ParseTuple(args, "|i", &bShow )) // @pyparm int|bShow|1|Flag + return NULL; + CString csRet; + GUI_BGN_SAVE; + pFrame->CFrameWnd::ShowOwnedWindows(bShow); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|SetActiveView|Sets the active view for a frame. +static PyObject * +PyCFrameWnd_SetActiveView(PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *ob; + BOOL bNotify = TRUE; + if (!PyArg_ParseTuple(args,"O|i:SetActiveView", + &ob, // @pyparm |view||The view to set active. + &bNotify)) // @pyparm int|bNotify|1|Specifies whether the view is to be notified of activation. If TRUE, OnActivateView is called for the new view; if FALSE, it is not. + return NULL; + CView *pView = NULL; + if (ob != Py_None) { + pView = PyCView::GetViewPtr(ob); + if (pView==NULL) + return NULL; + } + GUI_BGN_SAVE; + pFrame->SetActiveView(pView, bNotify); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCFrameWnd|GetActiveView|Retrieves the active view. +static PyObject * +PyCFrameWnd_GetActiveView( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, GetActiveView); + GUI_BGN_SAVE; + CView *pRet = pFrame->GetActiveView(); + GUI_END_SAVE; + if (pRet==NULL) + RETURN_ERR("There is no active view."); + return ui_assoc_object::make(UITypeFromCObject(pRet), pRet)->GetGoodRet(); +} + +// @pymethod int|PyCFrameWnd|OnBarCheck|Changes the state of the specified controlbar. +static PyObject * +PyCFrameWnd_OnBarCheck( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + int id; + if (!PyArg_ParseTuple(args, "i:OnBarCheck", &id)) // @pyparm int|id||The control ID of the control bar. + return NULL; + GUI_BGN_SAVE; + long rc = pFrame->OnBarCheck(id); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} + +// @pymethod int|PyCFrameWnd|OnUpdateControlBarMenu|Checks the state of a menu item +static PyObject * +PyCFrameWnd_OnUpdateControlBarMenu( PyObject *self, PyObject *args) +{ + CFrameWnd *pFrame = GetFramePtr(self); + if (!pFrame) + return NULL; + PyObject *obCU; + if (!PyArg_ParseTuple(args, "O:OnUpdateControlBarMenu", &obCU)) // @pyparm |cmdUI||A cmdui object + return NULL; + CCmdUI *pCU = PyCCmdUI::GetCCmdUIPtr(obCU); + if (pCU==NULL) + return NULL; + GUI_BGN_SAVE; + pFrame->OnUpdateControlBarMenu(pCU); + GUI_END_SAVE; + RETURN_NONE; +} + +// @object PyCFrameWnd|A windows frame window. Encapsulates an MFC class. Derived from a object. +static struct PyMethodDef PyCFrameWnd_methods[] = { + {"BeginModalState", PyCFrameWnd_BeginModalState, 1}, // @pymeth BeginModalState|Sets the frame window to modal. + {"CreateWindow", PyCFrameWnd_CreateWindow,1}, // @pymeth CreateWindow|Creates the underlying window for the object. + {"EndModalState", PyCFrameWnd_EndModalState, 1}, // @pymeth EndModalState|Ends the frame window's modal state. Enables all of the windows disabled by . + {"DockControlBar", PyCFrameWnd_DockControlBar, 1}, // @pymeth DockControlBar|Docks a control bar. + {"EnableDocking", PyCFrameWnd_EnableDocking, 1}, // @pymeth EnableDocking|Enable dockable control bars in a frame window + {"FloatControlBar", PyCFrameWnd_FloatControlBar, 1}, // @pymeth FloatControlBar|Floats a control bar. + {"GetActiveDocument", ui_frame_get_active_document, 1}, // @pymeth GetActiveDocument|Returns the currently active document + {"GetControlBar", PyCFrameWnd_GetControlBar, 1}, // @pymeth GetControlBar|Retrieves the specified control bar. + {"GetMessageString", PyCFrameWnd_GetMessageString, 1}, // @pymeth GetMessageString|Retrieves message corresponding to a command ID. + {"GetMessageBar", PyCFrameWnd_GetMessageBar, 1}, // @pymeth GetMessageBar|Retrieves the message bar for the frame. + {"IsTracking", PyCFrameWnd_IsTracking, 1}, // @pymeth IsTracking|Determines if splitter bar is currently being moved. + {"InModalState", PyCFrameWnd_InModalState, 1}, // @pymeth InModalState|Returns a value indicating whether or not a frame window is in a modal state. + {"LoadAccelTable", PyCFrameWnd_LoadAccelTable, 1}, // @pymeth LoadAccelTable|Loads an accelerator table. + {"LoadFrame", ui_frame_load_frame, 1}, // @pymeth LoadFrame|Creates the MDI Window's frame + {"LoadBarState", PyCFrameWnd_LoadBarState, 1}, // @pymeth LoadBarState|Loads a control bars settings + {"PreCreateWindow", PyCFrameWnd_pre_create_window,1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"SaveBarState", PyCFrameWnd_SaveBarState, 1}, // @pymeth SaveBarState|Saves a control bars settings + {"ShowControlBar", PyCFrameWnd_ShowControlBar, 1}, // @pymeth ShowControlBar|Shows a control bar. + {"RecalcLayout", ui_frame_recalc_layout, 1}, // @pymeth RecalcLayout|Called by the framework when the standard control bars are toggled on or off or when the frame window is resized. + {"GetActiveView", PyCFrameWnd_GetActiveView, 1}, // @pymeth GetActiveView|Retrieves the active view. + {"OnBarCheck", PyCFrameWnd_OnBarCheck, 1}, // @pymeth OnBarCheck|Changes the state of the specified controlbar. + {"OnUpdateControlBarMenu",PyCFrameWnd_OnUpdateControlBarMenu, 1}, // @pymeth OnUpdateControlBarMenu|Checks the state of a menu item + {"SetActiveView", PyCFrameWnd_SetActiveView, 1}, // @pymeth SetActiveView|Sets the active view for a frame. + {NULL, NULL} +}; + +ui_type_CObject PyCFrameWnd::type("PyCFrameWnd", + &PyCWnd::type, + RUNTIME_CLASS(CFrameWnd), + sizeof(PyCFrameWnd), + PyCFrameWnd_methods, + GET_PY_CTOR(PyCFrameWnd)); + +// @pymethod tuple|PyCMDIFrameWnd|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +static PyObject * +ui_mdi_frame_window_pre_create_window(PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pWnd = GetMDIFrame(self); + if (!pWnd) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + // @xref + BOOL ok; + GUI_BGN_SAVE; + ok = pWnd->CMDIFrameWnd::PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CMDIFrameWnd::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod None|PyCMDIFrameWnd|OnContextHelp|Calls the underlying MFC OnContextHelp method. +PyObject * +ui_mdi_frame_on_context_help(PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pWnd = GetMDIFrame(self); + if (!pWnd) + return NULL; + GUI_BGN_SAVE; + pWnd->CMDIFrameWnd::OnContextHelp(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod (, int)|PyCMDIFrameWnd|MDIGetActive|Retrieves the current active MDI child window, along with a flag indicating whether the child window is maximized. +static PyObject * +ui_mdi_frame_window_mdi_get_active( PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pFrame = GetMDIFrame(self); + if (!pFrame) + return NULL; + CHECK_NO_ARGS2(args, MDIGetActive); + BOOL bIsMax; + GUI_BGN_SAVE; + CMDIChildWnd *pRet = pFrame->MDIGetActive(&bIsMax); + GUI_END_SAVE; + if (pRet==NULL) + RETURN_ERR("There is no active window."); + PyObject *obui = ui_assoc_object::make(UITypeFromCObject(pRet), pRet)->GetGoodRet(); + if (obui==NULL) + return NULL; + PyObject *ret = Py_BuildValue("Oi", obui, bIsMax); + Py_XDECREF(obui); + return ret; +} + +// @pymethod |PyCMDIFrameWnd|MDINext|Activates the next MDI window +static PyObject * +ui_mdi_frame_window_mdi_next( PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pFrame = GetMDIFrame(self); + if (!pFrame) + return NULL; + // @comm Unlike MFC, this version supports the fNext param in the WM_MDINEXT message. + // @pyparm int|fNext|0|Indicates if the next (0) or previous (non-zero) window is requested. + int fnext = 0; + if (!PyArg_ParseTuple(args, "i", &fnext)) + return NULL; + GUI_BGN_SAVE; + :: SendMessage(pFrame->m_hWndMDIClient, WM_MDINEXT, 0, fnext); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCMDIFrameWnd|GetMDIClient|Returns the MDI client window +static PyObject * +ui_mdi_frame_get_mdi_client( PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pFrame = GetMDIFrame(self); + if (!pFrame)return NULL; + CHECK_NO_ARGS2(args, GetMDIClient); + + if(!pFrame->m_hWndMDIClient) + RETURN_ERR("MDIGetClient call but MDIFrameWnd has not been created"); + + CWnd *pWnd = CWnd::FromHandle(pFrame->m_hWndMDIClient); + if (pWnd==NULL) + RETURN_ERR("The window handle is invalid."); + return PyCWnd::make( UITypeFromCObject(pWnd), pWnd)->GetGoodRet(); +} + +// @pymethod |PyCMDIFrameWnd|MDIActivate|Activate an MDI child window +static PyObject * +ui_mdi_frame_mdi_activate( PyObject *self, PyObject *args) +{ + CMDIFrameWnd *pFrame = GetMDIFrame(self); + if (!pFrame)return NULL; + + PyObject *ob; + if (!PyArg_ParseTuple(args,"O:MDIActivate", + &ob)) // @pyparm |window||The window to activate. + return NULL; + + CWnd* pWndActivate = GetWndPtr(ob); + if(!pWndActivate) + RETURN_ERR("Argument is not a valid PyCWnd"); + + GUI_BGN_SAVE; + pFrame->MDIActivate(pWndActivate); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCMDIFrameWnd|PreTranslateMessage|Calls the base PreTranslateMessage handler +static PyObject * +ui_mdi_frame_pre_translate_message(PyObject *self, PyObject *args) +{ + // @xref + CMDIFrameWnd *pFrame = GetMDIFrame(self); + if (!pFrame) + return NULL; + if (!pFrame->IsKindOf(RUNTIME_CLASS(CPythonMDIFrameWnd))) + RETURN_TYPE_ERR("The frame window is not a Python frame"); + CPythonMDIFrameWnd *pWnd = (CPythonMDIFrameWnd *)pFrame; + MSG _msg; MSG *msg=&_msg; + if (!PyArg_ParseTuple(args, "(iiiii(ii))", &msg->hwnd,&msg->message,&msg->wParam,&msg->lParam,&msg->time,&msg->pt.x,&msg->pt.y)) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pWnd->_BasePreTranslateMessage(msg); + GUI_END_SAVE; + return Py_BuildValue("i(iiiii(ii))", rc, msg->hwnd,msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); +} + +// @pymethod |PyCMDIFrameWnd|OnCommand|Calls the standard Python framework OnCommand handler +static PyObject * +ui_mdi_frame_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonMDIFrameWnd, &PyCMDIFrameWnd::type, self, args); +} + +// @pymethod |PyCMDIFrameWnd|OnClose|Calls the standard Python framework OnClose handler +static PyObject * +ui_mdi_frame_on_close(PyObject *self, PyObject *args) +{ + return DoBaseOnClose(CPythonMDIFrameWnd, &PyCMDIFrameWnd::type, self, args); +} + +// @object PyCMDIFrameWnd|A main application frame window. Encapsulates an MFC class +static struct PyMethodDef PyCMDIFrameWnd_methods[] = { + {"GetMDIClient",ui_mdi_frame_get_mdi_client,1}, // @pymeth GetMDIClient|Returns the MDI client window + {"MDIGetActive", ui_mdi_frame_window_mdi_get_active, 1}, // @pymeth MDIGetActive|Retrieves the current active MDI child window, along with a flag indicating whether the child window is maximized. + {"MDIActivate",ui_mdi_frame_mdi_activate,1}, // @pymeth MDIActivate|Activate an MDI child window + {"MDINext", ui_mdi_frame_window_mdi_next, 1}, // @pymeth MDINext|Activates the next MDI window + {"PreCreateWindow", ui_mdi_frame_window_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"PreTranslateMessage", ui_mdi_frame_pre_translate_message, 1}, // @pymeth PreTranslateMessage|Calls the underlying MFC PreTranslateMessage method. + {"OnCommand", ui_mdi_frame_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {"OnContextHelp", ui_mdi_frame_on_context_help, 1}, // @pymeth OnContextHelp|Calls the underlying MFC OnContextHelp method. + {"OnClose", ui_mdi_frame_on_close, 1}, // @pymeth OnClose|Calls the standard Python framework OnClose handler + {NULL, NULL} +}; + +ui_type_CObject PyCMDIFrameWnd::type("PyCMDIFrameWnd", + &PyCFrameWnd::type, + RUNTIME_CLASS(CMDIFrameWnd), + sizeof(PyCMDIFrameWnd), + PyCMDIFrameWnd_methods, + GET_PY_CTOR(PyCMDIFrameWnd)); + +// @pymethod |PyCMDIChildWnd|ActivateFrame|Calls the underlying MFC ActivateFrame method. +static PyObject * +ui_mdi_child_window_activate_frame(PyObject *self, PyObject *args) +{ + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd) + return NULL; + int cmdShow = -1; + //@pyparm int|cmdShow|-1|The status of the window. + if (!PyArg_ParseTuple(args, "|i:ActivateFrame", &cmdShow)) + return NULL; + // @xref + GUI_BGN_SAVE; + pWnd->CMDIChildWnd::ActivateFrame(cmdShow); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod tuple|PyCMDIChildWnd|PreCreateWindow|Calls the underlying MFC PreCreateWindow method. +static PyObject * +ui_mdi_child_window_pre_create_window(PyObject *self, PyObject *args) +{ + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd) + return NULL; + CREATESTRUCT cs; + //@pyparm tuple|createStruct||A tuple representing a CREATESTRUCT structure. + if (!CreateStructFromPyObject( &cs, args, "PreCreateWindow", TRUE)) + return NULL; + + // @xref + BOOL ok; + GUI_BGN_SAVE; + ok = pWnd->CMDIChildWnd::PreCreateWindow(cs); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CMDIChildWnd::PreCreateWindow failed"); + return PyObjectFromCreateStruct(&cs); +} + +// @pymethod tuple|PyCMDIChildWnd|CreateWindow|Creates the actual window for the PyCWnd object. +// @comm You do not need to call this method if you use the MFC Document/View framework. +static PyObject * +ui_mdi_child_window_create_window(PyObject *self, PyObject *args) +{ + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd) + return NULL; + PythonCreateContext cc; + RECT rect = CMDIChildWnd::rectDefault; + PyObject *obRect = Py_None; + PyObject *obParent = Py_None; + PyObject *obContext = Py_None; + char *szClass, *szTitle; + DWORD style = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW; + if (!PyArg_ParseTuple(args, "zs|lOOO:CreateWindow", + &szClass, // @pyparm string|wndClass||The window class name, or None + &szTitle, // @pyparm string|title||The window title + &style, // @pyparm int|style|WS_CHILD \| WS_VISIBLE \| WS_OVERLAPPEDWINDOW|The window style + &obRect, // @pyparm int, int, int, int|rect|None|The default rectangle + &obParent, // @pyparm parent||None|The parent window + &obContext))// @pyparm tuple|createContext|None|A tuple representing a CREATECONTEXT structure. + return NULL; + + CCreateContext* pContext = NULL; + if (obContext != Py_None) { + cc.SetPythonObject(obContext); + pContext = &cc; + } + if (obRect != Py_None) { + if (!PyArg_ParseTuple(obRect, "iiii", &rect.left, &rect.top, &rect.right, &rect.bottom)) { + PyErr_Clear(); + RETURN_TYPE_ERR("Rect must be None or a tuple of (iiii)"); + } + } + CMDIFrameWnd *pParent = NULL; + if (obParent != Py_None) { + pParent = GetMDIFrame( obParent ); + if (pParent==NULL) + RETURN_TYPE_ERR("The parent window is not a valid PyCMDIFrameWnd"); + } + + GUI_BGN_SAVE; + BOOL ok = pWnd->Create(szClass, szTitle, style, rect, pParent, pContext); + GUI_END_SAVE; + if (!ok) + RETURN_ERR("CMDIChildWnd::Create"); + RETURN_NONE; +} + +// @pymethod |PyCMDIChildWnd|GetMDIFrame|Returns the MDI parent frame +static PyObject * +ui_mdi_child_window_get_mdi_frame(PyObject *self, PyObject *args) +{ + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd)return NULL; + CHECK_NO_ARGS2(args,GetMDIFrame); + + GUI_BGN_SAVE; + CMDIFrameWnd* pFrame=pWnd->GetMDIFrame(); + GUI_END_SAVE; + + return ui_assoc_object::make(UITypeFromCObject(pFrame), pFrame)->GetGoodRet(); +} + +// @pymethod |PyCMDIChildWnd|MDIActivate|Activates the MDI frame independent of the main frame. +static PyObject * +ui_mdi_child_window_mdi_activate_frame(PyObject *self, PyObject *args) +{ + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd) + return NULL; + //@pyparm int|cmdShow|-1|The status of the window. + if (!PyArg_ParseTuple(args, ":MDIActivate")) + return NULL; + // @xref + GUI_BGN_SAVE; + pWnd->MDIActivate(); + GUI_END_SAVE; + RETURN_NONE; +} + +// @pymethod |PyCMDIChildWnd|PreTranslateMessage|Calls the base PreTranslateMessage handler +static PyObject * +ui_mdi_child_window_pre_translate_message(PyObject *self, PyObject *args) +{ + // @xref + CPythonMDIChildWnd *pWnd = GetPythonFrame(self); + if (!pWnd) + return NULL; + MSG _msg; MSG *msg=&_msg; + if (!PyArg_ParseTuple(args, "(iiiii(ii))", &msg->hwnd,&msg->message,&msg->wParam,&msg->lParam,&msg->time,&msg->pt.x,&msg->pt.y)) + return NULL; + GUI_BGN_SAVE; + BOOL rc = pWnd->_BasePreTranslateMessage(msg); + GUI_END_SAVE; + return Py_BuildValue("i(iiiii(ii))", rc, msg->hwnd,msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); +} + +// @pymethod |PyCMDIChildWnd|OnCommand|Calls the standard Python framework OnCommand handler +static PyObject * +ui_mdi_child_window_on_command(PyObject *self, PyObject *args) +{ + // @xref + // @pyparm int|wparam|| + // @pyparm int|lparam|| + return DoBaseOnCommand(CPythonMDIChildWnd, &PyCMDIChildWnd::type, self, args); +} + +// @pymethod |PyCMDIChildWnd|OnClose|Calls the standard Python framework OnClose handler +static PyObject * +ui_mdi_child_window_on_close(PyObject *self, PyObject *args) +{ + // @xref + return DoBaseOnClose(CPythonMDIChildWnd, &PyCMDIChildWnd::type, self, args); +} + + +// @object PyCMDIChildWnd|A windows frame window. Encapsulates an MFC class +static struct PyMethodDef PyCMDIChildWnd_methods[] = { + {"ActivateFrame", ui_mdi_child_window_activate_frame, 1}, // @pymeth ActivateFrame|Calls the underlying MFC ActivateFrame method. + {"CreateWindow", ui_mdi_child_window_create_window, 1}, // @pymeth CreateWindow|Creates the actual window for the PyCWnd object. + {"GetMDIFrame",ui_mdi_child_window_get_mdi_frame,1}, // @pymeth GetMDIFrame|Returns the MDI parent frame + {"MDIActivate", ui_mdi_child_window_mdi_activate_frame, 1}, // @pymeth MDIActivate|Activates the MDI frame independent of the main frame. + {"PreCreateWindow", ui_mdi_child_window_pre_create_window, 1}, // @pymeth PreCreateWindow|Calls the underlying MFC PreCreateWindow method. + {"PreTranslateMessage", ui_mdi_child_window_pre_translate_message, 1}, // @pymeth PreTranslateMessage|Calls the underlying MFC PreTranslateMessage method. + {"OnCommand", ui_mdi_child_window_on_command, 1}, // @pymeth OnCommand|Calls the standard Python framework OnCommand handler + {"OnClose", ui_mdi_child_window_on_close, 1}, // @pymeth OnClose|Calls the standard Python framework OnClose handler + {NULL, NULL} +}; + +ui_type_CObject PyCMDIChildWnd::type("PyCMDIChildWnd", + &PyCFrameWnd::type, + RUNTIME_CLASS(CMDIChildWnd), + sizeof(PyCMDIChildWnd), + PyCMDIChildWnd_methods, + GET_PY_CTOR(PyCMDIChildWnd)); diff --git a/Pythonwin/win32win.h b/Pythonwin/win32win.h new file mode 100644 index 0000000000..3a06ce3e15 --- /dev/null +++ b/Pythonwin/win32win.h @@ -0,0 +1,146 @@ +// win32win.h - support for Python/MFC Windows objects. + +// +// Window Objects +// +class PYW_EXPORT PyCWnd : public PyCCmdTarget{ +public: // probably shouldnt be, but... + MAKE_PY_CTOR(PyCWnd) + CMapWordToPtr *pMessageHookList; + CMapWordToPtr *pKeyHookList; + PyObject *obKeyStrokeHandler; + BOOL bDidSubclass; + + // ensure that the pointer is permanent + static PyCWnd *make( ui_type_CObject &makeType, CWnd *pSearch, HWND wnd=NULL ); +protected: + PyCWnd(); + ~PyCWnd(); + virtual void DoKillAssoc( BOOL bDestructing = FALSE ); +public: + static CWnd *GetPythonGenericWnd(PyObject *self, ui_type_CObject *pType = &type); + static PyObject *get_window(PyObject *self, PyObject *args); + static PyObject *get_top_window(PyObject *self, PyObject *args); + + static PyObject *FindWindow(PyObject *self, PyObject *args); + static PyObject *FindWindowEx(PyObject *self, PyObject *args); + static PyObject *CreateWindowFromHandle(PyObject *self, PyObject *args); + static PyObject *CreateControl(PyObject *self, PyObject *args); + static PyObject *GetActiveWindow(PyObject *self, PyObject *args); + static PyObject *GetForegroundWindow(PyObject *self, PyObject *args); + static PyObject *GetFocus(PyObject *self, PyObject *args); + + + // virtuals for Python support + virtual CString repr(); + + BOOL check_key_stroke(UINT ch); + + static ui_type_CObject type; +}; + +// +// views +// +class PYW_EXPORT PyCView : public PyCWnd { +protected: + PyCView() {} + ~PyCView(){} +public: + static CView *GetViewPtr(PyObject *self); + static ui_type_CObject type; +}; + +class PYW_EXPORT PyCScrollView : public PyCView { +protected: + PyCScrollView() {} + ~PyCScrollView(){} +public: + static PyObject *create(PyObject *self, PyObject *args); + static CScrollView *GetViewPtr(PyObject *self); + static ui_type_CObject type; + MAKE_PY_CTOR(PyCScrollView) +}; + +class PYW_EXPORT PyCFormView : public PyCView { +protected: +public: + static PyObject *create(PyObject *self, PyObject *args); + static ui_type_CObject type; + MAKE_PY_CTOR(PyCFormView) +}; + +// +// frame windows +// +class PYW_EXPORT PyCFrameWnd : public PyCWnd { +protected: + PyCFrameWnd() {} + ~PyCFrameWnd(){} +public: + MAKE_PY_CTOR(PyCFrameWnd) + static ui_type_CObject type; +}; + +class PYW_EXPORT PyCMDIFrameWnd : public PyCFrameWnd { +protected: + PyCMDIFrameWnd() {return;} +public: + MAKE_PY_CTOR(PyCMDIFrameWnd) + static ui_type_CObject type; +}; + +class PYW_EXPORT PyCMDIChildWnd : public PyCFrameWnd { +protected: + PyCMDIChildWnd() {return;} +public: + MAKE_PY_CTOR(PyCMDIChildWnd) +// static PyObject *create(PyObject *self, PyObject *args); + static ui_type_CObject type; +}; + +extern PYW_EXPORT CWnd *GetWndPtr(PyObject *); + +///////////////////////////////////////////////////////// +// +// Additional helpers +// +// Allow objects to simply provide OnCommand to call the +// base handler without recursing to death. +template +PyObject *__DoBaseOnCommand(ui_type_CObject *type, PyObject *self, PyObject *args) +{ + CObject *ob = (CObject *)ui_assoc_CObject::GetGoodCppObject( self, type ); + if (ob==NULL) { + PyErr_Clear(); + RETURN_TYPE_ERR("The object is not a Python MFC object"); + } + long wparam, lparam; + if (!PyArg_ParseTuple(args, "ll", &wparam, &lparam)) + return NULL; + GUI_BGN_SAVE; + ClassFramework *pcf = (ClassFramework *)ob; + BOOL rc = pcf->_BaseOnCommand(wparam, lparam); + GUI_END_SAVE; + return PyInt_FromLong(rc); +} +#define DoBaseOnCommand(Class, type, self, args) \ + __DoBaseOnCommand(type, self, args) + +template +PyObject *__DoBaseOnClose(ui_type_CObject *type, PyObject *self, PyObject *args) +{ + CObject *ob = (CObject *)ui_assoc_CObject::GetGoodCppObject( self, type ); + if (ob==NULL) { + PyErr_Clear(); + RETURN_TYPE_ERR("The object is not a Python MFC object"); + } + if (!PyArg_ParseTuple(args, "")) return NULL; + GUI_BGN_SAVE; + ClassFramework *pcf = (ClassFramework *)ob; + pcf->_BaseOnClose(); + GUI_END_SAVE; + RETURN_NONE; +} +#define DoBaseOnClose(Class, type, self, args) \ + __DoBaseOnClose(type, self, args)