PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/common/ctrlcmn.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 602 lines | 373 code | 112 blank | 117 comment | 75 complexity | 0a46edc7f9befe6dd450c3f39f371662 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/common/ctrlcmn.cpp
  3. // Purpose: wxControl common interface
  4. // Author: Vadim Zeitlin
  5. // Modified by:
  6. // Created: 26.07.99
  7. // RCS-ID: $Id$
  8. // Copyright: (c) wxWidgets team
  9. // Licence: wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11. // ============================================================================
  12. // declarations
  13. // ============================================================================
  14. // ----------------------------------------------------------------------------
  15. // headers
  16. // ----------------------------------------------------------------------------
  17. // For compilers that support precompilation, includes "wx.h".
  18. #include "wx/wxprec.h"
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22. #if wxUSE_CONTROLS
  23. #include "wx/control.h"
  24. #ifndef WX_PRECOMP
  25. #include "wx/dc.h"
  26. #include "wx/log.h"
  27. #include "wx/radiobut.h"
  28. #include "wx/statbmp.h"
  29. #include "wx/bitmap.h"
  30. #include "wx/utils.h" // for wxStripMenuCodes()
  31. #include "wx/settings.h"
  32. #endif
  33. #include "wx/private/markupparser.h"
  34. const char wxControlNameStr[] = "control";
  35. // ============================================================================
  36. // implementation
  37. // ============================================================================
  38. wxControlBase::~wxControlBase()
  39. {
  40. // this destructor is required for Darwin
  41. }
  42. bool wxControlBase::Create(wxWindow *parent,
  43. wxWindowID id,
  44. const wxPoint &pos,
  45. const wxSize &size,
  46. long style,
  47. const wxValidator& wxVALIDATOR_PARAM(validator),
  48. const wxString &name)
  49. {
  50. bool ret = wxWindow::Create(parent, id, pos, size, style, name);
  51. #if wxUSE_VALIDATORS
  52. if ( ret )
  53. SetValidator(validator);
  54. #endif // wxUSE_VALIDATORS
  55. return ret;
  56. }
  57. bool wxControlBase::CreateControl(wxWindowBase *parent,
  58. wxWindowID id,
  59. const wxPoint& pos,
  60. const wxSize& size,
  61. long style,
  62. const wxValidator& validator,
  63. const wxString& name)
  64. {
  65. // even if it's possible to create controls without parents in some port,
  66. // it should surely be discouraged because it doesn't work at all under
  67. // Windows
  68. wxCHECK_MSG( parent, false, wxT("all controls must have parents") );
  69. if ( !CreateBase(parent, id, pos, size, style, validator, name) )
  70. return false;
  71. parent->AddChild(this);
  72. return true;
  73. }
  74. void wxControlBase::Command(wxCommandEvent& event)
  75. {
  76. (void)GetEventHandler()->ProcessEvent(event);
  77. }
  78. void wxControlBase::InitCommandEvent(wxCommandEvent& event) const
  79. {
  80. event.SetEventObject(const_cast<wxControlBase *>(this));
  81. // event.SetId(GetId()); -- this is usuall done in the event ctor
  82. switch ( m_clientDataType )
  83. {
  84. case wxClientData_Void:
  85. event.SetClientData(GetClientData());
  86. break;
  87. case wxClientData_Object:
  88. event.SetClientObject(GetClientObject());
  89. break;
  90. case wxClientData_None:
  91. // nothing to do
  92. ;
  93. }
  94. }
  95. bool wxControlBase::SetFont(const wxFont& font)
  96. {
  97. InvalidateBestSize();
  98. return wxWindow::SetFont(font);
  99. }
  100. // wxControl-specific processing after processing the update event
  101. void wxControlBase::DoUpdateWindowUI(wxUpdateUIEvent& event)
  102. {
  103. // call inherited
  104. wxWindowBase::DoUpdateWindowUI(event);
  105. // update label
  106. if ( event.GetSetText() )
  107. {
  108. if ( event.GetText() != GetLabel() )
  109. SetLabel(event.GetText());
  110. }
  111. // Unfortunately we don't yet have common base class for
  112. // wxRadioButton, so we handle updates of radiobuttons here.
  113. // TODO: If once wxRadioButtonBase will exist, move this code there.
  114. #if wxUSE_RADIOBTN
  115. if ( event.GetSetChecked() )
  116. {
  117. wxRadioButton *radiobtn = wxDynamicCastThis(wxRadioButton);
  118. if ( radiobtn )
  119. radiobtn->SetValue(event.GetChecked());
  120. }
  121. #endif // wxUSE_RADIOBTN
  122. }
  123. /* static */
  124. wxString wxControlBase::GetLabelText(const wxString& label)
  125. {
  126. // we don't want strip the TABs here, just the mnemonics
  127. return wxStripMenuCodes(label, wxStrip_Mnemonics);
  128. }
  129. /* static */
  130. wxString wxControlBase::RemoveMnemonics(const wxString& str)
  131. {
  132. // we don't want strip the TABs here, just the mnemonics
  133. return wxStripMenuCodes(str, wxStrip_Mnemonics);
  134. }
  135. /* static */
  136. wxString wxControlBase::EscapeMnemonics(const wxString& text)
  137. {
  138. wxString label(text);
  139. label.Replace("&", "&&");
  140. return label;
  141. }
  142. /* static */
  143. int wxControlBase::FindAccelIndex(const wxString& label, wxString *labelOnly)
  144. {
  145. // the character following MNEMONIC_PREFIX is the accelerator for this
  146. // control unless it is MNEMONIC_PREFIX too - this allows to insert
  147. // literal MNEMONIC_PREFIX chars into the label
  148. static const wxChar MNEMONIC_PREFIX = wxT('&');
  149. if ( labelOnly )
  150. {
  151. labelOnly->Empty();
  152. labelOnly->Alloc(label.length());
  153. }
  154. int indexAccel = -1;
  155. for ( wxString::const_iterator pc = label.begin(); pc != label.end(); ++pc )
  156. {
  157. if ( *pc == MNEMONIC_PREFIX )
  158. {
  159. ++pc; // skip it
  160. if ( pc == label.end() )
  161. break;
  162. else if ( *pc != MNEMONIC_PREFIX )
  163. {
  164. if ( indexAccel == -1 )
  165. {
  166. // remember it (-1 is for MNEMONIC_PREFIX itself
  167. indexAccel = pc - label.begin() - 1;
  168. }
  169. else
  170. {
  171. wxFAIL_MSG(wxT("duplicate accel char in control label"));
  172. }
  173. }
  174. }
  175. if ( labelOnly )
  176. {
  177. *labelOnly += *pc;
  178. }
  179. }
  180. return indexAccel;
  181. }
  182. wxBorder wxControlBase::GetDefaultBorder() const
  183. {
  184. return wxBORDER_THEME;
  185. }
  186. /* static */ wxVisualAttributes
  187. wxControlBase::GetCompositeControlsDefaultAttributes(wxWindowVariant WXUNUSED(variant))
  188. {
  189. wxVisualAttributes attrs;
  190. attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
  191. attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
  192. attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
  193. return attrs;
  194. }
  195. // ----------------------------------------------------------------------------
  196. // wxControl markup support
  197. // ----------------------------------------------------------------------------
  198. #if wxUSE_MARKUP
  199. /* static */
  200. wxString wxControlBase::RemoveMarkup(const wxString& markup)
  201. {
  202. return wxMarkupParser::Strip(markup);
  203. }
  204. bool wxControlBase::DoSetLabelMarkup(const wxString& markup)
  205. {
  206. const wxString label = RemoveMarkup(markup);
  207. if ( label.empty() && !markup.empty() )
  208. return false;
  209. SetLabel(label);
  210. return true;
  211. }
  212. #endif // wxUSE_MARKUP
  213. // ----------------------------------------------------------------------------
  214. // wxControlBase - ellipsization code
  215. // ----------------------------------------------------------------------------
  216. #define wxELLIPSE_REPLACEMENT wxS("...")
  217. namespace
  218. {
  219. struct EllipsizeCalculator
  220. {
  221. EllipsizeCalculator(const wxString& s, const wxDC& dc,
  222. int maxFinalWidthPx, int replacementWidthPx)
  223. :
  224. m_initialCharToRemove(0),
  225. m_nCharsToRemove(0),
  226. m_outputNeedsUpdate(true),
  227. m_str(s),
  228. m_dc(dc),
  229. m_maxFinalWidthPx(maxFinalWidthPx),
  230. m_replacementWidthPx(replacementWidthPx)
  231. {
  232. m_isOk = dc.GetPartialTextExtents(s, m_charOffsetsPx);
  233. wxASSERT( m_charOffsetsPx.GetCount() == s.length() );
  234. }
  235. bool IsOk() const { return m_isOk; }
  236. bool EllipsizationNotNeeded() const
  237. {
  238. // NOTE: charOffsetsPx[n] is the width in pixels of the first n characters (with the last one INCLUDED)
  239. // thus charOffsetsPx[len-1] is the total width of the string
  240. return m_charOffsetsPx.Last() <= m_maxFinalWidthPx;
  241. }
  242. void Init(size_t initialCharToRemove, size_t nCharsToRemove)
  243. {
  244. m_initialCharToRemove = initialCharToRemove;
  245. m_nCharsToRemove = nCharsToRemove;
  246. }
  247. void RemoveFromEnd()
  248. {
  249. m_nCharsToRemove++;
  250. }
  251. void RemoveFromStart()
  252. {
  253. m_initialCharToRemove--;
  254. m_nCharsToRemove++;
  255. }
  256. size_t GetFirstRemoved() const { return m_initialCharToRemove; }
  257. size_t GetLastRemoved() const { return m_initialCharToRemove + m_nCharsToRemove - 1; }
  258. const wxString& GetEllipsizedText()
  259. {
  260. if ( m_outputNeedsUpdate )
  261. {
  262. wxASSERT(m_initialCharToRemove <= m_str.length() - 1); // see valid range for initialCharToRemove above
  263. wxASSERT(m_nCharsToRemove >= 1 && m_nCharsToRemove <= m_str.length() - m_initialCharToRemove); // see valid range for nCharsToRemove above
  264. // erase m_nCharsToRemove characters after m_initialCharToRemove (included);
  265. // e.g. if we have the string "foobar" (len = 6)
  266. // ^
  267. // \--- m_initialCharToRemove = 2
  268. // and m_nCharsToRemove = 2, then we get "foar"
  269. m_output = m_str;
  270. m_output.replace(m_initialCharToRemove, m_nCharsToRemove, wxELLIPSE_REPLACEMENT);
  271. }
  272. return m_output;
  273. }
  274. bool IsShortEnough()
  275. {
  276. if ( m_nCharsToRemove == m_str.length() )
  277. return true; // that's the best we could do
  278. // Width calculation using partial extents is just an inaccurate
  279. // estimate: partial extents have sub-pixel precision and are rounded
  280. // by GetPartialTextExtents(); replacing part of the string with "..."
  281. // may change them too thanks to changes in ligatures, kerning etc.
  282. //
  283. // The correct algorithm would be to call GetTextExtent() in every step
  284. // of ellipsization, but that would be too expensive, especially when
  285. // the difference is just a few pixels. So we use partial extents to
  286. // estimate string width and only verify it with GetTextExtent() when
  287. // it looks good.
  288. int estimatedWidth = m_replacementWidthPx; // length of "..."
  289. // length of text before the removed part:
  290. if ( m_initialCharToRemove > 0 )
  291. estimatedWidth += m_charOffsetsPx[m_initialCharToRemove - 1];
  292. // length of text after the removed part:
  293. if ( GetLastRemoved() < m_str.length() )
  294. estimatedWidth += m_charOffsetsPx.Last() - m_charOffsetsPx[GetLastRemoved()];
  295. if ( estimatedWidth > m_maxFinalWidthPx )
  296. return false;
  297. return m_dc.GetTextExtent(GetEllipsizedText()).GetWidth() <= m_maxFinalWidthPx;
  298. }
  299. // calculation state:
  300. // REMEMBER: indexes inside the string have a valid range of [0;len-1] if not otherwise constrained
  301. // lengths/counts of characters (e.g. nCharsToRemove) have a
  302. // valid range of [0;len] if not otherwise constrained
  303. // NOTE: since this point we know we have for sure a non-empty string from which we need
  304. // to remove _at least_ one character (thus nCharsToRemove below is constrained to be >= 1)
  305. // index of first character to erase, valid range is [0;len-1]:
  306. size_t m_initialCharToRemove;
  307. // how many chars do we need to erase? valid range is [0;len-m_initialCharToRemove]
  308. size_t m_nCharsToRemove;
  309. wxString m_output;
  310. bool m_outputNeedsUpdate;
  311. // inputs:
  312. wxString m_str;
  313. const wxDC& m_dc;
  314. int m_maxFinalWidthPx;
  315. int m_replacementWidthPx;
  316. wxArrayInt m_charOffsetsPx;
  317. bool m_isOk;
  318. };
  319. } // anonymous namespace
  320. /* static and protected */
  321. wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
  322. wxEllipsizeMode mode, int maxFinalWidthPx,
  323. int replacementWidthPx)
  324. {
  325. wxASSERT_MSG(replacementWidthPx > 0, "Invalid parameters");
  326. wxASSERT_LEVEL_2_MSG(!curLine.Contains('\n'),
  327. "Use Ellipsize() instead!");
  328. wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" );
  329. // NOTE: this function assumes that any mnemonic/tab character has already
  330. // been handled if it was necessary to handle them (see Ellipsize())
  331. if (maxFinalWidthPx <= 0)
  332. return wxEmptyString;
  333. size_t len = curLine.length();
  334. if (len <= 1 )
  335. return curLine;
  336. EllipsizeCalculator calc(curLine, dc, maxFinalWidthPx, replacementWidthPx);
  337. if ( !calc.IsOk() )
  338. return curLine;
  339. if ( calc.EllipsizationNotNeeded() )
  340. return curLine;
  341. // let's compute the range of characters to remove depending on the ellipsization mode:
  342. switch (mode)
  343. {
  344. case wxELLIPSIZE_START:
  345. {
  346. calc.Init(0, 1);
  347. while ( !calc.IsShortEnough() )
  348. calc.RemoveFromEnd();
  349. // always show at least one character of the string:
  350. if ( calc.m_nCharsToRemove == len )
  351. return wxString(wxELLIPSE_REPLACEMENT) + curLine[len-1];
  352. break;
  353. }
  354. case wxELLIPSIZE_MIDDLE:
  355. {
  356. // NOTE: the following piece of code works also when len == 1
  357. // start the removal process from the middle of the string
  358. // i.e. separe the string in three parts:
  359. // - the first one to preserve, valid range [0;initialCharToRemove-1] or the empty range if initialCharToRemove==0
  360. // - the second one to remove, valid range [initialCharToRemove;endCharToRemove]
  361. // - the third one to preserve, valid range [endCharToRemove+1;len-1] or the empty range if endCharToRemove==len-1
  362. // NOTE: empty range != range [0;0] since the range [0;0] contains 1 character (the zero-th one)!
  363. calc.Init(len/2, 0);
  364. bool removeFromStart = true;
  365. while ( !calc.IsShortEnough() )
  366. {
  367. const bool canRemoveFromStart = calc.GetFirstRemoved() > 0;
  368. const bool canRemoveFromEnd = calc.GetLastRemoved() < len - 1;
  369. if ( !canRemoveFromStart && !canRemoveFromEnd )
  370. {
  371. // we need to remove all the characters of the string!
  372. break;
  373. }
  374. // Remove from the beginning in even steps and from the end
  375. // in odd steps, unless we exhausted one side already:
  376. removeFromStart = !removeFromStart;
  377. if ( removeFromStart && !canRemoveFromStart )
  378. removeFromStart = false;
  379. else if ( !removeFromStart && !canRemoveFromEnd )
  380. removeFromStart = true;
  381. if ( removeFromStart )
  382. calc.RemoveFromStart();
  383. else
  384. calc.RemoveFromEnd();
  385. }
  386. // Always show at least one character of the string.
  387. // Additionally, if there's only one character left, prefer
  388. // "a..." to "...a":
  389. if ( calc.m_nCharsToRemove == len ||
  390. calc.m_nCharsToRemove == len - 1 )
  391. {
  392. return curLine[0] + wxString(wxELLIPSE_REPLACEMENT);
  393. }
  394. }
  395. break;
  396. case wxELLIPSIZE_END:
  397. {
  398. calc.Init(len - 1, 1);
  399. while ( !calc.IsShortEnough() )
  400. calc.RemoveFromStart();
  401. // always show at least one character of the string:
  402. if ( calc.m_nCharsToRemove == len )
  403. return curLine[0] + wxString(wxELLIPSE_REPLACEMENT);
  404. break;
  405. }
  406. case wxELLIPSIZE_NONE:
  407. default:
  408. wxFAIL_MSG("invalid ellipsize mode");
  409. return curLine;
  410. }
  411. return calc.GetEllipsizedText();
  412. }
  413. /* static */
  414. wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
  415. wxEllipsizeMode mode, int maxFinalWidth,
  416. int flags)
  417. {
  418. wxString ret;
  419. // these cannot be cached between different Ellipsize() calls as they can
  420. // change because of e.g. a font change; however we calculate them only once
  421. // when ellipsizing multiline labels:
  422. int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
  423. // NB: we must handle correctly labels with newlines:
  424. wxString curLine;
  425. for ( wxString::const_iterator pc = label.begin(); ; ++pc )
  426. {
  427. if ( pc == label.end() || *pc == wxS('\n') )
  428. {
  429. curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
  430. replacementWidth);
  431. // add this (ellipsized) row to the rest of the label
  432. ret << curLine;
  433. if ( pc == label.end() )
  434. {
  435. // NOTE: this is the return which always exits the function
  436. return ret;
  437. }
  438. else
  439. {
  440. ret << *pc;
  441. curLine.clear();
  442. }
  443. }
  444. // we need to remove mnemonics from the label for correct calculations
  445. else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS) )
  446. {
  447. // pc+1 is safe: at worst we'll be at end()
  448. wxString::const_iterator next = pc + 1;
  449. if ( next != label.end() && *next == wxS('&') )
  450. curLine += wxS('&'); // && becomes &
  451. //else: remove this ampersand
  452. }
  453. // we need also to expand tabs to properly calc their size
  454. else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_FLAGS_EXPAND_TABS) )
  455. {
  456. // Windows natively expands the TABs to 6 spaces. Do the same:
  457. curLine += wxS(" ");
  458. }
  459. else
  460. {
  461. curLine += *pc;
  462. }
  463. }
  464. // this return would generate a
  465. // warning C4702: unreachable code
  466. // with MSVC since the function always exits from inside the loop
  467. //return ret;
  468. }
  469. // ----------------------------------------------------------------------------
  470. // wxStaticBitmap
  471. // ----------------------------------------------------------------------------
  472. #if wxUSE_STATBMP
  473. wxStaticBitmapBase::~wxStaticBitmapBase()
  474. {
  475. // this destructor is required for Darwin
  476. }
  477. wxSize wxStaticBitmapBase::DoGetBestSize() const
  478. {
  479. wxSize best;
  480. wxBitmap bmp = GetBitmap();
  481. if ( bmp.IsOk() )
  482. best = wxSize(bmp.GetWidth(), bmp.GetHeight());
  483. else
  484. // this is completely arbitrary
  485. best = wxSize(16, 16);
  486. CacheBestSize(best);
  487. return best;
  488. }
  489. #endif // wxUSE_STATBMP
  490. #endif // wxUSE_CONTROLS