bool wxNotebook::SetPageText(size_t nPage, const wxString& strText) { wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("notebook page out of range") ); TC_ITEM tcItem; tcItem.mask = TCIF_TEXT; tcItem.pszText = wxMSW_CONV_LPTSTR(strText); if ( !HasFlag(wxNB_MULTILINE) ) return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0; // multiline - we need to set new page size if a line is added or removed int rows = GetRowCount(); bool ret = TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0; if ( ret && rows != GetRowCount() ) { const wxRect r = GetPageSize(); const size_t count = m_pages.Count(); for ( size_t page = 0; page < count; page++ ) m_pages[page]->SetSize(r); } return ret; }
void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label) { wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") ); m_menus[pos]->wxMenuBase::SetTitle(label); if ( !IsAttached() ) { return; } //else: have to modify the existing menu int mswpos = MSWPositionForWxMenu(GetMenu(pos),pos); UINT_PTR id; UINT flagsOld = ::GetMenuState((HMENU)m_hMenu, mswpos, MF_BYPOSITION); if ( flagsOld == 0xFFFFFFFF ) { wxLogLastError(wxT("GetMenuState")); return; } if ( flagsOld & MF_POPUP ) { // HIBYTE contains the number of items in the submenu in this case flagsOld &= 0xff; id = (UINT_PTR)::GetSubMenu((HMENU)m_hMenu, mswpos); } else { id = pos; } #ifdef __WXWINCE__ WinStruct<MENUITEMINFO> info; info.fMask = MIIM_TYPE; info.fType = MFT_STRING; info.cch = label.length(); info.dwTypeData = wxMSW_CONV_LPTSTR(label); if ( !SetMenuItemInfo(GetHmenu(), id, TRUE, &info) ) { wxLogLastError(wxT("SetMenuItemInfo")); } #else if ( ::ModifyMenu(GetHmenu(), mswpos, MF_BYPOSITION | MF_STRING | flagsOld, id, label.t_str()) == (int)0xFFFFFFFF ) { wxLogLastError(wxT("ModifyMenu")); } #endif Refresh(); }
void wxToolTip::DoSetTip(WXHWND hWnd) { // update the tip text shown by the control wxToolInfo ti((HWND)hWnd, m_id, m_rect); // for some reason, changing the tooltip text directly results in // repaint of the controls under it, see #10520 -- but this doesn't // happen if we reset it first ti.lpszText = const_cast<wxChar *>(wxT("")); (void)SendTooltipMessage(GetToolTipCtrl(), TTM_UPDATETIPTEXT, &ti); ti.lpszText = wxMSW_CONV_LPTSTR(m_text); (void)SendTooltipMessage(GetToolTipCtrl(), TTM_UPDATETIPTEXT, &ti); }
void wxToolTip::DoAddHWND(WXHWND hWnd) { HWND hwnd = (HWND)hWnd; wxToolInfo ti(hwnd, m_id, m_rect); // another possibility would be to specify LPSTR_TEXTCALLBACK here as we // store the tooltip text ourselves anyhow, and provide it in response to // TTN_NEEDTEXT (sent via WM_NOTIFY), but then we would be limited to 79 // character tooltips as this is the size of the szText buffer in // NMTTDISPINFO struct -- and setting the tooltip here we can have tooltips // of any length ti.hwnd = hwnd; ti.lpszText = wxMSW_CONV_LPTSTR(m_text); if ( !SendTooltipMessage(GetToolTipCtrl(), TTM_ADDTOOL, &ti) ) { wxLogDebug(wxT("Failed to create the tooltip '%s'"), m_text.c_str()); return; } #ifdef TTM_SETMAXTIPWIDTH if ( !AdjustMaxWidth() ) #endif // TTM_SETMAXTIPWIDTH { // replace the '\n's with spaces because otherwise they appear as // unprintable characters in the tooltip string m_text.Replace(wxT("\n"), wxT(" ")); ti.lpszText = wxMSW_CONV_LPTSTR(m_text); if ( !SendTooltipMessage(GetToolTipCtrl(), TTM_ADDTOOL, &ti) ) { wxLogDebug(wxT("Failed to create the tooltip '%s'"), m_text.c_str()); } } }
long wxExecute(const wxString& cmd, int flags, wxProcess *handler, const wxExecuteEnv *env) { wxCHECK_MSG( !cmd.empty(), 0, wxT("empty command in wxExecute") ); #if wxUSE_THREADS // for many reasons, the code below breaks down if it's called from another // thread -- this could be fixed, but as Unix versions don't support this // neither I don't want to waste time on this now wxASSERT_MSG( wxThread::IsMain(), wxT("wxExecute() can be called only from the main thread") ); #endif // wxUSE_THREADS wxString command; #if wxUSE_IPC // DDE hack: this is really not pretty, but we need to allow this for // transparent handling of DDE servers in wxMimeTypesManager. Usually it // returns the command which should be run to view/open/... a file of the // given type. Sometimes, however, this command just launches the server // and an additional DDE request must be made to really open the file. To // keep all this well hidden from the application, we allow a special form // of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which // case we execute just <command> and process the rest below wxString ddeServer, ddeTopic, ddeCommand; static const size_t lenDdePrefix = 7; // strlen("WX_DDE:") if ( cmd.Left(lenDdePrefix) == wxT("WX_DDE#") ) { // speed up the concatenations below ddeServer.reserve(256); ddeTopic.reserve(256); ddeCommand.reserve(256); const wxChar *p = cmd.c_str() + 7; #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( *p && *p != wxT('#') ) { command += *p++; } if ( *p ) { // skip '#' p++; } else { wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( *p && *p != wxT('#') ) { ddeServer += *p++; } if ( *p ) { // skip '#' p++; } else { wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( *p && *p != wxT('#') ) { ddeTopic += *p++; } if ( *p ) { // skip '#' p++; } else { wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( *p ) { ddeCommand += *p++; } // if we want to just launch the program and not wait for its // termination, try to execute DDE command right now, it can succeed if // the process is already running - but as it fails if it's not // running, suppress any errors it might generate if ( !(flags & wxEXEC_SYNC) ) { wxLogNull noErrors; if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) ) { // a dummy PID - this is a hack, of course, but it's well worth // it as we don't open a new server each time we're called // which would be quite bad return -1; } } } else #endif // wxUSE_IPC { // no DDE command = cmd; } // the IO redirection is only supported with wxUSE_STREAMS BOOL redirect = FALSE; #if wxUSE_STREAMS wxPipe pipeIn, pipeOut, pipeErr; // open the pipes to which child process IO will be redirected if needed if ( handler && handler->IsRedirected() ) { // create pipes for redirecting stdin, stdout and stderr if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() ) { wxLogSysError(_("Failed to redirect the child process IO")); // indicate failure: we need to return different error code // depending on the sync flag return flags & wxEXEC_SYNC ? -1 : 0; } redirect = TRUE; } #endif // wxUSE_STREAMS // create the process STARTUPINFO si; wxZeroMemory(si); si.cb = sizeof(si); #if wxUSE_STREAMS if ( redirect ) { si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = pipeIn[wxPipe::Read]; si.hStdOutput = pipeOut[wxPipe::Write]; si.hStdError = pipeErr[wxPipe::Write]; // We must set the handles to those sides of std* pipes that we won't // in the child to be non-inheritable. We must do this before launching // the child process as otherwise these handles will be inherited by // the child which will never close them and so the pipe will not // return ERROR_BROKEN_PIPE if the parent or child exits unexpectedly // causing the remaining process to potentially become deadlocked in // ReadFile() or WriteFile(). if ( !::SetHandleInformation(pipeIn[wxPipe::Write], HANDLE_FLAG_INHERIT, 0) ) wxLogLastError(wxT("SetHandleInformation(pipeIn)")); if ( !::SetHandleInformation(pipeOut[wxPipe::Read], HANDLE_FLAG_INHERIT, 0) ) wxLogLastError(wxT("SetHandleInformation(pipeOut)")); if ( !::SetHandleInformation(pipeErr[wxPipe::Read], HANDLE_FLAG_INHERIT, 0) ) wxLogLastError(wxT("SetHandleInformation(pipeErr)")); } #endif // wxUSE_STREAMS // The default logic for showing the console is to show it only if the IO // is not redirected however wxEXEC_{SHOW,HIDE}_CONSOLE flags can be // explicitly specified to change it. if ( (flags & wxEXEC_HIDE_CONSOLE) || (redirect && !(flags & wxEXEC_SHOW_CONSOLE)) ) { si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; } PROCESS_INFORMATION pi; DWORD dwFlags = CREATE_SUSPENDED; if ( (flags & wxEXEC_MAKE_GROUP_LEADER) ) dwFlags |= CREATE_NEW_PROCESS_GROUP; dwFlags |= CREATE_DEFAULT_ERROR_MODE ; wxWxCharBuffer envBuffer; bool useCwd = false; if ( env ) { useCwd = !env->cwd.empty(); // Translate environment variable map into NUL-terminated list of // NUL-terminated strings. if ( !env->env.empty() ) { #if wxUSE_UNICODE // Environment variables can contain non-ASCII characters. We could // check for it and not use this flag if everything is really ASCII // only but there doesn't seem to be any reason to do it so just // assume Unicode by default. dwFlags |= CREATE_UNICODE_ENVIRONMENT; #endif // wxUSE_UNICODE wxEnvVariableHashMap::const_iterator it; size_t envSz = 1; // ending '\0' #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ for ( it = env->env.begin(); it != env->env.end(); ++it ) { // Add size of env variable name and value, and '=' char and // ending '\0' envSz += it->first.length() + it->second.length() + 2; } envBuffer.extend(envSz); wxChar *p = envBuffer.data(); #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ for ( it = env->env.begin(); it != env->env.end(); ++it ) { const wxString line = it->first + wxS("=") + it->second; // Include the trailing NUL which will always terminate the // buffer returned by t_str(). const size_t len = line.length() + 1; wxTmemcpy(p, line.t_str(), len); p += len; } // And another NUL to terminate the list of NUL-terminated strings. *p = 0; } } // Translate wxWidgets priority to Windows conventions. if ( handler ) { unsigned prio = handler->GetPriority(); if ( prio <= 20 ) dwFlags |= IDLE_PRIORITY_CLASS; else if ( prio <= 40 ) dwFlags |= BELOW_NORMAL_PRIORITY_CLASS; else if ( prio <= 60 ) dwFlags |= NORMAL_PRIORITY_CLASS; else if ( prio <= 80 ) dwFlags |= ABOVE_NORMAL_PRIORITY_CLASS; else if ( prio <= 99 ) dwFlags |= HIGH_PRIORITY_CLASS; else if ( prio <= 100 ) dwFlags |= REALTIME_PRIORITY_CLASS; else { wxFAIL_MSG(wxT("invalid value of thread priority parameter")); dwFlags |= NORMAL_PRIORITY_CLASS; } } bool ok = ::CreateProcess ( NULL, // application name (use only cmd line) wxMSW_CONV_LPTSTR(command), // full command line NULL, // security attributes: defaults for both NULL, // the process and its main thread redirect, // inherit handles if we use pipes dwFlags, // process creation flags envBuffer.data(), // environment (may be NULL which is fine) useCwd // initial working directory ? wxMSW_CONV_LPTSTR(env->cwd) : NULL, // (or use the same) &si, // startup info (unused here) &pi // process info ) != 0; #if wxUSE_STREAMS // we can close the pipe ends used by child anyhow if ( redirect ) { ::CloseHandle(pipeIn.Detach(wxPipe::Read)); ::CloseHandle(pipeOut.Detach(wxPipe::Write)); ::CloseHandle(pipeErr.Detach(wxPipe::Write)); } #endif // wxUSE_STREAMS if ( !ok ) { #if wxUSE_STREAMS // close the other handles too if ( redirect ) { ::CloseHandle(pipeIn.Detach(wxPipe::Write)); ::CloseHandle(pipeOut.Detach(wxPipe::Read)); ::CloseHandle(pipeErr.Detach(wxPipe::Read)); } #endif // wxUSE_STREAMS wxLogSysError(_("Execution of command '%s' failed"), command.c_str()); return flags & wxEXEC_SYNC ? -1 : 0; } #if wxUSE_STREAMS // the input buffer bufOut is connected to stdout, this is why it is // called bufOut and not bufIn wxStreamTempInputBuffer bufOut, bufErr; if ( redirect ) { // We can now initialize the wxStreams wxPipeInputStream * outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read)); wxPipeInputStream * errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read)); wxPipeOutputStream * inStream = new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write)); handler->SetPipeStreams(outStream, inStream, errStream); bufOut.Init(outStream); bufErr.Init(errStream); } #endif // wxUSE_STREAMS // create a hidden window to receive notification about process // termination HWND hwnd = wxCreateHiddenWindow ( &gs_classForHiddenWindow, wxMSWEXEC_WNDCLASSNAME, (WNDPROC)wxExecuteWindowCbk ); wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") ); // Alloc data wxExecuteData *data = new wxExecuteData; data->hProcess = pi.hProcess; data->dwProcessId = pi.dwProcessId; data->hWnd = hwnd; data->state = (flags & wxEXEC_SYNC) != 0; if ( flags & wxEXEC_SYNC ) { // handler may be !NULL for capturing program output, but we don't use // it wxExecuteData struct in this case data->handler = NULL; } else { // may be NULL or not data->handler = handler; if (handler) handler->SetPid(pi.dwProcessId); } DWORD tid; HANDLE hThread = ::CreateThread(NULL, 0, wxExecuteThread, (void *)data, 0, &tid); // resume process we created now - whether the thread creation succeeded or // not if ( ::ResumeThread(pi.hThread) == (DWORD)-1 ) { // ignore it - what can we do? wxLogLastError(wxT("ResumeThread in wxExecute")); } // close unneeded handle if ( !::CloseHandle(pi.hThread) ) { wxLogLastError(wxT("CloseHandle(hThread)")); } if ( !hThread ) { wxLogLastError(wxT("CreateThread in wxExecute")); DestroyWindow(hwnd); delete data; // the process still started up successfully... return pi.dwProcessId; } gs_asyncThreads.push_back(hThread); data->hThread = hThread; #if wxUSE_IPC // second part of DDE hack: now establish the DDE conversation with the // just launched process if ( !ddeServer.empty() ) { bool ddeOK; // give the process the time to init itself // // we use a very big timeout hoping that WaitForInputIdle() will return // much sooner, but not INFINITE just in case the process hangs // completely - like this we will regain control sooner or later switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) ) { default: wxFAIL_MSG( wxT("unexpected WaitForInputIdle() return code") ); // fall through case WAIT_FAILED: wxLogLastError(wxT("WaitForInputIdle() in wxExecute")); case WAIT_TIMEOUT: wxLogDebug(wxT("Timeout too small in WaitForInputIdle")); ddeOK = false; break; case 0: // ok, process ready to accept DDE requests ddeOK = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand); } if ( !ddeOK ) { wxLogDebug(wxT("Failed to send DDE request to the process \"%s\"."), cmd.c_str()); } } #endif // wxUSE_IPC if ( !(flags & wxEXEC_SYNC) ) { // clean up will be done when the process terminates // return the pid return pi.dwProcessId; } wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; wxCHECK_MSG( traits, -1, wxT("no wxAppTraits in wxExecute()?") ); void *cookie = NULL; if ( !(flags & wxEXEC_NODISABLE) ) { // disable all app windows while waiting for the child process to finish cookie = traits->BeforeChildWaitLoop(); } // wait until the child process terminates #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( data->state ) { #if wxUSE_STREAMS if ( !bufOut.Update() && !bufErr.Update() ) #endif // wxUSE_STREAMS { // don't eat 100% of the CPU -- ugly but anything else requires // real async IO which we don't have for the moment ::Sleep(50); } // we must always process messages for our hidden window or we'd never // get wxWM_PROC_TERMINATED and so this loop would never terminate MSG msg; ::PeekMessage(&msg, data->hWnd, 0, 0, PM_REMOVE); // we may also need to process messages for all the other application // windows if ( !(flags & wxEXEC_NOEVENTS) ) { wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); if ( loop ) loop->Yield(); } } if ( !(flags & wxEXEC_NODISABLE) ) { // reenable disabled windows back traits->AfterChildWaitLoop(cookie); } DWORD dwExitCode = data->dwExitCode; delete data; // return the exit code return dwExitCode; }
BOOL Open( const wxString& printerName, LPPRINTER_DEFAULTS pDefault=(LPPRINTER_DEFAULTS)NULL ) { Close(); return OpenPrinter( wxMSW_CONV_LPTSTR(printerName), &m_hPrinter, pDefault ); }
bool wxWindowsPrintNativeData::TransferFrom( const wxPrintData &data ) { WinPrinter printer; LPTSTR szPrinterName = wxMSW_CONV_LPTSTR(data.GetPrinterName()); if (!m_devMode) InitializeDevMode(data.GetPrinterName(), &printer); HGLOBAL hDevMode = static_cast<HGLOBAL>(m_devMode); if ( hDevMode ) { GlobalPtrLock lockDevMode(hDevMode); DEVMODE * const devMode = static_cast<DEVMODE *>(lockDevMode.Get()); //// Orientation devMode->dmOrientation = (short)data.GetOrientation(); //// Collation devMode->dmCollate = (data.GetCollate() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE); devMode->dmFields |= DM_COLLATE; //// Number of copies devMode->dmCopies = (short)data.GetNoCopies(); devMode->dmFields |= DM_COPIES; //// Printer name wxString name = data.GetPrinterName(); if (!name.empty()) { // NB: the cast is needed in the ANSI build, strangely enough // dmDeviceName is BYTE[] and not char[] there wxStrlcpy(reinterpret_cast<wxChar *>(devMode->dmDeviceName), name.t_str(), WXSIZEOF(devMode->dmDeviceName)); } //// Colour if (data.GetColour()) devMode->dmColor = DMCOLOR_COLOR; else devMode->dmColor = DMCOLOR_MONOCHROME; devMode->dmFields |= DM_COLOR; //// Paper size // Paper id has priority over paper size. If id is specified, then size // is ignored (as it can be filled in even for standard paper sizes) wxPrintPaperType *paperType = NULL; const wxPaperSize paperId = data.GetPaperId(); if ( paperId != wxPAPER_NONE && wxThePrintPaperDatabase ) { paperType = wxThePrintPaperDatabase->FindPaperType(paperId); } if ( paperType ) { devMode->dmPaperSize = (short)paperType->GetPlatformId(); devMode->dmFields |= DM_PAPERSIZE; } else // custom (or no) paper size { const wxSize paperSize = data.GetPaperSize(); if ( paperSize != wxDefaultSize ) { // Fall back on specifying the paper size explicitly if(m_customWindowsPaperId != 0) devMode->dmPaperSize = m_customWindowsPaperId; else devMode->dmPaperSize = DMPAPER_USER; devMode->dmPaperWidth = (short)(paperSize.x * 10); devMode->dmPaperLength = (short)(paperSize.y * 10); devMode->dmFields |= DM_PAPERWIDTH; devMode->dmFields |= DM_PAPERLENGTH; // A printer driver may or may not also want DM_PAPERSIZE to // be specified. Also, if the printer driver doesn't implement the DMPAPER_USER // size, then this won't work, and even if you found the correct id by // enumerating the driver's paper sizes, it probably won't change the actual size, // it'll just select that custom paper type with its own current setting. // For a discussion on this, see http://www.codeguru.com/forum/showthread.php?threadid=458617 // Although m_customWindowsPaperId is intended to work around this, it's // unclear how it can help you set the custom paper size programmatically. } //else: neither paper type nor size specified, don't fill DEVMODE // at all so that the system defaults are used } //// Duplex short duplex; switch (data.GetDuplex()) { case wxDUPLEX_HORIZONTAL: duplex = DMDUP_HORIZONTAL; break; case wxDUPLEX_VERTICAL: duplex = DMDUP_VERTICAL; break; default: // in fact case wxDUPLEX_SIMPLEX: duplex = DMDUP_SIMPLEX; break; } devMode->dmDuplex = duplex; devMode->dmFields |= DM_DUPLEX; //// Quality short quality; switch (data.GetQuality()) { case wxPRINT_QUALITY_MEDIUM: quality = DMRES_MEDIUM; break; case wxPRINT_QUALITY_LOW: quality = DMRES_LOW; break; case wxPRINT_QUALITY_DRAFT: quality = DMRES_DRAFT; break; case wxPRINT_QUALITY_HIGH: quality = DMRES_HIGH; break; default: quality = (short)data.GetQuality(); devMode->dmYResolution = quality; devMode->dmFields |= DM_YRESOLUTION; break; } devMode->dmPrintQuality = quality; devMode->dmFields |= DM_PRINTQUALITY; if (data.GetPrivDataLen() > 0) { memcpy( (char *)devMode+devMode->dmSize, data.GetPrivData(), data.GetPrivDataLen() ); devMode->dmDriverExtra = (WXWORD)data.GetPrivDataLen(); } if (data.GetBin() != wxPRINTBIN_DEFAULT) { switch (data.GetBin()) { case wxPRINTBIN_ONLYONE: devMode->dmDefaultSource = DMBIN_ONLYONE; break; case wxPRINTBIN_LOWER: devMode->dmDefaultSource = DMBIN_LOWER; break; case wxPRINTBIN_MIDDLE: devMode->dmDefaultSource = DMBIN_MIDDLE; break; case wxPRINTBIN_MANUAL: devMode->dmDefaultSource = DMBIN_MANUAL; break; case wxPRINTBIN_ENVELOPE: devMode->dmDefaultSource = DMBIN_ENVELOPE; break; case wxPRINTBIN_ENVMANUAL: devMode->dmDefaultSource = DMBIN_ENVMANUAL; break; case wxPRINTBIN_AUTO: devMode->dmDefaultSource = DMBIN_AUTO; break; case wxPRINTBIN_TRACTOR: devMode->dmDefaultSource = DMBIN_TRACTOR; break; case wxPRINTBIN_SMALLFMT: devMode->dmDefaultSource = DMBIN_SMALLFMT; break; case wxPRINTBIN_LARGEFMT: devMode->dmDefaultSource = DMBIN_LARGEFMT; break; case wxPRINTBIN_LARGECAPACITY: devMode->dmDefaultSource = DMBIN_LARGECAPACITY; break; case wxPRINTBIN_CASSETTE: devMode->dmDefaultSource = DMBIN_CASSETTE; break; case wxPRINTBIN_FORMSOURCE: devMode->dmDefaultSource = DMBIN_FORMSOURCE; break; default: devMode->dmDefaultSource = (short)(DMBIN_USER + data.GetBin() - wxPRINTBIN_USER); // 256 + data.GetBin() - 14 = 242 + data.GetBin() break; } devMode->dmFields |= DM_DEFAULTSOURCE; } if (data.GetMedia() != wxPRINTMEDIA_DEFAULT) { devMode->dmMediaType = data.GetMedia(); devMode->dmFields |= DM_MEDIATYPE; } if( printer ) { // Step 3: // Merge the new settings with the old. // This gives the driver an opportunity to update any private // portions of the DevMode structure. DocumentProperties( NULL, printer, szPrinterName, (LPDEVMODE)hDevMode, // Reuse our buffer for output. (LPDEVMODE)hDevMode, // Pass the driver our changes DM_IN_BUFFER | // Commands to Merge our changes and DM_OUT_BUFFER ); // write the result. } } if ( m_devNames ) { ::GlobalFree(static_cast<HGLOBAL>(m_devNames)); } // TODO: I hope it's OK to pass some empty strings to DEVNAMES. m_devNames = wxCreateDevNames(wxEmptyString, data.GetPrinterName(), wxEmptyString); return true; }
void wxWindowsPrintNativeData::InitializeDevMode(const wxString& printerName, WinPrinter* printer) { if (m_devMode) return; LPTSTR szPrinterName = wxMSW_CONV_LPTSTR(printerName); // From MSDN: How To Modify Printer Settings with the DocumentProperties() Function // The purpose of this is to fill the DEVMODE with privdata from printer driver. // If we have a printer name and OpenPrinter successfully returns // this replaces the PrintDlg function which creates the DEVMODE filled only with data from default printer. if ( !m_devMode && !printerName.IsEmpty() ) { // Open printer if ( printer && printer->Open( printerName ) == TRUE ) { DWORD dwNeeded, dwRet; // Step 1: // Allocate a buffer of the correct size. dwNeeded = DocumentProperties( NULL, *printer, // Handle to our printer. szPrinterName, // Name of the printer. NULL, // Asking for size, so NULL, // these are not used. 0 ); // Zero returns buffer size. LPDEVMODE tempDevMode = static_cast<LPDEVMODE>( GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, dwNeeded ) ); // Step 2: // Get the default DevMode for the printer dwRet = DocumentProperties( NULL, *printer, szPrinterName, tempDevMode, // The address of the buffer to fill. NULL, // Not using the input buffer. DM_OUT_BUFFER ); // Have the output buffer filled. if ( dwRet != IDOK ) { // If failure, cleanup GlobalFree( tempDevMode ); printer->Close(); } else { m_devMode = tempDevMode; tempDevMode = NULL; } } } if ( !m_devMode ) { // Use PRINTDLG as a way of creating a DEVMODE object PRINTDLG pd; memset(&pd, 0, sizeof(PRINTDLG)); #ifdef __WXWINCE__ pd.cbStruct = sizeof(PRINTDLG); #else pd.lStructSize = sizeof(PRINTDLG); #endif pd.hwndOwner = NULL; pd.hDevMode = NULL; // Will be created by PrintDlg pd.hDevNames = NULL; // Ditto pd.Flags = PD_RETURNDEFAULT; pd.nCopies = 1; // Fill out the DEVMODE structure // so we can use it as input in the 'real' PrintDlg if (!PrintDlg(&pd)) { if ( pd.hDevMode ) GlobalFree(pd.hDevMode); if ( pd.hDevNames ) GlobalFree(pd.hDevNames); pd.hDevMode = NULL; pd.hDevNames = NULL; #if wxDEBUG_LEVEL wxLogDebug(wxT("Printing error: ") + wxGetPrintDlgError()); #endif // wxDEBUG_LEVEL } else { m_devMode = pd.hDevMode; pd.hDevMode = NULL; // We'll create a new DEVNAMEs structure below. if ( pd.hDevNames ) GlobalFree(pd.hDevNames); pd.hDevNames = NULL; // hDevNames = pd->hDevNames; // m_devNames = (void*)(long) hDevNames; // pd->hDevnames = NULL; } } }
// same as AddPage() but does it at given position bool wxNotebook::InsertPage(size_t nPage, wxNotebookPage *pPage, const wxString& strText, bool bSelect, int imageId) { wxCHECK_MSG( pPage != NULL, false, wxT("NULL page in wxNotebook::InsertPage") ); wxCHECK_MSG( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), false, wxT("invalid index in wxNotebook::InsertPage") ); wxASSERT_MSG( pPage->GetParent() == this, wxT("notebook pages must have notebook as parent") ); // add a new tab to the control // ---------------------------- // init all fields to 0 TC_ITEM tcItem; wxZeroMemory(tcItem); // set the image, if any if ( imageId != -1 ) { tcItem.mask |= TCIF_IMAGE; tcItem.iImage = imageId; } // and the text if ( !strText.empty() ) { tcItem.mask |= TCIF_TEXT; tcItem.pszText = wxMSW_CONV_LPTSTR(strText); } // hide the page: unless it is selected, it shouldn't be shown (and if it // is selected it will be shown later) HWND hwnd = GetWinHwnd(pPage); SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE); // this updates internal flag too -- otherwise it would get out of sync // with the real state pPage->Show(false); // fit the notebook page to the tab control's display area: this should be // done before adding it to the notebook or TabCtrl_InsertItem() will // change the notebooks size itself! AdjustPageSize(pPage); // finally do insert it if ( TabCtrl_InsertItem(GetHwnd(), nPage, &tcItem) == -1 ) { wxLogError(wxT("Can't create the notebook page '%s'."), strText.c_str()); return false; } // need to update the bg brush when the first page is added // so the first panel gets the correct themed background if ( m_pages.empty() ) { #if wxUSE_UXTHEME UpdateBgBrush(); #endif // wxUSE_UXTHEME } // succeeded: save the pointer to the page m_pages.Insert(pPage, nPage); // we may need to adjust the size again if the notebook size changed: // normally this only happens for the first page we add (the tabs which // hadn't been there before are now shown) but for a multiline notebook it // can happen for any page at all as a new row could have been started if ( m_pages.GetCount() == 1 || HasFlag(wxNB_MULTILINE) ) { AdjustPageSize(pPage); // Additionally, force the layout of the notebook itself by posting a // size event to it. If we don't do it, notebooks with pages on the // left or the right side may fail to account for the fact that they // are now big enough to fit all all of their pages on one row and // still reserve space for the second row of tabs, see #1792. const wxSize s = GetSize(); ::PostMessage(GetHwnd(), WM_SIZE, SIZE_RESTORED, MAKELPARAM(s.x, s.y)); } // now deal with the selection // --------------------------- // if the inserted page is before the selected one, we must update the // index of the selected page if ( int(nPage) <= m_selection ) { // one extra page added m_selection++; } DoSetSelectionAfterInsertion(nPage, bSelect); InvalidateBestSize(); return true; }
void wxMenuItem::SetItemLabel(const wxString& txt) { wxString text = txt; // don't do anything if label didn't change if ( m_text == txt ) return; // wxMenuItemBase will do stock ID checks wxMenuItemBase::SetItemLabel(text); // the item can be not attached to any menu yet and SetItemLabel() is still // valid to call in this case and should do nothing else if ( !m_parentMenu ) return; #if wxUSE_ACCEL m_parentMenu->UpdateAccel(this); #endif // wxUSE_ACCEL const int itemPos = MSGetMenuItemPos(); if ( itemPos == -1 ) return; HMENU hMenu = GetHMenuOf(m_parentMenu); // update the text of the native menu item WinStruct<MENUITEMINFO> info; // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't // work as it resets the menu bitmap, so we need to first get the old item // state and then modify it const bool isLaterThanWin95 = wxGetWinVersion() > wxWinVersion_95; info.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_DATA; if ( isLaterThanWin95 ) info.fMask |= MIIM_BITMAP | MIIM_FTYPE; else info.fMask |= MIIM_TYPE; if ( !::GetMenuItemInfo(hMenu, itemPos, TRUE, &info) ) { wxLogLastError(wxT("GetMenuItemInfo")); return; } #if wxUSE_OWNER_DRAWN // Don't set the text for the owner drawn items, they don't use it and even // though setting it doesn't seem to actually do any harm under Windows 7, // avoid doing this relatively nonsensical operation just in case it does // break something on other, past or future, Windows versions. // // Notice that we do need to call SetMenuItemInfo() even for the ownerdrawn // items however as otherwise their size wouldn't be recalculated as // WM_MEASUREITEM wouldn't be sent and this could result in display // problems if the length of the menu item changed significantly. // // Also notice that we shouldn't use our IsOwnerDrawn() because it can be // true because it was set by e.g. SetBitmap(), even if the item wasn't // made owner drawn at Windows level. if ( !(info.fState & MF_OWNERDRAW) ) #endif // wxUSE_OWNER_DRAWN { if ( isLaterThanWin95 ) info.fMask |= MIIM_STRING; //else: MIIM_TYPE already specified info.dwTypeData = wxMSW_CONV_LPTSTR(m_text); info.cch = m_text.length(); } if ( !::SetMenuItemInfo(hMenu, itemPos, TRUE, &info) ) { wxLogLastError(wxT("SetMenuItemInfo")); } }
void wxMenu::SetTitle(const wxString& label) { bool hasNoTitle = m_title.empty(); m_title = label; HMENU hMenu = GetHmenu(); if ( hasNoTitle ) { if ( !label.empty() ) { if ( !::InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING, (UINT_PTR)idMenuTitle, m_title.t_str()) || !::InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) ) { wxLogLastError(wxT("InsertMenu")); } } } else { if ( label.empty() ) { // remove the title and the separator after it if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) || !RemoveMenu(hMenu, 0, MF_BYPOSITION) ) { wxLogLastError(wxT("RemoveMenu")); } } else { // modify the title #ifdef __WXWINCE__ WinStruct<MENUITEMINFO> info; info.fMask = MIIM_TYPE; info.fType = MFT_STRING; info.cch = m_title.length(); info.dwTypeData = wxMSW_CONV_LPTSTR(m_title); if ( !SetMenuItemInfo(hMenu, 0, TRUE, & info) ) { wxLogLastError(wxT("SetMenuItemInfo")); } #else if ( !ModifyMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING, (UINT_PTR)idMenuTitle, m_title.t_str()) ) { wxLogLastError(wxT("ModifyMenu")); } #endif } } #ifdef __WIN32__ // put the title string in bold face if ( !m_title.empty() ) { SetDefaultMenuItem(GetHmenu(), (UINT)idMenuTitle); } #endif // Win32 }
// append a new item or submenu to the menu bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) { #if wxUSE_ACCEL UpdateAccel(pItem); #endif // wxUSE_ACCEL // we should support disabling the item even prior to adding it to the menu UINT flags = pItem->IsEnabled() ? MF_ENABLED : MF_GRAYED; // if "Break" has just been called, insert a menu break before this item // (and don't forget to reset the flag) if ( m_doBreak ) { flags |= MF_MENUBREAK; m_doBreak = false; } if ( pItem->IsSeparator() ) { flags |= MF_SEPARATOR; } // id is the numeric id for normal menu items and HMENU for submenus as // required by ::AppendMenu() API UINT_PTR id; wxMenu *submenu = pItem->GetSubMenu(); if ( submenu != NULL ) { wxASSERT_MSG( submenu->GetHMenu(), wxT("invalid submenu") ); submenu->SetParent(this); id = (UINT_PTR)submenu->GetHMenu(); flags |= MF_POPUP; } else { id = pItem->GetMSWId(); } // prepare to insert the item in the menu wxString itemText = pItem->GetItemLabel(); LPCTSTR pData = NULL; if ( pos == (size_t)-1 ) { // append at the end (note that the item is already appended to // internal data structures) pos = GetMenuItemCount() - 1; } // Update radio groups data if we're inserting a new radio item. // // NB: If we supported inserting non-radio items in the middle of existing // radio groups to break them into two subgroups, we'd need to update // m_radioData in this case too but currently this is not supported. bool checkInitially = false; if ( pItem->GetKind() == wxITEM_RADIO ) { if ( !m_radioData ) m_radioData = new wxMenuRadioItemsData; if ( m_radioData->UpdateOnInsert(pos) ) checkInitially = true; } // adjust position to account for the title of a popup menu, if any if ( !GetMenuBar() && !m_title.empty() ) pos += 2; // for the title itself and its separator BOOL ok = false; #if wxUSE_OWNER_DRAWN // Under older systems mixing owner-drawn and non-owner-drawn items results // in inconsistent margins, so we force this one to be owner-drawn if any // other items already are. if ( m_ownerDrawn ) pItem->SetOwnerDrawn(true); #endif // wxUSE_OWNER_DRAWN // check if we have something more than a simple text item #if wxUSE_OWNER_DRAWN bool makeItemOwnerDrawn = false; if ( pItem->IsOwnerDrawn() ) { #ifndef __DMC__ if ( !m_ownerDrawn && !pItem->IsSeparator() ) { // MIIM_BITMAP only works under WinME/2000+ so we always use owner // drawn item under the previous versions and we also have to use // them in any case if the item has custom colours or font static const wxWinVersion winver = wxGetWinVersion(); bool mustUseOwnerDrawn = winver < wxWinVersion_98 || pItem->GetTextColour().IsOk() || pItem->GetBackgroundColour().IsOk() || pItem->GetFont().IsOk(); if ( !mustUseOwnerDrawn ) { const wxBitmap& bmpUnchecked = pItem->GetBitmap(false), bmpChecked = pItem->GetBitmap(true); if ( (bmpUnchecked.IsOk() && IsGreaterThanStdSize(bmpUnchecked)) || (bmpChecked.IsOk() && IsGreaterThanStdSize(bmpChecked)) ) { mustUseOwnerDrawn = true; } } // use InsertMenuItem() if possible as it's guaranteed to look // correct while our owner-drawn code is not if ( !mustUseOwnerDrawn ) { WinStruct<MENUITEMINFO> mii; mii.fMask = MIIM_STRING | MIIM_DATA; // don't set hbmpItem for the checkable items as it would // be used for both checked and unchecked state if ( pItem->IsCheckable() ) { mii.fMask |= MIIM_CHECKMARKS; mii.hbmpChecked = GetHBitmapForMenu(pItem, true); mii.hbmpUnchecked = GetHBitmapForMenu(pItem, false); } else if ( pItem->GetBitmap().IsOk() ) { mii.fMask |= MIIM_BITMAP; mii.hbmpItem = GetHBitmapForMenu(pItem); } mii.cch = itemText.length(); mii.dwTypeData = wxMSW_CONV_LPTSTR(itemText); if ( flags & MF_POPUP ) { mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = GetHmenuOf(pItem->GetSubMenu()); } else { mii.fMask |= MIIM_ID; mii.wID = id; } mii.dwItemData = reinterpret_cast<ULONG_PTR>(pItem); ok = ::InsertMenuItem(GetHmenu(), pos, TRUE /* by pos */, &mii); if ( !ok ) { wxLogLastError(wxT("InsertMenuItem()")); } else // InsertMenuItem() ok { // we need to remove the extra indent which is reserved for // the checkboxes by default as it looks ugly unless check // boxes are used together with bitmaps and this is not the // case in wx API WinStruct<MENUINFO> mi; // don't call SetMenuInfo() directly, this would prevent // the app from starting up under Windows 95/NT 4 typedef BOOL (WINAPI *SetMenuInfo_t)(HMENU, MENUINFO *); wxDynamicLibrary dllUser(wxT("user32")); wxDYNLIB_FUNCTION(SetMenuInfo_t, SetMenuInfo, dllUser); if ( pfnSetMenuInfo ) { mi.fMask = MIM_STYLE; mi.dwStyle = MNS_CHECKORBMP; if ( !(*pfnSetMenuInfo)(GetHmenu(), &mi) ) { wxLogLastError(wxT("SetMenuInfo(MNS_NOCHECK)")); } } // tell the item that it's not really owner-drawn but only // needs to draw its bitmap, the rest is done by Windows pItem->SetOwnerDrawn(false); } } } #endif // __DMC__ if ( !ok ) { // item draws itself, pass pointer to it in data parameter flags |= MF_OWNERDRAW; pData = (LPCTSTR)pItem; bool updateAllMargins = false; // get size of bitmap always return valid value (0 for invalid bitmap), // so we don't needed check if bitmap is valid ;) int uncheckedW = pItem->GetBitmap(false).GetWidth(); int checkedW = pItem->GetBitmap(true).GetWidth(); if ( m_maxBitmapWidth < uncheckedW ) { m_maxBitmapWidth = uncheckedW; updateAllMargins = true; } if ( m_maxBitmapWidth < checkedW ) { m_maxBitmapWidth = checkedW; updateAllMargins = true; } // make other item ownerdrawn and update margin width for equals alignment if ( !m_ownerDrawn || updateAllMargins ) { // we must use position in SetOwnerDrawnMenuItem because // all separators have the same id int pos = 0; wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); while (node) { wxMenuItem* item = node->GetData(); if ( !item->IsOwnerDrawn()) { item->SetOwnerDrawn(true); SetOwnerDrawnMenuItem(GetHmenu(), pos, reinterpret_cast<ULONG_PTR>(item), TRUE); } item->SetMarginWidth(m_maxBitmapWidth); node = node->GetNext(); pos++; } // set menu as ownerdrawn m_ownerDrawn = true; // also ensure that the new item itself is made owner drawn makeItemOwnerDrawn = true; ResetMaxAccelWidth(); } // only update our margin for equals alignment to other item else if ( !updateAllMargins ) { pItem->SetMarginWidth(m_maxBitmapWidth); } } } else #endif // wxUSE_OWNER_DRAWN { // item is just a normal string (passed in data parameter) flags |= MF_STRING; #ifdef __WXWINCE__ itemText = wxMenuItem::GetLabelText(itemText); #endif pData = itemText.t_str(); } // item might have already been inserted by InsertMenuItem() above if ( !ok ) { if ( !::InsertMenu(GetHmenu(), pos, flags | MF_BYPOSITION, id, pData) ) { wxLogLastError(wxT("InsertMenu[Item]()")); return false; } if ( makeItemOwnerDrawn ) { SetOwnerDrawnMenuItem(GetHmenu(), pos, reinterpret_cast<ULONG_PTR>(pItem), TRUE); } } // Check the item if it should be initially checked. if ( checkInitially ) pItem->Check(true); // if we just appended the title, highlight it if ( id == (UINT_PTR)idMenuTitle ) { // visually select the menu title SetDefaultMenuItem(GetHmenu(), id); } // if we're already attached to the menubar, we must update it if ( IsAttached() && GetMenuBar()->IsAttached() ) { GetMenuBar()->Refresh(); } return true; }
void wxToolTip::DoAddHWND(WXHWND hWnd) { HWND hwnd = (HWND)hWnd; wxToolInfo ti(hwnd, m_id, m_rect); // another possibility would be to specify LPSTR_TEXTCALLBACK here as we // store the tooltip text ourselves anyhow, and provide it in response to // TTN_NEEDTEXT (sent via WM_NOTIFY), but then we would be limited to 79 // character tooltips as this is the size of the szText buffer in // NMTTDISPINFO struct -- and setting the tooltip here we can have tooltips // of any length ti.hwnd = hwnd; ti.lpszText = wxMSW_CONV_LPTSTR(m_text); if ( !SendTooltipMessage(GetToolTipCtrl(), TTM_ADDTOOL, &ti) ) { wxLogDebug(wxT("Failed to create the tooltip '%s'"), m_text.c_str()); return; } #ifdef TTM_SETMAXTIPWIDTH if ( wxApp::GetComCtl32Version() >= 470 ) { // use TTM_SETMAXTIPWIDTH to make tooltip multiline using the // extent of its first line as max value HFONT hfont = (HFONT) SendTooltipMessage(GetToolTipCtrl(), WM_GETFONT, 0); if ( !hfont ) { hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); if ( !hfont ) { wxLogLastError(wxT("GetStockObject(DEFAULT_GUI_FONT)")); } } MemoryHDC hdc; if ( !hdc ) { wxLogLastError(wxT("CreateCompatibleDC(NULL)")); } if ( !SelectObject(hdc, hfont) ) { wxLogLastError(wxT("SelectObject(hfont)")); } // find the width of the widest line int maxWidth = 0; wxStringTokenizer tokenizer(m_text, wxT("\n")); while ( tokenizer.HasMoreTokens() ) { const wxString token = tokenizer.GetNextToken(); SIZE sz; if ( !::GetTextExtentPoint32(hdc, token.t_str(), token.length(), &sz) ) { wxLogLastError(wxT("GetTextExtentPoint32")); } if ( sz.cx > maxWidth ) maxWidth = sz.cx; } // limit size to ms_maxWidth, if set if ( ms_maxWidth == 0 ) { // this is more or less arbitrary but seems to work well static const int DEFAULT_MAX_WIDTH = 400; ms_maxWidth = wxGetClientDisplayRect().width / 2; if ( ms_maxWidth > DEFAULT_MAX_WIDTH ) ms_maxWidth = DEFAULT_MAX_WIDTH; } if ( ms_maxWidth != -1 && maxWidth > ms_maxWidth ) maxWidth = ms_maxWidth; // only set a new width if it is bigger than the current setting: // otherwise adding a tooltip with shorter line(s) than a previous // one would result in breaking the longer lines unnecessarily as // all our tooltips share the same maximal width if ( maxWidth > SendTooltipMessage(GetToolTipCtrl(), TTM_GETMAXTIPWIDTH, 0) ) { SendTooltipMessage(GetToolTipCtrl(), TTM_SETMAXTIPWIDTH, wxUIntToPtr(maxWidth)); } } else #endif // TTM_SETMAXTIPWIDTH { // replace the '\n's with spaces because otherwise they appear as // unprintable characters in the tooltip string m_text.Replace(wxT("\n"), wxT(" ")); ti.lpszText = wxMSW_CONV_LPTSTR(m_text); if ( !SendTooltipMessage(GetToolTipCtrl(), TTM_ADDTOOL, &ti) ) { wxLogDebug(wxT("Failed to create the tooltip '%s'"), m_text.c_str()); } } }
// append a new item or submenu to the menu bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) { #if wxUSE_ACCEL UpdateAccel(pItem); #endif // wxUSE_ACCEL // we should support disabling the item even prior to adding it to the menu UINT flags = pItem->IsEnabled() ? MF_ENABLED : MF_GRAYED; // if "Break" has just been called, insert a menu break before this item // (and don't forget to reset the flag) if ( m_doBreak ) { flags |= MF_MENUBREAK; m_doBreak = false; } if ( pItem->IsSeparator() ) { flags |= MF_SEPARATOR; } // id is the numeric id for normal menu items and HMENU for submenus as // required by ::AppendMenu() API UINT_PTR id; wxMenu *submenu = pItem->GetSubMenu(); if ( submenu != NULL ) { wxASSERT_MSG( submenu->GetHMenu(), wxT("invalid submenu") ); submenu->SetParent(this); id = (UINT_PTR)submenu->GetHMenu(); flags |= MF_POPUP; } else { id = pItem->GetMSWId(); } // prepare to insert the item in the menu wxString itemText = pItem->GetItemLabel(); LPCTSTR pData = NULL; if ( pos == (size_t)-1 ) { // append at the end (note that the item is already appended to // internal data structures) pos = GetMenuItemCount() - 1; } // Update radio groups data if we're inserting a new menu item. // Inserting radio and non-radio item has a different impact // on radio groups so we have to handle each case separately. // (Inserting a radio item in the middle of existing group extends // the group, but inserting non-radio item breaks it into two subgroups.) // bool checkInitially = false; if ( pItem->IsRadio() ) { if ( !m_radioData ) m_radioData = new wxMenuRadioItemsData; if ( m_radioData->UpdateOnInsertRadio(pos) ) checkInitially = true; } else if ( m_radioData ) { if ( m_radioData->UpdateOnInsertNonRadio(pos) ) { // One of the existing groups has been split into two subgroups. wxFAIL_MSG(wxS("Inserting non-radio item inside a radio group?")); } } // Also handle the case of check menu items that had been checked before // being attached to the menu: we don't need to actually call Check() on // them, so we don't use checkInitially in this case, but we do need to // make them checked at Windows level too. Notice that we shouldn't ask // Windows for the checked state here, as wxMenuItem::IsChecked() does, as // the item is not attached yet, so explicitly call the base class version. if ( pItem->IsCheck() && pItem->wxMenuItemBase::IsChecked() ) flags |= MF_CHECKED; // adjust position to account for the title of a popup menu, if any if ( !GetMenuBar() && !m_title.empty() ) pos += 2; // for the title itself and its separator BOOL ok = false; #if wxUSE_OWNER_DRAWN // Under older systems mixing owner-drawn and non-owner-drawn items results // in inconsistent margins, so we force this one to be owner-drawn if any // other items already are. if ( m_ownerDrawn ) pItem->SetOwnerDrawn(true); // check if we have something more than a simple text item bool makeItemOwnerDrawn = false; #endif // wxUSE_OWNER_DRAWN if ( #if wxUSE_OWNER_DRAWN !pItem->IsOwnerDrawn() && #endif !pItem->IsSeparator() ) { WinStruct<MENUITEMINFO> mii; mii.fMask = MIIM_STRING | MIIM_DATA; // don't set hbmpItem for the checkable items as it would // be used for both checked and unchecked state if ( pItem->IsCheckable() ) { mii.fMask |= MIIM_CHECKMARKS; mii.hbmpChecked = pItem->GetHBitmapForMenu(wxMenuItem::Checked); mii.hbmpUnchecked = pItem->GetHBitmapForMenu(wxMenuItem::Unchecked); } else if ( pItem->GetBitmap().IsOk() ) { mii.fMask |= MIIM_BITMAP; mii.hbmpItem = pItem->GetHBitmapForMenu(wxMenuItem::Normal); } mii.cch = itemText.length(); mii.dwTypeData = wxMSW_CONV_LPTSTR(itemText); if ( flags & MF_POPUP ) { mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = GetHmenuOf(pItem->GetSubMenu()); } else { mii.fMask |= MIIM_ID; mii.wID = id; } if ( flags & MF_GRAYED ) { mii.fMask |= MIIM_STATE; mii.fState = MFS_GRAYED; } if ( flags & MF_CHECKED ) { mii.fMask |= MIIM_STATE; mii.fState = MFS_CHECKED; } mii.dwItemData = reinterpret_cast<ULONG_PTR>(pItem); ok = ::InsertMenuItem(GetHmenu(), pos, TRUE /* by pos */, &mii); if ( !ok ) { wxLogLastError(wxT("InsertMenuItem()")); #if wxUSE_OWNER_DRAWN // In case of failure switch new item to the owner-drawn mode. makeItemOwnerDrawn = true; #endif } else // InsertMenuItem() ok { // we need to remove the extra indent which is reserved for // the checkboxes by default as it looks ugly unless check // boxes are used together with bitmaps and this is not the // case in wx API WinStruct<MENUINFO> mi; mi.fMask = MIM_STYLE; mi.dwStyle = MNS_CHECKORBMP; if ( !::SetMenuInfo(GetHmenu(), &mi) ) { wxLogLastError(wxT("SetMenuInfo(MNS_NOCHECK)")); } } } #if wxUSE_OWNER_DRAWN if ( pItem->IsOwnerDrawn() || makeItemOwnerDrawn ) { // item draws itself, pass pointer to it in data parameter flags |= MF_OWNERDRAW; pData = (LPCTSTR)pItem; bool updateAllMargins = false; // get size of bitmap always return valid value (0 for invalid bitmap), // so we don't needed check if bitmap is valid ;) int uncheckedW = pItem->GetBitmap(false).GetWidth(); int checkedW = pItem->GetBitmap(true).GetWidth(); if ( m_maxBitmapWidth < uncheckedW ) { m_maxBitmapWidth = uncheckedW; updateAllMargins = true; } if ( m_maxBitmapWidth < checkedW ) { m_maxBitmapWidth = checkedW; updateAllMargins = true; } // make other item ownerdrawn and update margin width for equals alignment if ( !m_ownerDrawn || updateAllMargins ) { // we must use position in SetOwnerDrawnMenuItem because // all separators have the same id int position = 0; wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); while (node) { wxMenuItem* item = node->GetData(); // Current item is already added to the list of items // but is not yet physically attached to the menu // so we have to skip setting it as an owner drawn. // It will be done later on when the item will be created. if ( !item->IsOwnerDrawn() && item != pItem ) { item->SetOwnerDrawn(true); SetOwnerDrawnMenuItem(GetHmenu(), position, reinterpret_cast<ULONG_PTR>(item), TRUE); } item->SetMarginWidth(m_maxBitmapWidth); node = node->GetNext(); // Current item is already added to the list of items // but is not yet physically attached to the menu // so it cannot be counted while determining position // in the menu. if ( item != pItem ) position++; } // set menu as ownerdrawn m_ownerDrawn = true; ResetMaxAccelWidth(); } // only update our margin for equals alignment to other item else if ( !updateAllMargins ) { pItem->SetMarginWidth(m_maxBitmapWidth); } } #endif // wxUSE_OWNER_DRAWN // item might have already been inserted by InsertMenuItem() above if ( !ok ) { if ( !::InsertMenu(GetHmenu(), pos, flags | MF_BYPOSITION, id, pData) ) { wxLogLastError(wxT("InsertMenu[Item]()")); return false; } #if wxUSE_OWNER_DRAWN if ( makeItemOwnerDrawn ) { pItem->SetOwnerDrawn(true); SetOwnerDrawnMenuItem(GetHmenu(), pos, reinterpret_cast<ULONG_PTR>(pItem), TRUE); } #endif } // Check the item if it should be initially checked. if ( checkInitially ) pItem->Check(true); // if we just appended the title, highlight it if ( id == (UINT_PTR)idMenuTitle ) { // visually select the menu title SetDefaultMenuItem(GetHmenu(), id); } // if we're already attached to the menubar, we must update it if ( IsAttached() && GetMenuBar()->IsAttached() ) { GetMenuBar()->Refresh(); } return true; }
void wxMenuItem::SetItemLabel(const wxString& txt) { wxString text = txt; // don't do anything if label didn't change if ( m_text == txt ) return; // wxMenuItemBase will do stock ID checks wxMenuItemBase::SetItemLabel(text); // the item can be not attached to any menu yet and SetItemLabel() is still // valid to call in this case and should do nothing else if ( !m_parentMenu ) return; #if wxUSE_ACCEL m_parentMenu->UpdateAccel(this); #endif // wxUSE_ACCEL const UINT id = GetMSWId(); HMENU hMenu = GetHMenuOf(m_parentMenu); if ( !hMenu || ::GetMenuState(hMenu, id, MF_BYCOMMAND) == (UINT)-1 ) return; #if wxUSE_OWNER_DRAWN if ( IsOwnerDrawn() ) { // we don't need to do anything for owner drawn items, they will redraw // themselves using the new text the next time they're displayed return; } #endif // owner drawn // update the text of the native menu item WinStruct<MENUITEMINFO> info; // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't // work as it resets the menu bitmap, so we need to first get the old item // state and then modify it const bool isLaterThanWin95 = wxGetWinVersion() > wxWinVersion_95; info.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_DATA; if ( isLaterThanWin95 ) info.fMask |= MIIM_BITMAP | MIIM_FTYPE; else info.fMask |= MIIM_TYPE; if ( !::GetMenuItemInfo(hMenu, id, FALSE, &info) ) { wxLogLastError(wxT("GetMenuItemInfo")); return; } if ( isLaterThanWin95 ) info.fMask |= MIIM_STRING; //else: MIIM_TYPE already specified info.dwTypeData = wxMSW_CONV_LPTSTR(m_text); info.cch = m_text.length(); if ( !::SetMenuItemInfo(hMenu, id, FALSE, &info) ) { wxLogLastError(wxT("SetMenuItemInfo")); } }