]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notebook.cpp
Added wxNB_LEFT,RIGHT,BOTTOM styles to alter tab placement
[wxWidgets.git] / src / msw / notebook.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/notebook.cpp
3 // Purpose: implementation of wxNotebook
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.06.98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "notebook.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 // wxWindows
24 #ifndef WX_PRECOMP
25 #include <wx/string.h>
26 #endif // WX_PRECOMP
27
28 #include <wx/log.h>
29 #include <wx/imaglist.h>
30 #include <wx/event.h>
31 #include <wx/control.h>
32 #include <wx/notebook.h>
33
34 #include <wx/msw/private.h>
35
36 // Windows standard headers
37 #ifndef __WIN95__
38 #error "wxNotebook is only supported Windows 95 and above"
39 #endif //Win95
40
41 #include <windowsx.h> // for SetWindowFont
42
43 #ifndef __TWIN32__
44 #ifdef __GNUWIN32__
45 #ifndef wxUSE_NORLANDER_HEADERS
46 #include "wx/msw/gnuwin32/extra.h"
47 #endif
48 #endif
49 #endif
50
51 #if !defined(__GNUWIN32__) || defined(__TWIN32__) || defined(wxUSE_NORLANDER_HEADERS)
52 #include <commctrl.h>
53 #endif
54
55 // ----------------------------------------------------------------------------
56 // macros
57 // ----------------------------------------------------------------------------
58
59 // check that the page index is valid
60 #define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((nPage) < GetPageCount()))
61
62 // hide the ugly cast
63 #define m_hwnd (HWND)GetHWND()
64
65 // ----------------------------------------------------------------------------
66 // event table
67 // ----------------------------------------------------------------------------
68
69 #if !USE_SHARED_LIBRARIES
70 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
71 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
72
73 EVT_SIZE(wxNotebook::OnSize)
74
75 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
76
77 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
78 END_EVENT_TABLE()
79
80 IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
81 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
82 #endif
83
84 // ============================================================================
85 // implementation
86 // ============================================================================
87
88 // ----------------------------------------------------------------------------
89 // wxNotebook construction
90 // ----------------------------------------------------------------------------
91
92 // common part of all ctors
93 void wxNotebook::Init()
94 {
95 m_pImageList = NULL;
96 m_nSelection = -1;
97 }
98
99 // default for dynamic class
100 wxNotebook::wxNotebook()
101 {
102 Init();
103 }
104
105 // the same arguments as for wxControl
106 wxNotebook::wxNotebook(wxWindow *parent,
107 wxWindowID id,
108 const wxPoint& pos,
109 const wxSize& size,
110 long style,
111 const wxString& name)
112 {
113 Init();
114
115 Create(parent, id, pos, size, style, name);
116 }
117
118 // Create() function
119 bool wxNotebook::Create(wxWindow *parent,
120 wxWindowID id,
121 const wxPoint& pos,
122 const wxSize& size,
123 long style,
124 const wxString& name)
125 {
126 // base init
127 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
128 return FALSE;
129
130 // colors and font
131 m_backgroundColour = wxColour(GetSysColor(COLOR_BTNFACE));
132 m_foregroundColour = *wxBLACK ;
133
134 // style
135 m_windowStyle = style | wxTAB_TRAVERSAL;
136
137 long tabStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | TCS_TABS;
138
139 if (m_windowStyle & wxCLIP_CHILDREN)
140 tabStyle |= WS_CLIPCHILDREN;
141 if ( m_windowStyle & wxTC_MULTILINE )
142 tabStyle |= TCS_MULTILINE;
143 if ( m_windowStyle & wxBORDER )
144 tabStyle &= WS_BORDER;
145 if (m_windowStyle & wxNB_FIXEDWIDTH)
146 tabStyle |= TCS_FIXEDWIDTH ;
147 if (m_windowStyle & wxNB_BOTTOM)
148 tabStyle |= TCS_RIGHT;
149 if (m_windowStyle & wxNB_LEFT)
150 tabStyle |= TCS_VERTICAL;
151 if (m_windowStyle & wxNB_RIGHT)
152 tabStyle |= TCS_VERTICAL|TCS_RIGHT;
153
154
155 if ( !MSWCreate(GetId(), GetParent(), WC_TABCONTROL,
156 this, NULL, pos.x, pos.y, size.x, size.y,
157 tabStyle, NULL, 0) )
158 {
159 return FALSE;
160 }
161
162 // Not all compilers recognise SetWindowFont
163 ::SendMessage(GetHwnd(), WM_SETFONT,
164 (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), TRUE);
165
166
167 if ( parent != NULL )
168 parent->AddChild(this);
169
170 SubclassWin(m_hWnd);
171
172 return TRUE;
173 }
174
175 // dtor
176 wxNotebook::~wxNotebook()
177 {
178 }
179
180 // ----------------------------------------------------------------------------
181 // wxNotebook accessors
182 // ----------------------------------------------------------------------------
183 int wxNotebook::GetPageCount() const
184 {
185 // consistency check
186 wxASSERT( (int)m_aPages.Count() == TabCtrl_GetItemCount(m_hwnd) );
187
188 return m_aPages.Count();
189 }
190
191 int wxNotebook::GetRowCount() const
192 {
193 return TabCtrl_GetRowCount(m_hwnd);
194 }
195
196 int wxNotebook::SetSelection(int nPage)
197 {
198 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, _T("notebook page out of range") );
199
200 ChangePage(m_nSelection, nPage);
201
202 return TabCtrl_SetCurSel(m_hwnd, nPage);
203 }
204
205 void wxNotebook::AdvanceSelection(bool bForward)
206 {
207 int nSel = GetSelection();
208 int nMax = GetPageCount() - 1;
209 if ( bForward )
210 SetSelection(nSel == nMax ? 0 : nSel + 1);
211 else
212 SetSelection(nSel == 0 ? nMax : nSel - 1);
213 }
214
215 bool wxNotebook::SetPageText(int nPage, const wxString& strText)
216 {
217 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, _T("notebook page out of range") );
218
219 TC_ITEM tcItem;
220 tcItem.mask = TCIF_TEXT;
221 tcItem.pszText = (wxChar *)strText.c_str();
222
223 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
224 }
225
226 wxString wxNotebook::GetPageText(int nPage) const
227 {
228 wxCHECK_MSG( IS_VALID_PAGE(nPage), _T(""), _T("notebook page out of range") );
229
230 wxChar buf[256];
231 TC_ITEM tcItem;
232 tcItem.mask = TCIF_TEXT;
233 tcItem.pszText = buf;
234 tcItem.cchTextMax = WXSIZEOF(buf);
235
236 wxString str;
237 if ( TabCtrl_GetItem(m_hwnd, nPage, &tcItem) )
238 str = tcItem.pszText;
239
240 return str;
241 }
242
243 int wxNotebook::GetPageImage(int nPage) const
244 {
245 wxCHECK_MSG( IS_VALID_PAGE(nPage), -1, _T("notebook page out of range") );
246
247 TC_ITEM tcItem;
248 tcItem.mask = TCIF_IMAGE;
249
250 return TabCtrl_GetItem(m_hwnd, nPage, &tcItem) ? tcItem.iImage : -1;
251 }
252
253 bool wxNotebook::SetPageImage(int nPage, int nImage)
254 {
255 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, _T("notebook page out of range") );
256
257 TC_ITEM tcItem;
258 tcItem.mask = TCIF_IMAGE;
259 tcItem.iImage = nImage;
260
261 return TabCtrl_SetItem(m_hwnd, nPage, &tcItem) != 0;
262 }
263
264 void wxNotebook::SetImageList(wxImageList* imageList)
265 {
266 m_pImageList = imageList;
267 TabCtrl_SetImageList(m_hwnd, (HIMAGELIST)imageList->GetHIMAGELIST());
268 }
269
270
271 // Windows-only at present. Also, you must use the wxNB_FIXEDWIDTH
272 // style.
273 void wxNotebook::SetTabSize(const wxSize& sz)
274 {
275 ::SendMessage(GetHwnd(), TCM_SETITEMSIZE, 0, MAKELPARAM(sz.x, sz.y));
276 }
277
278 // ----------------------------------------------------------------------------
279 // wxNotebook operations
280 // ----------------------------------------------------------------------------
281
282 // remove one page from the notebook
283 bool wxNotebook::DeletePage(int nPage)
284 {
285 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, _T("notebook page out of range") );
286
287 if ( m_nSelection == nPage ) {
288 // advance selection backwards - the page being deleted shouldn't be left
289 // selected
290 AdvanceSelection(FALSE);
291 }
292
293 TabCtrl_DeleteItem(m_hwnd, nPage);
294
295 delete m_aPages[nPage];
296 m_aPages.Remove(nPage);
297
298 if ( m_aPages.IsEmpty() ) {
299 // no selection if the notebook became empty
300 m_nSelection = -1;
301 }
302
303 return TRUE;
304 }
305
306 // remove one page from the notebook, without deleting
307 bool wxNotebook::RemovePage(int nPage)
308 {
309 wxCHECK_MSG( IS_VALID_PAGE(nPage), FALSE, _T("notebook page out of range") );
310
311 TabCtrl_DeleteItem(m_hwnd, nPage);
312
313 m_aPages.Remove(nPage);
314
315 return TRUE;
316 }
317
318 // remove all pages
319 bool wxNotebook::DeleteAllPages()
320 {
321 int nPageCount = GetPageCount();
322 int nPage;
323 for ( nPage = 0; nPage < nPageCount; nPage++ )
324 delete m_aPages[nPage];
325
326 m_aPages.Clear();
327
328 TabCtrl_DeleteAllItems(m_hwnd);
329
330 return TRUE;
331 }
332
333 // add a page to the notebook
334 bool wxNotebook::AddPage(wxNotebookPage *pPage,
335 const wxString& strText,
336 bool bSelect,
337 int imageId)
338 {
339 return InsertPage(GetPageCount(), pPage, strText, bSelect, imageId);
340 }
341
342 // same as AddPage() but does it at given position
343 bool wxNotebook::InsertPage(int nPage,
344 wxNotebookPage *pPage,
345 const wxString& strText,
346 bool bSelect,
347 int imageId)
348 {
349 wxASSERT( pPage != NULL );
350 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
351
352 // do add the tab to the control
353
354 // init all fields to 0
355 TC_ITEM tcItem;
356 memset(&tcItem, 0, sizeof(tcItem));
357
358 if ( imageId != -1 )
359 {
360 tcItem.mask |= TCIF_IMAGE;
361 tcItem.iImage = imageId;
362 }
363
364 if ( !strText.IsEmpty() )
365 {
366 tcItem.mask |= TCIF_TEXT;
367 tcItem.pszText = (wxChar *)strText.c_str(); // const_cast
368 }
369
370 if ( TabCtrl_InsertItem(m_hwnd, nPage, &tcItem) == -1 ) {
371 wxLogError(_T("Can't create the notebook page '%s'."), strText.c_str());
372
373 return FALSE;
374 }
375
376 // if the inserted page is before the selected one, we must update the
377 // index of the selected page
378 if ( nPage <= m_nSelection )
379 {
380 // one extra page added
381 m_nSelection++;
382 }
383
384 // save the pointer to the page
385 m_aPages.Insert(pPage, nPage);
386
387 // don't show pages by default (we'll need to adjust their size first)
388 HWND hwnd = GetWinHwnd(pPage);
389 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VISIBLE);
390
391 // this updates internal flag too - otherwise it will get out of sync
392 pPage->Show(FALSE);
393
394 // some page should be selected: either this one or the first one if there is
395 // still no selection
396 int selNew = -1;
397 if ( bSelect )
398 selNew = nPage;
399 else if ( m_nSelection == -1 )
400 selNew = 0;
401
402 if ( selNew != -1 )
403 SetSelection(selNew);
404
405 return TRUE;
406 }
407
408 // ----------------------------------------------------------------------------
409 // wxNotebook callbacks
410 // ----------------------------------------------------------------------------
411
412 void wxNotebook::OnSize(wxSizeEvent& event)
413 {
414 // fit the notebook page to the tab control's display area
415 RECT rc;
416 rc.left = rc.top = 0;
417 GetSize((int *)&rc.right, (int *)&rc.bottom);
418
419 TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
420 size_t nCount = m_aPages.Count();
421 for ( size_t nPage = 0; nPage < nCount; nPage++ ) {
422 wxNotebookPage *pPage = m_aPages[nPage];
423 pPage->SetSize(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
424 if ( pPage->GetAutoLayout() )
425 pPage->Layout();
426 }
427
428 event.Skip();
429 }
430
431 void wxNotebook::OnSelChange(wxNotebookEvent& event)
432 {
433 // is it our tab control?
434 if ( event.GetEventObject() == this )
435 {
436 int sel = event.GetOldSelection();
437 if ( sel != -1 )
438 m_aPages[sel]->Show(FALSE);
439
440 sel = event.GetSelection();
441 if ( sel != -1 )
442 {
443 wxNotebookPage *pPage = m_aPages[sel];
444 pPage->Show(TRUE);
445 pPage->SetFocus();
446 }
447
448 m_nSelection = sel;
449 }
450
451 // we want to give others a chance to process this message as well
452 event.Skip();
453 }
454
455 void wxNotebook::OnSetFocus(wxFocusEvent& event)
456 {
457 // set focus to the currently selected page if any
458 if ( m_nSelection != -1 )
459 m_aPages[m_nSelection]->SetFocus();
460
461 event.Skip();
462 }
463
464 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
465 {
466 if ( event.IsWindowChange() ) {
467 // change pages
468 AdvanceSelection(event.GetDirection());
469 }
470 else {
471 // pass to the parent
472 if ( GetParent() ) {
473 event.SetCurrentFocus(this);
474 GetParent()->GetEventHandler()->ProcessEvent(event);
475 }
476 }
477 }
478
479 // ----------------------------------------------------------------------------
480 // wxNotebook base class virtuals
481 // ----------------------------------------------------------------------------
482
483 // override these 2 functions to do nothing: everything is done in OnSize
484
485 void wxNotebook::SetConstraintSizes(bool /* recurse */)
486 {
487 // don't set the sizes of the pages - their correct size is not yet known
488 wxControl::SetConstraintSizes(FALSE);
489 }
490
491 bool wxNotebook::DoPhase(int /* nPhase */)
492 {
493 return TRUE;
494 }
495
496 bool wxNotebook::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result)
497 {
498 wxNotebookEvent event(wxEVT_NULL, m_windowId);
499
500 NMHDR* hdr = (NMHDR *)lParam;
501 switch ( hdr->code ) {
502 case TCN_SELCHANGE:
503 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
504 break;
505
506 case TCN_SELCHANGING:
507 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING);
508 break;
509
510 default:
511 return wxControl::MSWOnNotify(idCtrl, lParam, result);
512 }
513
514 event.SetSelection(TabCtrl_GetCurSel(m_hwnd));
515 event.SetOldSelection(m_nSelection);
516 event.SetEventObject(this);
517 event.SetInt(idCtrl);
518
519 bool processed = GetEventHandler()->ProcessEvent(event);
520 *result = !event.IsAllowed();
521 return processed;
522 }
523
524 // ----------------------------------------------------------------------------
525 // wxNotebook helper functions
526 // ----------------------------------------------------------------------------
527
528 // generate the page changing and changed events, hide the currently active
529 // panel and show the new one
530 void wxNotebook::ChangePage(int nOldSel, int nSel)
531 {
532 // MT-FIXME should use a real semaphore
533 static bool s_bInsideChangePage = FALSE;
534
535 // when we call ProcessEvent(), our own OnSelChange() is called which calls
536 // this function - break the infinite loop
537 if ( s_bInsideChangePage )
538 return;
539
540 // it's not an error (the message may be generated by the tab control itself)
541 // and it may happen - just do nothing
542 if ( nSel == nOldSel )
543 return;
544
545 s_bInsideChangePage = TRUE;
546
547 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId);
548 event.SetSelection(nSel);
549 event.SetOldSelection(nOldSel);
550 event.SetEventObject(this);
551 if ( ProcessEvent(event) && !event.IsAllowed() )
552 {
553 // program doesn't allow the page change
554 s_bInsideChangePage = FALSE;
555 return;
556 }
557
558 event.SetEventType(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED);
559 ProcessEvent(event);
560
561 s_bInsideChangePage = FALSE;
562 }