]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/flatnotebook.py
added missing button state
[wxWidgets.git] / wxPython / wx / lib / flatnotebook.py
1 # --------------------------------------------------------------------------- #
2 # FLATNOTEBOOK Widget wxPython IMPLEMENTATION
3 #
4 # Original C++ Code From Eran. You Can Find It At:
5 #
6 # http://wxforum.shadonet.com/viewtopic.php?t=5761&start=0
7 #
8 # License: wxWidgets license
9 #
10 #
11 # Python Code By:
12 #
13 # Andrea Gavana, @ 02 Oct 2006
14 # Latest Revision: 12 Oct 2006, 20.00 GMT
15 #
16 #
17 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
18 # Write To Me At:
19 #
20 # andrea.gavana@gmail.com
21 # gavana@kpo.kz
22 #
23 # Or, Obviously, To The wxPython Mailing List!!!
24 #
25 #
26 # End Of Comments
27 # --------------------------------------------------------------------------- #
28
29 """
30 The FlatNotebook is a full implementation of the wx.Notebook, and designed to be
31 a drop-in replacement for wx.Notebook. The API functions are similar so one can
32 expect the function to behave in the same way.
33
34 Some features:
35
36 - The buttons are highlighted a la Firefox style
37 - The scrolling is done for bulks of tabs (so, the scrolling is faster and better)
38 - The buttons area is never overdrawn by tabs (unlike many other implementations I saw)
39 - It is a generic control
40 - Currently there are 4 differnt styles - VC8, VC 71, Standard and Fancy
41 - Mouse middle click can be used to close tabs
42 - A function to add right click menu for tabs (simple as SetRightClickMenu)
43 - All styles has bottom style as well (they can be drawn in the bottom of screen)
44 - An option to hide 'X' button or navigation buttons (separately)
45 - Gradient coloring of the selected tabs and border
46 - Support for drag 'n' drop of tabs, both in the same notebook or to another notebook
47 - Possibility to have closing button on the active tab directly
48 - Support for disabled tabs
49 - Colours for active/inactive tabs, and captions
50 - Background of tab area can be painted in gradient (VC8 style only)
51 - Colourful tabs - a random gentle colour is generated for each new tab (very cool, VC8 style only)
52
53
54 And much more.
55
56
57 License And Version:
58
59 FlatNotebook Is Freeware And Distributed Under The wxPython License.
60
61 Latest Revision: Andrea Gavana @ 12 Oct 2006, 20.00 GMT
62
63 Version 2.0.
64
65 @undocumented: FNB_HEIGHT_SPACER, VERTICAL_BORDER_PADDING, VC8_SHAPE_LEN,
66 wxEVT*, left_arrow_*, right_arrow*, x_button*, down_arrow*,
67 FNBDragInfo, FNBDropTarget, GetMondrian*
68 """
69
70 __docformat__ = "epytext"
71
72
73 #----------------------------------------------------------------------
74 # Beginning Of FLATNOTEBOOK wxPython Code
75 #----------------------------------------------------------------------
76
77 import wx
78 import random
79 import math
80 import weakref
81 import cPickle
82
83 # Check for the new method in 2.7 (not present in 2.6.3.3)
84 if wx.VERSION_STRING < "2.7":
85 wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point)
86
87 FNB_HEIGHT_SPACER = 10
88
89 # Use Visual Studio 2003 (VC7.1) style for tabs
90 FNB_VC71 = 1
91 """Use Visual Studio 2003 (VC7.1) style for tabs"""
92
93 # Use fancy style - square tabs filled with gradient coloring
94 FNB_FANCY_TABS = 2
95 """Use fancy style - square tabs filled with gradient coloring"""
96
97 # Draw thin border around the page
98 FNB_TABS_BORDER_SIMPLE = 4
99 """Draw thin border around the page"""
100
101 # Do not display the 'X' button
102 FNB_NO_X_BUTTON = 8
103 """Do not display the 'X' button"""
104
105 # Do not display the Right / Left arrows
106 FNB_NO_NAV_BUTTONS = 16
107 """Do not display the right/left arrows"""
108
109 # Use the mouse middle button for cloing tabs
110 FNB_MOUSE_MIDDLE_CLOSES_TABS = 32
111 """Use the mouse middle button for cloing tabs"""
112
113 # Place tabs at bottom - the default is to place them
114 # at top
115 FNB_BOTTOM = 64
116 """Place tabs at bottom - the default is to place them at top"""
117
118 # Disable dragging of tabs
119 FNB_NODRAG = 128
120 """Disable dragging of tabs"""
121
122 # Use Visual Studio 2005 (VC8) style for tabs
123 FNB_VC8 = 256
124 """Use Visual Studio 2005 (VC8) style for tabs"""
125
126 # Place 'X' on a tab
127 FNB_X_ON_TAB = 512
128 """Place 'X' close button on the active tab"""
129
130 FNB_BACKGROUND_GRADIENT = 1024
131 """Use gradients to paint the tabs background"""
132
133 FNB_COLORFUL_TABS = 2048
134 """Use colourful tabs (VC8 style only)"""
135
136 # Style to close tab using double click - styles 1024, 2048 are reserved
137 FNB_DCLICK_CLOSES_TABS = 4096
138 """Style to close tab using double click"""
139
140 FNB_SMART_TABS = 8192
141 """Use Smart Tabbing, like Alt+Tab on Windows"""
142
143 FNB_DROPDOWN_TABS_LIST = 16384
144 """Use a dropdown menu on the left in place of the arrows"""
145
146 FNB_ALLOW_FOREIGN_DND = 32768
147 """Allows drag 'n' drop operations between different L{FlatNotebook}s"""
148
149 FNB_HIDE_ON_SINGLE_TAB = 65536
150 """Hides the Page Container when there is one or fewer tabs"""
151
152 VERTICAL_BORDER_PADDING = 4
153
154 # Button size is a 16x16 xpm bitmap
155 BUTTON_SPACE = 16
156 """Button size is a 16x16 xpm bitmap"""
157
158 VC8_SHAPE_LEN = 16
159
160 MASK_COLOR = wx.Colour(0, 128, 128)
161 """Mask colour for the arrow bitmaps"""
162
163 # Button status
164 FNB_BTN_PRESSED = 2
165 """Navigation button is pressed"""
166 FNB_BTN_HOVER = 1
167 """Navigation button is hovered"""
168 FNB_BTN_NONE = 0
169 """No navigation"""
170
171 # Hit Test results
172 FNB_TAB = 1 # On a tab
173 """Indicates mouse coordinates inside a tab"""
174 FNB_X = 2 # On the X button
175 """Indicates mouse coordinates inside the I{X} region"""
176 FNB_TAB_X = 3 # On the 'X' button (tab's X button)
177 """Indicates mouse coordinates inside the I{X} region in a tab"""
178 FNB_LEFT_ARROW = 4 # On the rotate left arrow button
179 """Indicates mouse coordinates inside the left arrow region"""
180 FNB_RIGHT_ARROW = 5 # On the rotate right arrow button
181 """Indicates mouse coordinates inside the right arrow region"""
182 FNB_DROP_DOWN_ARROW = 6 # On the drop down arrow button
183 """Indicates mouse coordinates inside the drop down arrow region"""
184 FNB_NOWHERE = 0 # Anywhere else
185 """Indicates mouse coordinates not on any tab of the notebook"""
186
187 FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS | FNB_HIDE_ON_SINGLE_TAB
188 """L{FlatNotebook} default style"""
189
190 # FlatNotebook Events:
191 # wxEVT_FLATNOTEBOOK_PAGE_CHANGED: Event Fired When You Switch Page;
192 # wxEVT_FLATNOTEBOOK_PAGE_CHANGING: Event Fired When You Are About To Switch
193 # Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call
194 # event.Skip() In Your Event Handler;
195 # wxEVT_FLATNOTEBOOK_PAGE_CLOSING: Event Fired When A Page Is Closing, But
196 # You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip()
197 # In Your Event Handler;
198 # wxEVT_FLATNOTEBOOK_PAGE_CLOSED: Event Fired When A Page Is Closed.
199 # wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU: Event Fired When A Menu Pops-up In A Tab.
200
201 wxEVT_FLATNOTEBOOK_PAGE_CHANGED = wx.NewEventType()
202 wxEVT_FLATNOTEBOOK_PAGE_CHANGING = wx.NewEventType()
203 wxEVT_FLATNOTEBOOK_PAGE_CLOSING = wx.NewEventType()
204 wxEVT_FLATNOTEBOOK_PAGE_CLOSED = wx.NewEventType()
205 wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType()
206
207 #-----------------------------------#
208 # FlatNotebookEvent
209 #-----------------------------------#
210
211 EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGED, 1)
212 """Notify client objects when the active page in L{FlatNotebook}
213 has changed."""
214 EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, 1)
215 """Notify client objects when the active page in L{FlatNotebook}
216 is about to change."""
217 EVT_FLATNOTEBOOK_PAGE_CLOSING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, 1)
218 """Notify client objects when a page in L{FlatNotebook} is closing."""
219 EVT_FLATNOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, 1)
220 """Notify client objects when a page in L{FlatNotebook} has been closed."""
221 EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, 1)
222 """Notify client objects when a pop-up menu should appear next to a tab."""
223
224
225 # Some icons in XPM format
226
227 left_arrow_disabled_xpm = [
228 " 16 16 8 1",
229 "` c #008080",
230 ". c #555555",
231 "# c #000000",
232 "a c #000000",
233 "b c #000000",
234 "c c #000000",
235 "d c #000000",
236 "e c #000000",
237 "````````````````",
238 "````````````````",
239 "````````````````",
240 "````````.```````",
241 "```````..```````",
242 "``````.`.```````",
243 "`````.``.```````",
244 "````.```.```````",
245 "`````.``.```````",
246 "``````.`.```````",
247 "```````..```````",
248 "````````.```````",
249 "````````````````",
250 "````````````````",
251 "````````````````",
252 "````````````````"
253 ]
254
255 x_button_pressed_xpm = [
256 " 16 16 8 1",
257 "` c #008080",
258 ". c #4766e0",
259 "# c #9e9ede",
260 "a c #000000",
261 "b c #000000",
262 "c c #000000",
263 "d c #000000",
264 "e c #000000",
265 "````````````````",
266 "`..............`",
267 "`.############.`",
268 "`.############.`",
269 "`.############.`",
270 "`.###aa####aa#.`",
271 "`.####aa##aa##.`",
272 "`.#####aaaa###.`",
273 "`.######aa####.`",
274 "`.#####aaaa###.`",
275 "`.####aa##aa##.`",
276 "`.###aa####aa#.`",
277 "`.############.`",
278 "`..............`",
279 "````````````````",
280 "````````````````"
281 ]
282
283
284 left_arrow_xpm = [
285 " 16 16 8 1",
286 "` c #008080",
287 ". c #555555",
288 "# c #000000",
289 "a c #000000",
290 "b c #000000",
291 "c c #000000",
292 "d c #000000",
293 "e c #000000",
294 "````````````````",
295 "````````````````",
296 "````````````````",
297 "````````.```````",
298 "```````..```````",
299 "``````...```````",
300 "`````....```````",
301 "````.....```````",
302 "`````....```````",
303 "``````...```````",
304 "```````..```````",
305 "````````.```````",
306 "````````````````",
307 "````````````````",
308 "````````````````",
309 "````````````````"
310 ]
311
312 x_button_hilite_xpm = [
313 " 16 16 8 1",
314 "` c #008080",
315 ". c #4766e0",
316 "# c #c9dafb",
317 "a c #000000",
318 "b c #000000",
319 "c c #000000",
320 "d c #000000",
321 "e c #000000",
322 "````````````````",
323 "`..............`",
324 "`.############.`",
325 "`.############.`",
326 "`.##aa####aa##.`",
327 "`.###aa##aa###.`",
328 "`.####aaaa####.`",
329 "`.#####aa#####.`",
330 "`.####aaaa####.`",
331 "`.###aa##aa###.`",
332 "`.##aa####aa##.`",
333 "`.############.`",
334 "`.############.`",
335 "`..............`",
336 "````````````````",
337 "````````````````"
338 ]
339
340 x_button_xpm = [
341 " 16 16 8 1",
342 "` c #008080",
343 ". c #555555",
344 "# c #000000",
345 "a c #000000",
346 "b c #000000",
347 "c c #000000",
348 "d c #000000",
349 "e c #000000",
350 "````````````````",
351 "````````````````",
352 "````````````````",
353 "````````````````",
354 "````..````..````",
355 "`````..``..`````",
356 "``````....``````",
357 "```````..```````",
358 "``````....``````",
359 "`````..``..`````",
360 "````..````..````",
361 "````````````````",
362 "````````````````",
363 "````````````````",
364 "````````````````",
365 "````````````````"
366 ]
367
368 left_arrow_pressed_xpm = [
369 " 16 16 8 1",
370 "` c #008080",
371 ". c #4766e0",
372 "# c #9e9ede",
373 "a c #000000",
374 "b c #000000",
375 "c c #000000",
376 "d c #000000",
377 "e c #000000",
378 "````````````````",
379 "`..............`",
380 "`.############.`",
381 "`.############.`",
382 "`.#######a####.`",
383 "`.######aa####.`",
384 "`.#####aaa####.`",
385 "`.####aaaa####.`",
386 "`.###aaaaa####.`",
387 "`.####aaaa####.`",
388 "`.#####aaa####.`",
389 "`.######aa####.`",
390 "`.#######a####.`",
391 "`..............`",
392 "````````````````",
393 "````````````````"
394 ]
395
396 left_arrow_hilite_xpm = [
397 " 16 16 8 1",
398 "` c #008080",
399 ". c #4766e0",
400 "# c #c9dafb",
401 "a c #000000",
402 "b c #000000",
403 "c c #000000",
404 "d c #000000",
405 "e c #000000",
406 "````````````````",
407 "`..............`",
408 "`.############.`",
409 "`.######a#####.`",
410 "`.#####aa#####.`",
411 "`.####aaa#####.`",
412 "`.###aaaa#####.`",
413 "`.##aaaaa#####.`",
414 "`.###aaaa#####.`",
415 "`.####aaa#####.`",
416 "`.#####aa#####.`",
417 "`.######a#####.`",
418 "`.############.`",
419 "`..............`",
420 "````````````````",
421 "````````````````"
422 ]
423
424 right_arrow_disabled_xpm = [
425 " 16 16 8 1",
426 "` c #008080",
427 ". c #555555",
428 "# c #000000",
429 "a c #000000",
430 "b c #000000",
431 "c c #000000",
432 "d c #000000",
433 "e c #000000",
434 "````````````````",
435 "````````````````",
436 "````````````````",
437 "```````.````````",
438 "```````..```````",
439 "```````.`.``````",
440 "```````.``.`````",
441 "```````.```.````",
442 "```````.``.`````",
443 "```````.`.``````",
444 "```````..```````",
445 "```````.````````",
446 "````````````````",
447 "````````````````",
448 "````````````````",
449 "````````````````"
450 ]
451
452 right_arrow_hilite_xpm = [
453 " 16 16 8 1",
454 "` c #008080",
455 ". c #4766e0",
456 "# c #c9dafb",
457 "a c #000000",
458 "b c #000000",
459 "c c #000000",
460 "d c #000000",
461 "e c #000000",
462 "````````````````",
463 "`..............`",
464 "`.############.`",
465 "`.####a#######.`",
466 "`.####aa######.`",
467 "`.####aaa#####.`",
468 "`.####aaaa####.`",
469 "`.####aaaaa###.`",
470 "`.####aaaa####.`",
471 "`.####aaa#####.`",
472 "`.####aa######.`",
473 "`.####a#######.`",
474 "`.############.`",
475 "`..............`",
476 "````````````````",
477 "````````````````"
478 ]
479
480 right_arrow_pressed_xpm = [
481 " 16 16 8 1",
482 "` c #008080",
483 ". c #4766e0",
484 "# c #9e9ede",
485 "a c #000000",
486 "b c #000000",
487 "c c #000000",
488 "d c #000000",
489 "e c #000000",
490 "````````````````",
491 "`..............`",
492 "`.############.`",
493 "`.############.`",
494 "`.#####a######.`",
495 "`.#####aa#####.`",
496 "`.#####aaa####.`",
497 "`.#####aaaa###.`",
498 "`.#####aaaaa##.`",
499 "`.#####aaaa###.`",
500 "`.#####aaa####.`",
501 "`.#####aa#####.`",
502 "`.#####a######.`",
503 "`..............`",
504 "````````````````",
505 "````````````````"
506 ]
507
508
509 right_arrow_xpm = [
510 " 16 16 8 1",
511 "` c #008080",
512 ". c #555555",
513 "# c #000000",
514 "a c #000000",
515 "b c #000000",
516 "c c #000000",
517 "d c #000000",
518 "e c #000000",
519 "````````````````",
520 "````````````````",
521 "````````````````",
522 "```````.````````",
523 "```````..```````",
524 "```````...``````",
525 "```````....`````",
526 "```````.....````",
527 "```````....`````",
528 "```````...``````",
529 "```````..```````",
530 "```````.````````",
531 "````````````````",
532 "````````````````",
533 "````````````````",
534 "````````````````"
535 ]
536
537 down_arrow_hilite_xpm = [
538 " 16 16 8 1",
539 "` c #008080",
540 ". c #4766e0",
541 "# c #c9dafb",
542 "a c #000000",
543 "b c #000000",
544 "c c #000000",
545 "d c #000000",
546 "e c #000000",
547 "````````````````",
548 "``.............`",
549 "``.###########.`",
550 "``.###########.`",
551 "``.###########.`",
552 "``.#aaaaaaaaa#.`",
553 "``.##aaaaaaa##.`",
554 "``.###aaaaa###.`",
555 "``.####aaa####.`",
556 "``.#####a#####.`",
557 "``.###########.`",
558 "``.###########.`",
559 "``.###########.`",
560 "``.............`",
561 "````````````````",
562 "````````````````"
563 ]
564
565 down_arrow_pressed_xpm = [
566 " 16 16 8 1",
567 "` c #008080",
568 ". c #4766e0",
569 "# c #9e9ede",
570 "a c #000000",
571 "b c #000000",
572 "c c #000000",
573 "d c #000000",
574 "e c #000000",
575 "````````````````",
576 "``.............`",
577 "``.###########.`",
578 "``.###########.`",
579 "``.###########.`",
580 "``.###########.`",
581 "``.###########.`",
582 "``.#aaaaaaaaa#.`",
583 "``.##aaaaaaa##.`",
584 "``.###aaaaa###.`",
585 "``.####aaa####.`",
586 "``.#####a#####.`",
587 "``.###########.`",
588 "``.............`",
589 "````````````````",
590 "````````````````"
591 ]
592
593
594 down_arrow_xpm = [
595 " 16 16 8 1",
596 "` c #008080",
597 ". c #000000",
598 "# c #000000",
599 "a c #000000",
600 "b c #000000",
601 "c c #000000",
602 "d c #000000",
603 "e c #000000",
604 "````````````````",
605 "````````````````",
606 "````````````````",
607 "````````````````",
608 "````````````````",
609 "````````````````",
610 "````.........```",
611 "`````.......````",
612 "``````.....`````",
613 "```````...``````",
614 "````````.```````",
615 "````````````````",
616 "````````````````",
617 "````````````````",
618 "````````````````",
619 "````````````````"
620 ]
621
622
623 #----------------------------------------------------------------------
624 def GetMondrianData():
625 return \
626 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\
627 \x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\
628 ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\
629 o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\
630 \xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\
631 \x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\
632 \x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82'
633
634
635 def GetMondrianBitmap():
636 return wx.BitmapFromImage(GetMondrianImage().Scale(16, 16))
637
638
639 def GetMondrianImage():
640 import cStringIO
641 stream = cStringIO.StringIO(GetMondrianData())
642 return wx.ImageFromStream(stream)
643
644
645 def GetMondrianIcon():
646 icon = wx.EmptyIcon()
647 icon.CopyFromBitmap(GetMondrianBitmap())
648 return icon
649 #----------------------------------------------------------------------
650
651
652 def LightColour(color, percent):
653 """ Brighten input colour by percent. """
654
655 end_color = wx.WHITE
656
657 rd = end_color.Red() - color.Red()
658 gd = end_color.Green() - color.Green()
659 bd = end_color.Blue() - color.Blue()
660
661 high = 100
662
663 # We take the percent way of the color from color -. white
664 i = percent
665 r = color.Red() + ((i*rd*100)/high)/100
666 g = color.Green() + ((i*gd*100)/high)/100
667 b = color.Blue() + ((i*bd*100)/high)/100
668 return wx.Colour(r, g, b)
669
670
671 def RandomColour():
672 """ Creates a random colour. """
673
674 r = random.randint(0, 255) # Random value betweem 0-255
675 g = random.randint(0, 255) # Random value betweem 0-255
676 b = random.randint(0, 255) # Random value betweem 0-255
677
678 return wx.Colour(r, g, b)
679
680
681 def PaintStraightGradientBox(dc, rect, startColor, endColor, vertical=True):
682 """ Draws a gradient colored box from startColor to endColor. """
683
684 rd = endColor.Red() - startColor.Red()
685 gd = endColor.Green() - startColor.Green()
686 bd = endColor.Blue() - startColor.Blue()
687
688 # Save the current pen and brush
689 savedPen = dc.GetPen()
690 savedBrush = dc.GetBrush()
691
692 if vertical:
693 high = rect.GetHeight()-1
694 else:
695 high = rect.GetWidth()-1
696
697 if high < 1:
698 return
699
700 for i in xrange(high+1):
701
702 r = startColor.Red() + ((i*rd*100)/high)/100
703 g = startColor.Green() + ((i*gd*100)/high)/100
704 b = startColor.Blue() + ((i*bd*100)/high)/100
705
706 p = wx.Pen(wx.Colour(r, g, b))
707 dc.SetPen(p)
708
709 if vertical:
710 dc.DrawLine(rect.x, rect.y+i, rect.x+rect.width, rect.y+i)
711 else:
712 dc.DrawLine(rect.x+i, rect.y, rect.x+i, rect.y+rect.height)
713
714 # Restore the pen and brush
715 dc.SetPen(savedPen)
716 dc.SetBrush(savedBrush)
717
718
719 # ---------------------------------------------------------------------------- #
720 # Class FNBDropSource
721 # Gives Some Custom UI Feedback during the DnD Operations
722 # ---------------------------------------------------------------------------- #
723
724 class FNBDropSource(wx.DropSource):
725 """
726 Give some custom UI feedback during the drag and drop operation in this
727 function. It is called on each mouse move, so your implementation must
728 not be too slow.
729 """
730
731 def __init__(self, win):
732 """ Default class constructor. Used internally. """
733
734 wx.DropSource.__init__(self, win)
735 self._win = win
736
737
738 def GiveFeedback(self, effect):
739 """ Provides user with a nice feedback when tab is being dragged. """
740
741 self._win.DrawDragHint()
742 return False
743
744
745 # ---------------------------------------------------------------------------- #
746 # Class FNBDragInfo
747 # Stores All The Information To Allow Drag And Drop Between Different
748 # FlatNotebooks.
749 # ---------------------------------------------------------------------------- #
750
751 class FNBDragInfo:
752
753 _map = weakref.WeakValueDictionary()
754
755 def __init__(self, container, pageindex):
756 """ Default class constructor. """
757
758 self._id = id(container)
759 FNBDragInfo._map[self._id] = container
760 self._pageindex = pageindex
761
762
763 def GetContainer(self):
764 """ Returns the L{FlatNotebook} page (usually a panel). """
765
766 return FNBDragInfo._map.get(self._id, None)
767
768
769 def GetPageIndex(self):
770 """ Returns the page index associated with a page. """
771
772 return self._pageindex
773
774
775 # ---------------------------------------------------------------------------- #
776 # Class FNBDropTarget
777 # Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between
778 # Different FlatNotebooks.
779 # ---------------------------------------------------------------------------- #
780
781 class FNBDropTarget(wx.DropTarget):
782
783 def __init__(self, parent):
784 """ Default class constructor. """
785
786 wx.DropTarget.__init__(self)
787
788 self._parent = parent
789 self._dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook"))
790 self.SetDataObject(self._dataobject)
791
792
793 def OnData(self, x, y, dragres):
794 """ Handles the OnData() method to call the real DnD routine. """
795
796 if not self.GetData():
797 return wx.DragNone
798
799 draginfo = self._dataobject.GetData()
800 drginfo = cPickle.loads(draginfo)
801
802 return self._parent.OnDropTarget(x, y, drginfo.GetPageIndex(), drginfo.GetContainer())
803
804
805 # ---------------------------------------------------------------------------- #
806 # Class PageInfo
807 # Contains parameters for every FlatNotebook page
808 # ---------------------------------------------------------------------------- #
809
810 class PageInfo:
811 """
812 This class holds all the information (caption, image, etc...) belonging to a
813 single tab in L{FlatNotebook}.
814 """
815
816 def __init__(self, caption="", imageindex=-1, tabangle=0, enabled=True):
817 """
818 Default Class Constructor.
819
820 Parameters:
821 @param caption: the tab caption;
822 @param imageindex: the tab image index based on the assigned (set) wx.ImageList (if any);
823 @param tabangle: the tab angle (only on standard tabs, from 0 to 15 degrees);
824 @param enabled: sets enabled or disabled the tab.
825 """
826
827 self._strCaption = caption
828 self._TabAngle = tabangle
829 self._ImageIndex = imageindex
830 self._bEnabled = enabled
831 self._pos = wx.Point(-1, -1)
832 self._size = wx.Size(-1, -1)
833 self._region = wx.Region()
834 self._xRect = wx.Rect()
835 self._color = None
836
837
838 def SetCaption(self, value):
839 """ Sets the tab caption. """
840
841 self._strCaption = value
842
843
844 def GetCaption(self):
845 """ Returns the tab caption. """
846
847 return self._strCaption
848
849
850 def SetPosition(self, value):
851 """ Sets the tab position. """
852
853 self._pos = value
854
855
856 def GetPosition(self):
857 """ Returns the tab position. """
858
859 return self._pos
860
861
862 def SetSize(self, value):
863 """ Sets the tab size. """
864
865 self._size = value
866
867
868 def GetSize(self):
869 """ Returns the tab size. """
870
871 return self._size
872
873
874 def SetTabAngle(self, value):
875 """ Sets the tab header angle (0 <= tab <= 15 degrees). """
876
877 self._TabAngle = min(45, value)
878
879
880 def GetTabAngle(self):
881 """ Returns the tab angle. """
882
883 return self._TabAngle
884
885
886 def SetImageIndex(self, value):
887 """ Sets the tab image index. """
888
889 self._ImageIndex = value
890
891
892 def GetImageIndex(self):
893 """ Returns the tab umage index. """
894
895 return self._ImageIndex
896
897
898 def GetEnabled(self):
899 """ Returns whether the tab is enabled or not. """
900
901 return self._bEnabled
902
903
904 def Enable(self, enabled):
905 """ Sets the tab enabled or disabled. """
906
907 self._bEnabled = enabled
908
909
910 def SetRegion(self, points=[]):
911 """ Sets the tab region. """
912
913 self._region = wx.RegionFromPoints(points)
914
915
916 def GetRegion(self):
917 """ Returns the tab region. """
918
919 return self._region
920
921
922 def SetXRect(self, xrect):
923 """ Sets the button 'X' area rect. """
924
925 self._xRect = xrect
926
927
928 def GetXRect(self):
929 """ Returns the button 'X' area rect. """
930
931 return self._xRect
932
933
934 def GetColour(self):
935 """ Returns the tab colour. """
936
937 return self._color
938
939
940 def SetColour(self, color):
941 """ Sets the tab colour. """
942
943 self._color = color
944
945
946 # ---------------------------------------------------------------------------- #
947 # Class FlatNotebookEvent
948 # ---------------------------------------------------------------------------- #
949
950 class FlatNotebookEvent(wx.PyCommandEvent):
951 """
952 This events will be sent when a EVT_FLATNOTEBOOK_PAGE_CHANGED,
953 EVT_FLATNOTEBOOK_PAGE_CHANGING, EVT_FLATNOTEBOOK_PAGE_CLOSING,
954 EVT_FLATNOTEBOOK_PAGE_CLOSED and EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU is
955 mapped in the parent.
956 """
957
958 def __init__(self, eventType, id=1, nSel=-1, nOldSel=-1):
959 """ Default class constructor. """
960
961 wx.PyCommandEvent.__init__(self, eventType, id)
962 self._eventType = eventType
963
964 self.notify = wx.NotifyEvent(eventType, id)
965
966
967 def GetNotifyEvent(self):
968 """Returns the actual wx.NotifyEvent."""
969
970 return self.notify
971
972
973 def IsAllowed(self):
974 """Returns whether the event is allowed or not."""
975
976 return self.notify.IsAllowed()
977
978
979 def Veto(self):
980 """Vetos the event."""
981
982 self.notify.Veto()
983
984
985 def Allow(self):
986 """The event is allowed."""
987
988 self.notify.Allow()
989
990
991 def SetSelection(self, nSel):
992 """ Sets event selection. """
993
994 self._selection = nSel
995
996
997 def SetOldSelection(self, nOldSel):
998 """ Sets old event selection. """
999
1000 self._oldselection = nOldSel
1001
1002
1003 def GetSelection(self):
1004 """ Returns event selection. """
1005
1006 return self._selection
1007
1008
1009 def GetOldSelection(self):
1010 """ Returns old event selection """
1011
1012 return self._oldselection
1013
1014
1015 # ---------------------------------------------------------------------------- #
1016 # Class TabNavigatorWindow
1017 # ---------------------------------------------------------------------------- #
1018
1019 class TabNavigatorWindow(wx.Dialog):
1020 """
1021 This class is used to create a modal dialog that enables "Smart Tabbing",
1022 similar to what you would get by hitting Alt+Tab on Windows.
1023 """
1024
1025 def __init__(self, parent=None):
1026 """ Default class constructor. Used internally."""
1027
1028 wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
1029
1030 self._selectedItem = -1
1031 self._indexMap = []
1032
1033 self._bmp = GetMondrianBitmap()
1034
1035 sz = wx.BoxSizer(wx.VERTICAL)
1036
1037 self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
1038
1039 mem_dc = wx.MemoryDC()
1040 mem_dc.SelectObject(wx.EmptyBitmap(1,1))
1041 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1042 font.SetWeight(wx.BOLD)
1043 mem_dc.SetFont(font)
1044
1045 panelHeight = mem_dc.GetCharHeight()
1046 panelHeight += 4 # Place a spacer of 2 pixels
1047
1048 # Out signpost bitmap is 24 pixels
1049 if panelHeight < 24:
1050 panelHeight = 24
1051
1052 self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
1053
1054 sz.Add(self._panel)
1055 sz.Add(self._listBox, 1, wx.EXPAND)
1056
1057 self.SetSizer(sz)
1058
1059 # Connect events to the list box
1060 self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
1061 self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
1062 self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
1063
1064 # Connect paint event to the panel
1065 self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
1066 self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
1067
1068 self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
1069 self._listBox.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
1070 self.PopulateListControl(parent)
1071
1072 self.GetSizer().Fit(self)
1073 self.GetSizer().SetSizeHints(self)
1074 self.GetSizer().Layout()
1075 self.Centre()
1076
1077
1078 def OnKeyUp(self, event):
1079 """Handles the wx.EVT_KEY_UP for the L{TabNavigatorWindow}."""
1080
1081 if event.GetKeyCode() == wx.WXK_CONTROL:
1082 self.CloseDialog()
1083
1084
1085 def OnNavigationKey(self, event):
1086 """Handles the wx.EVT_NAVIGATION_KEY for the L{TabNavigatorWindow}. """
1087
1088 selected = self._listBox.GetSelection()
1089 bk = self.GetParent()
1090 maxItems = bk.GetPageCount()
1091
1092 if event.GetDirection():
1093
1094 # Select next page
1095 if selected == maxItems - 1:
1096 itemToSelect = 0
1097 else:
1098 itemToSelect = selected + 1
1099
1100 else:
1101
1102 # Previous page
1103 if selected == 0:
1104 itemToSelect = maxItems - 1
1105 else:
1106 itemToSelect = selected - 1
1107
1108 self._listBox.SetSelection(itemToSelect)
1109
1110
1111 def PopulateListControl(self, book):
1112 """Populates the L{TabNavigatorWindow} listbox with a list of tabs."""
1113
1114 selection = book.GetSelection()
1115 count = book.GetPageCount()
1116
1117 self._listBox.Append(book.GetPageText(selection))
1118 self._indexMap.append(selection)
1119
1120 prevSel = book.GetPreviousSelection()
1121
1122 if prevSel != wx.NOT_FOUND:
1123
1124 # Insert the previous selection as second entry
1125 self._listBox.Append(book.GetPageText(prevSel))
1126 self._indexMap.append(prevSel)
1127
1128 for c in xrange(count):
1129
1130 # Skip selected page
1131 if c == selection:
1132 continue
1133
1134 # Skip previous selected page as well
1135 if c == prevSel:
1136 continue
1137
1138 self._listBox.Append(book.GetPageText(c))
1139 self._indexMap.append(c)
1140
1141 # Select the next entry after the current selection
1142 self._listBox.SetSelection(0)
1143 dummy = wx.NavigationKeyEvent()
1144 dummy.SetDirection(True)
1145 self.OnNavigationKey(dummy)
1146
1147
1148 def OnItemSelected(self, event):
1149 """Handles the wx.EVT_LISTBOX_DCLICK event for the wx.ListBox inside L{TabNavigatorWindow}. """
1150
1151 self.CloseDialog()
1152
1153
1154 def CloseDialog(self):
1155 """Closes the L{TabNavigatorWindow} dialog, setting selection in L{FlatNotebook}."""
1156
1157 bk = self.GetParent()
1158 self._selectedItem = self._listBox.GetSelection()
1159 iter = self._indexMap[self._selectedItem]
1160 bk._pages.FireEvent(iter)
1161 self.EndModal(wx.ID_OK)
1162
1163
1164 def OnPanelPaint(self, event):
1165 """Handles the wx.EVT_PAINT event for L{TabNavigatorWindow} top panel. """
1166
1167 dc = wx.PaintDC(self._panel)
1168 rect = self._panel.GetClientRect()
1169
1170 bmp = wx.EmptyBitmap(rect.width, rect.height)
1171
1172 mem_dc = wx.MemoryDC()
1173 mem_dc.SelectObject(bmp)
1174
1175 endColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
1176 startColour = LightColour(endColour, 50)
1177 PaintStraightGradientBox(mem_dc, rect, startColour, endColour)
1178
1179 # Draw the caption title and place the bitmap
1180 # get the bitmap optimal position, and draw it
1181 bmpPt, txtPt = wx.Point(), wx.Point()
1182 bmpPt.y = (rect.height - self._bmp.GetHeight())/2
1183 bmpPt.x = 3
1184 mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
1185
1186 # get the text position, and draw it
1187 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1188 font.SetWeight(wx.BOLD)
1189 mem_dc.SetFont(font)
1190 fontHeight = mem_dc.GetCharHeight()
1191
1192 txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
1193 txtPt.y = (rect.height - fontHeight)/2
1194 mem_dc.SetTextForeground(wx.WHITE)
1195 mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
1196 mem_dc.SelectObject(wx.NullBitmap)
1197
1198 dc.DrawBitmap(bmp, 0, 0)
1199
1200
1201 def OnPanelEraseBg(self, event):
1202 """Handles the wx.EVT_ERASE_BACKGROUND event for L{TabNavigatorWindow} top panel. """
1203
1204 pass
1205
1206
1207 # ---------------------------------------------------------------------------- #
1208 # Class FNBRenderer
1209 # ---------------------------------------------------------------------------- #
1210
1211 class FNBRenderer:
1212 """
1213 Parent class for the 4 renderers defined: I{Standard}, I{VC71}, I{Fancy}
1214 and I{VC8}. This class implements the common methods of all 4 renderers.
1215 @undocumented: _GetBitmap*
1216 """
1217
1218 def __init__(self):
1219 """Default class constructor. """
1220
1221 self._tabXBgBmp = wx.EmptyBitmap(16, 16)
1222 self._xBgBmp = wx.EmptyBitmap(16, 14)
1223 self._leftBgBmp = wx.EmptyBitmap(16, 14)
1224 self._rightBgBmp = wx.EmptyBitmap(16, 14)
1225 self._tabHeight = None
1226
1227
1228 def GetLeftButtonPos(self, pageContainer):
1229 """ Returns the left button position in the navigation area. """
1230
1231 pc = pageContainer
1232 style = pc.GetParent().GetWindowStyleFlag()
1233 rect = pc.GetClientRect()
1234 clientWidth = rect.width
1235
1236 if style & FNB_NO_X_BUTTON:
1237 return clientWidth - 38
1238 else:
1239 return clientWidth - 54
1240
1241
1242
1243 def GetRightButtonPos(self, pageContainer):
1244 """ Returns the right button position in the navigation area. """
1245
1246 pc = pageContainer
1247 style = pc.GetParent().GetWindowStyleFlag()
1248 rect = pc.GetClientRect()
1249 clientWidth = rect.width
1250
1251 if style & FNB_NO_X_BUTTON:
1252 return clientWidth - 22
1253 else:
1254 return clientWidth - 38
1255
1256
1257 def GetDropArrowButtonPos(self, pageContainer):
1258 """ Returns the drop down button position in the navigation area. """
1259
1260 return self.GetRightButtonPos(pageContainer)
1261
1262
1263 def GetXPos(self, pageContainer):
1264 """ Returns the 'X' button position in the navigation area. """
1265
1266 pc = pageContainer
1267 style = pc.GetParent().GetWindowStyleFlag()
1268 rect = pc.GetClientRect()
1269 clientWidth = rect.width
1270
1271 if style & FNB_NO_X_BUTTON:
1272 return clientWidth
1273 else:
1274 return clientWidth - 22
1275
1276
1277 def GetButtonsAreaLength(self, pageContainer):
1278 """ Returns the navigation area width. """
1279
1280 pc = pageContainer
1281 style = pc.GetParent().GetWindowStyleFlag()
1282
1283 # ''
1284 if style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
1285 return 0
1286
1287 # 'x'
1288 elif style & FNB_NO_NAV_BUTTONS and not style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
1289 return 22
1290
1291 # '<>'
1292 if not style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
1293 return 53 - 16
1294
1295 # 'vx'
1296 if style & FNB_DROPDOWN_TABS_LIST and not style & FNB_NO_X_BUTTON:
1297 return 22 + 16
1298
1299 # 'v'
1300 if style & FNB_DROPDOWN_TABS_LIST and style & FNB_NO_X_BUTTON:
1301 return 22
1302
1303 # '<>x'
1304 return 53
1305
1306
1307 def DrawLeftArrow(self, pageContainer, dc):
1308 """ Draw the left navigation arrow. """
1309
1310 pc = pageContainer
1311
1312 style = pc.GetParent().GetWindowStyleFlag()
1313 if style & FNB_NO_NAV_BUTTONS:
1314 return
1315
1316 # Make sure that there are pages in the container
1317 if not pc._pagesInfoVec:
1318 return
1319
1320 # Set the bitmap according to the button status
1321 if pc._nLeftButtonStatus == FNB_BTN_HOVER:
1322 arrowBmp = wx.BitmapFromXPMData(left_arrow_hilite_xpm)
1323 elif pc._nLeftButtonStatus == FNB_BTN_PRESSED:
1324 arrowBmp = wx.BitmapFromXPMData(left_arrow_pressed_xpm)
1325 else:
1326 arrowBmp = wx.BitmapFromXPMData(left_arrow_xpm)
1327
1328 if pc._nFrom == 0:
1329 # Handle disabled arrow
1330 arrowBmp = wx.BitmapFromXPMData(left_arrow_disabled_xpm)
1331
1332 arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR))
1333
1334 # Erase old bitmap
1335 posx = self.GetLeftButtonPos(pc)
1336 dc.DrawBitmap(self._leftBgBmp, posx, 6)
1337
1338 # Draw the new bitmap
1339 dc.DrawBitmap(arrowBmp, posx, 6, True)
1340
1341
1342 def DrawRightArrow(self, pageContainer, dc):
1343 """ Draw the right navigation arrow. """
1344
1345 pc = pageContainer
1346
1347 style = pc.GetParent().GetWindowStyleFlag()
1348 if style & FNB_NO_NAV_BUTTONS:
1349 return
1350
1351 # Make sure that there are pages in the container
1352 if not pc._pagesInfoVec:
1353 return
1354
1355 # Set the bitmap according to the button status
1356 if pc._nRightButtonStatus == FNB_BTN_HOVER:
1357 arrowBmp = wx.BitmapFromXPMData(right_arrow_hilite_xpm)
1358 elif pc._nRightButtonStatus == FNB_BTN_PRESSED:
1359 arrowBmp = wx.BitmapFromXPMData(right_arrow_pressed_xpm)
1360 else:
1361 arrowBmp = wx.BitmapFromXPMData(right_arrow_xpm)
1362
1363 # Check if the right most tab is visible, if it is
1364 # don't rotate right anymore
1365 if pc._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1):
1366 arrowBmp = wx.BitmapFromXPMData(right_arrow_disabled_xpm)
1367
1368 arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR))
1369
1370 # erase old bitmap
1371 posx = self.GetRightButtonPos(pc)
1372 dc.DrawBitmap(self._rightBgBmp, posx, 6)
1373
1374 # Draw the new bitmap
1375 dc.DrawBitmap(arrowBmp, posx, 6, True)
1376
1377
1378 def DrawDropDownArrow(self, pageContainer, dc):
1379 """ Draws the drop-down arrow in the navigation area. """
1380
1381 pc = pageContainer
1382
1383 # Check if this style is enabled
1384 style = pc.GetParent().GetWindowStyleFlag()
1385 if not style & FNB_DROPDOWN_TABS_LIST:
1386 return
1387
1388 # Make sure that there are pages in the container
1389 if not pc._pagesInfoVec:
1390 return
1391
1392 if pc._nArrowDownButtonStatus == FNB_BTN_HOVER:
1393 downBmp = wx.BitmapFromXPMData(down_arrow_hilite_xpm)
1394 elif pc._nArrowDownButtonStatus == FNB_BTN_PRESSED:
1395 downBmp = wx.BitmapFromXPMData(down_arrow_pressed_xpm)
1396 else:
1397 downBmp = wx.BitmapFromXPMData(down_arrow_xpm)
1398
1399 downBmp.SetMask(wx.Mask(downBmp, MASK_COLOR))
1400
1401 # erase old bitmap
1402 posx = self.GetDropArrowButtonPos(pc)
1403 dc.DrawBitmap(self._xBgBmp, posx, 6)
1404
1405 # Draw the new bitmap
1406 dc.DrawBitmap(downBmp, posx, 6, True)
1407
1408
1409 def DrawX(self, pageContainer, dc):
1410 """ Draw the 'X' navigation button in the navigation area. """
1411
1412 pc = pageContainer
1413
1414 # Check if this style is enabled
1415 style = pc.GetParent().GetWindowStyleFlag()
1416 if style & FNB_NO_X_BUTTON:
1417 return
1418
1419 # Make sure that there are pages in the container
1420 if not pc._pagesInfoVec:
1421 return
1422
1423 # Set the bitmap according to the button status
1424 if pc._nXButtonStatus == FNB_BTN_HOVER:
1425 xbmp = wx.BitmapFromXPMData(x_button_hilite_xpm)
1426 elif pc._nXButtonStatus == FNB_BTN_PRESSED:
1427 xbmp = wx.BitmapFromXPMData(x_button_pressed_xpm)
1428 else:
1429 xbmp = wx.BitmapFromXPMData(x_button_xpm)
1430
1431 xbmp.SetMask(wx.Mask(xbmp, MASK_COLOR))
1432
1433 # erase old bitmap
1434 posx = self.GetXPos(pc)
1435 dc.DrawBitmap(self._xBgBmp, posx, 6)
1436
1437 # Draw the new bitmap
1438 dc.DrawBitmap(xbmp, posx, 6, True)
1439
1440
1441 def DrawTabX(self, pageContainer, dc, rect, tabIdx, btnStatus):
1442 """ Draws the 'X' in the selected tab. """
1443
1444 pc = pageContainer
1445 if not pc.HasFlag(FNB_X_ON_TAB):
1446 return
1447
1448 # We draw the 'x' on the active tab only
1449 if tabIdx != pc.GetSelection() or tabIdx < 0:
1450 return
1451
1452 # Set the bitmap according to the button status
1453
1454 if btnStatus == FNB_BTN_HOVER:
1455 xBmp = wx.BitmapFromXPMData(x_button_hilite_xpm)
1456 elif btnStatus == FNB_BTN_PRESSED:
1457 xBmp = wx.BitmapFromXPMData(x_button_pressed_xpm)
1458 else:
1459 xBmp = wx.BitmapFromXPMData(x_button_xpm)
1460
1461 # Set the masking
1462 xBmp.SetMask(wx.Mask(xBmp, MASK_COLOR))
1463
1464 # erase old button
1465 dc.DrawBitmap(self._tabXBgBmp, rect.x, rect.y)
1466
1467 # Draw the new bitmap
1468 dc.DrawBitmap(xBmp, rect.x, rect.y, True)
1469
1470 # Update the vector
1471 rr = wx.Rect(rect.x, rect.y, 14, 13)
1472 pc._pagesInfoVec[tabIdx].SetXRect(rr)
1473
1474
1475 def _GetBitmap(self, dc, rect, bmp):
1476
1477 mem_dc = wx.MemoryDC()
1478 mem_dc.SelectObject(bmp)
1479 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
1480 mem_dc.SelectObject(wx.NullBitmap)
1481 return bmp
1482
1483
1484 def DrawTabsLine(self, pageContainer, dc):
1485 """ Draws a line over the tabs. """
1486
1487 pc = pageContainer
1488
1489 clntRect = pc.GetClientRect()
1490 clientRect3 = wx.Rect(0, 0, clntRect.width, clntRect.height)
1491
1492 if pc.HasFlag(FNB_BOTTOM):
1493
1494 clientRect = wx.Rect(0, 2, clntRect.width, clntRect.height - 2)
1495 clientRect2 = wx.Rect(0, 1, clntRect.width, clntRect.height - 1)
1496
1497 else:
1498
1499 clientRect = wx.Rect(0, 0, clntRect.width, clntRect.height - 2)
1500 clientRect2 = wx.Rect(0, 0, clntRect.width, clntRect.height - 1)
1501
1502 dc.SetBrush(wx.TRANSPARENT_BRUSH)
1503 dc.SetPen(wx.Pen(pc.GetSingleLineBorderColour()))
1504 dc.DrawRectangleRect(clientRect2)
1505 dc.DrawRectangleRect(clientRect3)
1506
1507 dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
1508 dc.DrawRectangleRect(clientRect)
1509
1510 if not pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
1511
1512 dc.SetPen(wx.Pen((pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc._tabAreaColor])[0]))
1513 dc.DrawLine(0, 0, 0, clientRect.height+1)
1514
1515 if pc.HasFlag(FNB_BOTTOM):
1516
1517 dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1)
1518
1519 else:
1520
1521 dc.DrawLine(0, 0, clientRect.width, 0)
1522
1523 dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1)
1524
1525
1526 def CalcTabWidth(self, pageContainer, tabIdx, tabHeight):
1527 """ Calculates the width of the input tab. """
1528
1529 pc = pageContainer
1530 dc = wx.MemoryDC()
1531 dc.SelectObject(wx.EmptyBitmap(1,1))
1532
1533 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1534 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
1535
1536 if pc.IsDefaultTabs():
1537 shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
1538
1539 # Calculate the text length using the bold font, so when selecting a tab
1540 # its width will not change
1541 dc.SetFont(boldFont)
1542 width, pom = dc.GetTextExtent(pc.GetPageText(tabIdx))
1543
1544 # Set a minimum size to a tab
1545 if width < 20:
1546 width = 20
1547
1548 tabWidth = 2*pc._pParent.GetPadding() + width
1549
1550 # Style to add a small 'x' button on the top right
1551 # of the tab
1552 if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
1553 # The xpm image that contains the 'x' button is 9 pixels
1554 spacer = 9
1555 if pc.HasFlag(FNB_VC8):
1556 spacer = 4
1557
1558 tabWidth += pc._pParent.GetPadding() + spacer
1559
1560 if pc.IsDefaultTabs():
1561 # Default style
1562 tabWidth += 2*shapePoints
1563
1564 hasImage = pc._ImageList != None and pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
1565
1566 # For VC71 style, we only add the icon size (16 pixels)
1567 if hasImage:
1568
1569 if not pc.IsDefaultTabs():
1570 tabWidth += 16 + pc._pParent.GetPadding()
1571 else:
1572 # Default style
1573 tabWidth += 16 + pc._pParent.GetPadding() + shapePoints/2
1574
1575 return tabWidth
1576
1577
1578 def CalcTabHeight(self, pageContainer):
1579 """ Calculates the height of the input tab. """
1580
1581 if self._tabHeight:
1582 return self._tabHeight
1583
1584 pc = pageContainer
1585 dc = wx.MemoryDC()
1586 dc.SelectObject(wx.EmptyBitmap(1,1))
1587
1588 # For GTK it seems that we must do this steps in order
1589 # for the tabs will get the proper height on initialization
1590 # on MSW, preforming these steps yields wierd results
1591 normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1592 boldFont = normalFont
1593
1594 if "__WXGTK__" in wx.PlatformInfo:
1595 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
1596 dc.SetFont(boldFont)
1597
1598 height = dc.GetCharHeight()
1599
1600 tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
1601 if "__WXGTK__" in wx.PlatformInfo:
1602 # On GTK the tabs are should be larger
1603 tabHeight += 6
1604
1605 self._tabHeight = tabHeight
1606
1607 return tabHeight
1608
1609
1610 def DrawTabs(self, pageContainer, dc):
1611 """ Actually draws the tabs in L{FlatNotebook}."""
1612
1613 pc = pageContainer
1614 if "__WXMAC__" in wx.PlatformInfo:
1615 # Works well on MSW & GTK, however this lines should be skipped on MAC
1616 if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
1617 pc.Hide()
1618 return
1619
1620 # Get the text hight
1621 tabHeight = self.CalcTabHeight(pageContainer)
1622 style = pc.GetParent().GetWindowStyleFlag()
1623
1624 # Calculate the number of rows required for drawing the tabs
1625 rect = pc.GetClientRect()
1626 clientWidth = rect.width
1627
1628 # Set the maximum client size
1629 pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
1630 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
1631
1632 if style & FNB_VC71:
1633 backBrush = wx.Brush(wx.Colour(247, 243, 233))
1634 else:
1635 backBrush = wx.Brush(pc._tabAreaColor)
1636
1637 noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE))
1638 selBrush = wx.Brush(pc._activeTabColor)
1639
1640 size = pc.GetSize()
1641
1642 # Background
1643 dc.SetTextBackground((style & FNB_VC71 and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0])
1644 dc.SetTextForeground(pc._activeTextColor)
1645 dc.SetBrush(backBrush)
1646
1647 # If border style is set, set the pen to be border pen
1648 if pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
1649 dc.SetPen(borderPen)
1650 else:
1651 colr = (pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0]
1652 dc.SetPen(wx.Pen(colr))
1653
1654 dc.DrawRectangle(0, 0, size.x, size.y)
1655
1656 # Take 3 bitmaps for the background for the buttons
1657
1658 mem_dc = wx.MemoryDC()
1659 #---------------------------------------
1660 # X button
1661 #---------------------------------------
1662 rect = wx.Rect(self.GetXPos(pc), 6, 16, 14)
1663 mem_dc.SelectObject(self._xBgBmp)
1664 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
1665 mem_dc.SelectObject(wx.NullBitmap)
1666
1667 #---------------------------------------
1668 # Right button
1669 #---------------------------------------
1670 rect = wx.Rect(self.GetRightButtonPos(pc), 6, 16, 14)
1671 mem_dc.SelectObject(self._rightBgBmp)
1672 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
1673 mem_dc.SelectObject(wx.NullBitmap)
1674
1675 #---------------------------------------
1676 # Left button
1677 #---------------------------------------
1678 rect = wx.Rect(self.GetLeftButtonPos(pc), 6, 16, 14)
1679 mem_dc.SelectObject(self._leftBgBmp)
1680 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
1681 mem_dc.SelectObject(wx.NullBitmap)
1682
1683 # We always draw the bottom/upper line of the tabs
1684 # regradless the style
1685 dc.SetPen(borderPen)
1686 self.DrawTabsLine(pc, dc)
1687
1688 # Restore the pen
1689 dc.SetPen(borderPen)
1690
1691 if pc.HasFlag(FNB_VC71):
1692
1693 greyLineYVal = (pc.HasFlag(FNB_BOTTOM) and [0] or [size.y - 2])[0]
1694 whiteLineYVal = (pc.HasFlag(FNB_BOTTOM) and [3] or [size.y - 3])[0]
1695
1696 pen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
1697 dc.SetPen(pen)
1698
1699 # Draw thik grey line between the windows area and
1700 # the tab area
1701 for num in xrange(3):
1702 dc.DrawLine(0, greyLineYVal + num, size.x, greyLineYVal + num)
1703
1704 wbPen = (pc.HasFlag(FNB_BOTTOM) and [wx.BLACK_PEN] or [wx.WHITE_PEN])[0]
1705 dc.SetPen(wbPen)
1706 dc.DrawLine(1, whiteLineYVal, size.x - 1, whiteLineYVal)
1707
1708 # Restore the pen
1709 dc.SetPen(borderPen)
1710
1711 # Draw labels
1712 normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1713 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1714 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
1715 dc.SetFont(boldFont)
1716
1717 posx = pc._pParent.GetPadding()
1718
1719 # Update all the tabs from 0 to 'pc._nFrom' to be non visible
1720 for i in xrange(pc._nFrom):
1721
1722 pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
1723 pc._pagesInfoVec[i].GetRegion().Clear()
1724
1725 count = pc._nFrom
1726
1727 #----------------------------------------------------------
1728 # Go over and draw the visible tabs
1729 #----------------------------------------------------------
1730 for i in xrange(pc._nFrom, len(pc._pagesInfoVec)):
1731
1732 dc.SetPen(borderPen)
1733 dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
1734
1735 # Now set the font to the correct font
1736 dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
1737
1738 # Add the padding to the tab width
1739 # Tab width:
1740 # +-----------------------------------------------------------+
1741 # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
1742 # +-----------------------------------------------------------+
1743 tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
1744
1745 # Check if we can draw more
1746 if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
1747 break
1748
1749 count = count + 1
1750
1751 # By default we clean the tab region
1752 pc._pagesInfoVec[i].GetRegion().Clear()
1753
1754 # Clean the 'x' buttn on the tab.
1755 # A 'Clean' rectangle, is a rectangle with width or height
1756 # with values lower than or equal to 0
1757 pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
1758
1759 # Draw the tab (border, text, image & 'x' on tab)
1760 self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
1761
1762 # Restore the text forground
1763 dc.SetTextForeground(pc._activeTextColor)
1764
1765 # Update the tab position & size
1766 posy = (pc.HasFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
1767
1768 pc._pagesInfoVec[i].SetPosition(wx.Point(posx, posy))
1769 pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
1770 posx += tabWidth
1771
1772 # Update all tabs that can not fit into the screen as non-visible
1773 for i in xrange(count, len(pc._pagesInfoVec)):
1774 pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
1775 pc._pagesInfoVec[i].GetRegion().Clear()
1776
1777 # Draw the left/right/close buttons
1778 # Left arrow
1779 self.DrawLeftArrow(pc, dc)
1780 self.DrawRightArrow(pc, dc)
1781 self.DrawX(pc, dc)
1782 self.DrawDropDownArrow(pc, dc)
1783
1784
1785 def DrawDragHint(self, pc, tabIdx):
1786 """
1787 Draws tab drag hint, the default implementation is to do nothing.
1788 You can override this function to provide a nice feedback to user.
1789 """
1790
1791 pass
1792
1793
1794 def NumberTabsCanFit(self, pageContainer, fr=-1):
1795
1796 pc = pageContainer
1797
1798 rect = pc.GetClientRect()
1799 clientWidth = rect.width
1800
1801 vTabInfo = []
1802
1803 tabHeight = self.CalcTabHeight(pageContainer)
1804
1805 # The drawing starts from posx
1806 posx = pc._pParent.GetPadding()
1807
1808 if fr < 0:
1809 fr = pc._nFrom
1810
1811 for i in xrange(fr, len(pc._pagesInfoVec)):
1812
1813 tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
1814 if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
1815 break;
1816
1817 # Add a result to the returned vector
1818 tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth , tabHeight)
1819 vTabInfo.append(tabRect)
1820
1821 # Advance posx
1822 posx += tabWidth + FNB_HEIGHT_SPACER
1823
1824 return vTabInfo
1825
1826
1827 # ---------------------------------------------------------------------------- #
1828 # Class FNBRendererMgr
1829 # A manager that handles all the renderers defined below and calls the
1830 # appropriate one when drawing is needed
1831 # ---------------------------------------------------------------------------- #
1832
1833 class FNBRendererMgr:
1834 """
1835 This class represents a manager that handles all the 4 renderers defined
1836 and calls the appropriate one when drawing is needed.
1837 """
1838
1839 def __init__(self):
1840 """ Default class constructor. """
1841
1842 # register renderers
1843
1844 self._renderers = {}
1845 self._renderers.update({-1: FNBRendererDefault()})
1846 self._renderers.update({FNB_VC71: FNBRendererVC71()})
1847 self._renderers.update({FNB_FANCY_TABS: FNBRendererFancy()})
1848 self._renderers.update({FNB_VC8: FNBRendererVC8()})
1849
1850
1851 def GetRenderer(self, style):
1852 """ Returns the current renderer based on the style selected. """
1853
1854 # since we dont have a style for default tabs, we
1855 # test for all others - FIXME: add style for default tabs
1856 if not style & FNB_VC71 and not style & FNB_VC8 and not style & FNB_FANCY_TABS:
1857 return self._renderers[-1]
1858
1859 if style & FNB_VC71:
1860 return self._renderers[FNB_VC71]
1861
1862 if style & FNB_FANCY_TABS:
1863 return self._renderers[FNB_FANCY_TABS]
1864
1865 if style & FNB_VC8:
1866 return self._renderers[FNB_VC8]
1867
1868 # the default is to return the default renderer
1869 return self._renderers[-1]
1870
1871
1872 #------------------------------------------
1873 # Default renderer
1874 #------------------------------------------
1875
1876 class FNBRendererDefault(FNBRenderer):
1877 """
1878 This class handles the drawing of tabs using the I{Standard} renderer.
1879 """
1880
1881 def __init__(self):
1882 """ Default class constructor. """
1883
1884 FNBRenderer.__init__(self)
1885
1886
1887 def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
1888 """ Draws a tab using the I{Standard} style. """
1889
1890 # Default style
1891 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
1892 pc = pageContainer
1893
1894 tabPoints = [wx.Point() for ii in xrange(7)]
1895 tabPoints[0].x = posx
1896 tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
1897
1898 tabPoints[1].x = int(posx+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
1899 tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
1900
1901 tabPoints[2].x = tabPoints[1].x+2
1902 tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
1903
1904 tabPoints[3].x = int(posx+tabWidth-(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))-2
1905 tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
1906
1907 tabPoints[4].x = tabPoints[3].x+2
1908 tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
1909
1910 tabPoints[5].x = int(tabPoints[4].x+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
1911 tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
1912
1913 tabPoints[6].x = tabPoints[0].x
1914 tabPoints[6].y = tabPoints[0].y
1915
1916 if tabIdx == pc.GetSelection():
1917
1918 # Draw the tab as rounded rectangle
1919 dc.DrawPolygon(tabPoints)
1920
1921 else:
1922
1923 if tabIdx != pc.GetSelection() - 1:
1924
1925 # Draw a vertical line to the right of the text
1926 pt1x = tabPoints[5].x
1927 pt1y = (pc.HasFlag(FNB_BOTTOM) and [4] or [tabHeight - 6])[0]
1928 pt2x = tabPoints[5].x
1929 pt2y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 4] or [4])[0]
1930 dc.DrawLine(pt1x, pt1y, pt2x, pt2y)
1931
1932 if tabIdx == pc.GetSelection():
1933
1934 savePen = dc.GetPen()
1935 whitePen = wx.Pen(wx.WHITE)
1936 whitePen.SetWidth(1)
1937 dc.SetPen(whitePen)
1938
1939 secPt = wx.Point(tabPoints[5].x + 1, tabPoints[5].y)
1940 dc.DrawLine(tabPoints[0].x, tabPoints[0].y, secPt.x, secPt.y)
1941
1942 # Restore the pen
1943 dc.SetPen(savePen)
1944
1945 # -----------------------------------
1946 # Text and image drawing
1947 # -----------------------------------
1948
1949 # Text drawing offset from the left border of the
1950 # rectangle
1951
1952 # The width of the images are 16 pixels
1953 padding = pc.GetParent().GetPadding()
1954 shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
1955 hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
1956 imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
1957
1958 if hasImage:
1959 textOffset = 2*pc._pParent._nPadding + 16 + shapePoints/2
1960 else:
1961 textOffset = pc._pParent._nPadding + shapePoints/2
1962
1963 textOffset += 2
1964
1965 if tabIdx != pc.GetSelection():
1966
1967 # Set the text background to be like the vertical lines
1968 dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
1969
1970 if hasImage:
1971
1972 imageXOffset = textOffset - 16 - padding
1973 pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
1974 posx + imageXOffset, imageYCoord,
1975 wx.IMAGELIST_DRAW_TRANSPARENT, True)
1976
1977 dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
1978
1979 # draw 'x' on tab (if enabled)
1980 if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
1981
1982 textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
1983 tabCloseButtonXCoord = posx + textOffset + textWidth + 1
1984
1985 # take a bitmap from the position of the 'x' button (the x on tab button)
1986 # this bitmap will be used later to delete old buttons
1987 tabCloseButtonYCoord = imageYCoord
1988 x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
1989 self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
1990
1991 # Draw the tab
1992 self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
1993
1994
1995 #------------------------------------------------------------------
1996 # Visual studio 7.1
1997 #------------------------------------------------------------------
1998
1999 class FNBRendererVC71(FNBRenderer):
2000 """
2001 This class handles the drawing of tabs using the I{VC71} renderer.
2002 """
2003
2004 def __init__(self):
2005 """ Default class constructor. """
2006
2007 FNBRenderer.__init__(self)
2008
2009
2010 def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
2011 """ Draws a tab using the I{VC71} style. """
2012
2013 # Visual studio 7.1 style
2014 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
2015 pc = pageContainer
2016
2017 dc.SetPen((tabIdx == pc.GetSelection() and [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or [borderPen])[0])
2018 dc.SetBrush((tabIdx == pc.GetSelection() and [wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or [wx.Brush(wx.Colour(247, 243, 233))])[0])
2019
2020 if tabIdx == pc.GetSelection():
2021
2022 posy = (pc.HasFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
2023 tabH = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 5] or [tabHeight - 3])[0]
2024 dc.DrawRectangle(posx, posy, tabWidth, tabH)
2025
2026 # Draw a black line on the left side of the
2027 # rectangle
2028 dc.SetPen(wx.BLACK_PEN)
2029
2030 blackLineY1 = VERTICAL_BORDER_PADDING
2031 blackLineY2 = tabH
2032 dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
2033
2034 # To give the tab more 3D look we do the following
2035 # Incase the tab is on top,
2036 # Draw a thik white line on topof the rectangle
2037 # Otherwise, draw a thin (1 pixel) black line at the bottom
2038
2039 pen = wx.Pen((pc.HasFlag(FNB_BOTTOM) and [wx.BLACK] or [wx.WHITE])[0])
2040 dc.SetPen(pen)
2041 whiteLinePosY = (pc.HasFlag(FNB_BOTTOM) and [blackLineY2] or [VERTICAL_BORDER_PADDING ])[0]
2042 dc.DrawLine(posx , whiteLinePosY, posx + tabWidth + 1, whiteLinePosY)
2043
2044 # Draw a white vertical line to the left of the tab
2045 dc.SetPen(wx.WHITE_PEN)
2046 if not pc.HasFlag(FNB_BOTTOM):
2047 blackLineY2 += 1
2048
2049 dc.DrawLine(posx, blackLineY1, posx, blackLineY2)
2050
2051 else:
2052
2053 # We dont draw a rectangle for non selected tabs, but only
2054 # vertical line on the left
2055
2056 blackLineY1 = (pc.HasFlag(FNB_BOTTOM) and [VERTICAL_BORDER_PADDING + 2] or [VERTICAL_BORDER_PADDING + 1])[0]
2057 blackLineY2 = pc.GetSize().y - 5
2058 dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
2059
2060 # -----------------------------------
2061 # Text and image drawing
2062 # -----------------------------------
2063
2064 # Text drawing offset from the left border of the
2065 # rectangle
2066
2067 # The width of the images are 16 pixels
2068 padding = pc.GetParent().GetPadding()
2069 hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
2070 imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [5] or [8])[0]
2071
2072 if hasImage:
2073 textOffset = 2*pc._pParent._nPadding + 16
2074 else:
2075 textOffset = pc._pParent._nPadding
2076
2077 if tabIdx != pc.GetSelection():
2078
2079 # Set the text background to be like the vertical lines
2080 dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
2081
2082 if hasImage:
2083
2084 imageXOffset = textOffset - 16 - padding
2085 pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
2086 posx + imageXOffset, imageYCoord,
2087 wx.IMAGELIST_DRAW_TRANSPARENT, True)
2088
2089 dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
2090
2091 # draw 'x' on tab (if enabled)
2092 if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
2093
2094 textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
2095 tabCloseButtonXCoord = posx + textOffset + textWidth + 1
2096
2097 # take a bitmap from the position of the 'x' button (the x on tab button)
2098 # this bitmap will be used later to delete old buttons
2099 tabCloseButtonYCoord = imageYCoord
2100 x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
2101 self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
2102
2103 # Draw the tab
2104 self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
2105
2106
2107 #------------------------------------------------------------------
2108 # Fancy style
2109 #------------------------------------------------------------------
2110
2111 class FNBRendererFancy(FNBRenderer):
2112 """
2113 This class handles the drawing of tabs using the I{Fancy} renderer.
2114 """
2115
2116 def __init__(self):
2117 """ Default class constructor. """
2118
2119 FNBRenderer.__init__(self)
2120
2121
2122 def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
2123 """ Draws a tab using the I{Fancy} style, similar to VC71 but with gradients. """
2124
2125 # Fancy tabs - like with VC71 but with the following differences:
2126 # - The Selected tab is colored with gradient color
2127 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
2128 pc = pageContainer
2129
2130 pen = (tabIdx == pc.GetSelection() and [wx.Pen(pc._pParent.GetBorderColour())] or [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))])[0]
2131
2132 if tabIdx == pc.GetSelection():
2133
2134 posy = (pc.HasFlag(FNB_BOTTOM) and [2] or [VERTICAL_BORDER_PADDING])[0]
2135 th = tabHeight - 5
2136
2137 rect = wx.Rect(posx, posy, tabWidth, th)
2138
2139 col2 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourTo()] or [pc._pParent.GetGradientColourFrom()])[0]
2140 col1 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourFrom()] or [pc._pParent.GetGradientColourTo()])[0]
2141
2142 PaintStraightGradientBox(dc, rect, col1, col2)
2143 dc.SetBrush(wx.TRANSPARENT_BRUSH)
2144 dc.SetPen(pen)
2145 dc.DrawRectangleRect(rect)
2146
2147 # erase the bottom/top line of the rectangle
2148 dc.SetPen(wx.Pen(pc._pParent.GetGradientColourFrom()))
2149 if pc.HasFlag(FNB_BOTTOM):
2150 dc.DrawLine(rect.x, 2, rect.x + rect.width, 2)
2151 else:
2152 dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
2153
2154 else:
2155
2156 # We dont draw a rectangle for non selected tabs, but only
2157 # vertical line on the left
2158 dc.SetPen(borderPen)
2159 dc.DrawLine(posx + tabWidth, VERTICAL_BORDER_PADDING + 3, posx + tabWidth, tabHeight - 4)
2160
2161
2162 # -----------------------------------
2163 # Text and image drawing
2164 # -----------------------------------
2165
2166 # Text drawing offset from the left border of the
2167 # rectangle
2168
2169 # The width of the images are 16 pixels
2170 padding = pc.GetParent().GetPadding()
2171 hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
2172 imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
2173
2174 if hasImage:
2175 textOffset = 2*pc._pParent._nPadding + 16
2176 else:
2177 textOffset = pc._pParent._nPadding
2178
2179 textOffset += 2
2180
2181 if tabIdx != pc.GetSelection():
2182
2183 # Set the text background to be like the vertical lines
2184 dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
2185
2186 if hasImage:
2187
2188 imageXOffset = textOffset - 16 - padding
2189 pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
2190 posx + imageXOffset, imageYCoord,
2191 wx.IMAGELIST_DRAW_TRANSPARENT, True)
2192
2193 dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
2194
2195 # draw 'x' on tab (if enabled)
2196 if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
2197
2198 textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
2199 tabCloseButtonXCoord = posx + textOffset + textWidth + 1
2200
2201 # take a bitmap from the position of the 'x' button (the x on tab button)
2202 # this bitmap will be used later to delete old buttons
2203 tabCloseButtonYCoord = imageYCoord
2204 x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
2205 self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
2206
2207 # Draw the tab
2208 self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
2209
2210
2211 #------------------------------------------------------------------
2212 # Visual studio 2005 (VS8)
2213 #------------------------------------------------------------------
2214 class FNBRendererVC8(FNBRenderer):
2215 """
2216 This class handles the drawing of tabs using the I{VC8} renderer.
2217 """
2218
2219 def __init__(self):
2220 """ Default class constructor. """
2221
2222 FNBRenderer.__init__(self)
2223 self._first = True
2224 self._factor = 1
2225
2226
2227 def DrawTabs(self, pageContainer, dc):
2228 """ Draws all the tabs using VC8 style. Overloads The DrawTabs method in parent class. """
2229
2230 pc = pageContainer
2231
2232 if "__WXMAC__" in wx.PlatformInfo:
2233 # Works well on MSW & GTK, however this lines should be skipped on MAC
2234 if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
2235 pc.Hide()
2236 return
2237
2238 # Get the text hight
2239 tabHeight = self.CalcTabHeight(pageContainer)
2240
2241 # Set the font for measuring the tab height
2242 normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
2243 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
2244 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
2245
2246 # Calculate the number of rows required for drawing the tabs
2247 rect = pc.GetClientRect()
2248
2249 # Set the maximum client size
2250 pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
2251 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
2252
2253 # Create brushes
2254 backBrush = wx.Brush(pc._tabAreaColor)
2255 noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE))
2256 selBrush = wx.Brush(pc._activeTabColor)
2257 size = pc.GetSize()
2258
2259 # Background
2260 dc.SetTextBackground(pc.GetBackgroundColour())
2261 dc.SetTextForeground(pc._activeTextColor)
2262
2263 # If border style is set, set the pen to be border pen
2264 if pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
2265 dc.SetPen(borderPen)
2266 else:
2267 dc.SetPen(wx.TRANSPARENT_PEN)
2268
2269 lightFactor = (pc.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
2270
2271 # For VC8 style, we color the tab area in gradient coloring
2272 lightcolour = LightColour(pc._tabAreaColor, lightFactor)
2273 PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColor, lightcolour)
2274
2275 dc.SetBrush(wx.TRANSPARENT_BRUSH)
2276 dc.DrawRectangle(0, 0, size.x, size.y)
2277
2278 # Take 3 bitmaps for the background for the buttons
2279
2280 mem_dc = wx.MemoryDC()
2281 #---------------------------------------
2282 # X button
2283 #---------------------------------------
2284 rect = wx.Rect(self.GetXPos(pc), 6, 16, 14)
2285 mem_dc.SelectObject(self._xBgBmp)
2286 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
2287 mem_dc.SelectObject(wx.NullBitmap)
2288
2289 #---------------------------------------
2290 # Right button
2291 #---------------------------------------
2292 rect = wx.Rect(self.GetRightButtonPos(pc), 6, 16, 14)
2293 mem_dc.SelectObject(self._rightBgBmp)
2294 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
2295 mem_dc.SelectObject(wx.NullBitmap)
2296
2297 #---------------------------------------
2298 # Left button
2299 #---------------------------------------
2300 rect = wx.Rect(self.GetLeftButtonPos(pc), 6, 16, 14)
2301 mem_dc.SelectObject(self._leftBgBmp)
2302 mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
2303 mem_dc.SelectObject(wx.NullBitmap)
2304
2305 # We always draw the bottom/upper line of the tabs
2306 # regradless the style
2307 dc.SetPen(borderPen)
2308 self.DrawTabsLine(pc, dc)
2309
2310 # Restore the pen
2311 dc.SetPen(borderPen)
2312
2313 # Draw labels
2314 dc.SetFont(boldFont)
2315
2316 # Update all the tabs from 0 to 'pc.self._nFrom' to be non visible
2317 for i in xrange(pc._nFrom):
2318
2319 pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
2320 pc._pagesInfoVec[i].GetRegion().Clear()
2321
2322 # Draw the visible tabs, in VC8 style, we draw them from right to left
2323 vTabsInfo = self.NumberTabsCanFit(pc)
2324
2325 activeTabPosx = 0
2326 activeTabWidth = 0
2327 activeTabHeight = 0
2328
2329 for cur in xrange(len(vTabsInfo)-1, -1, -1):
2330
2331 # 'i' points to the index of the currently drawn tab
2332 # in pc.GetPageInfoVector() vector
2333 i = pc._nFrom + cur
2334 dc.SetPen(borderPen)
2335 dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
2336
2337 # Now set the font to the correct font
2338 dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
2339
2340 # Add the padding to the tab width
2341 # Tab width:
2342 # +-----------------------------------------------------------+
2343 # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
2344 # +-----------------------------------------------------------+
2345
2346 tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
2347 posx = vTabsInfo[cur].x
2348
2349 # By default we clean the tab region
2350 # incase we use the VC8 style which requires
2351 # the region, it will be filled by the function
2352 # drawVc8Tab
2353 pc._pagesInfoVec[i].GetRegion().Clear()
2354
2355 # Clean the 'x' buttn on the tab
2356 # 'Clean' rectanlge is a rectangle with width or height
2357 # with values lower than or equal to 0
2358 pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
2359
2360 # Draw the tab
2361 # Incase we are drawing the active tab
2362 # we need to redraw so it will appear on top
2363 # of all other tabs
2364
2365 # when using the vc8 style, we keep the position of the active tab so we will draw it again later
2366 if i == pc.GetSelection() and pc.HasFlag(FNB_VC8):
2367
2368 activeTabPosx = posx
2369 activeTabWidth = tabWidth
2370 activeTabHeight = tabHeight
2371
2372 else:
2373
2374 self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
2375
2376 # Restore the text forground
2377 dc.SetTextForeground(pc._activeTextColor)
2378
2379 # Update the tab position & size
2380 pc._pagesInfoVec[i].SetPosition(wx.Point(posx, VERTICAL_BORDER_PADDING))
2381 pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
2382
2383 # Incase we are in VC8 style, redraw the active tab (incase it is visible)
2384 if pc.GetSelection() >= pc._nFrom and pc.GetSelection() < pc._nFrom + len(vTabsInfo):
2385
2386 self.DrawTab(pc, dc, activeTabPosx, pc.GetSelection(), activeTabWidth, activeTabHeight, pc._nTabXButtonStatus)
2387
2388 # Update all tabs that can not fit into the screen as non-visible
2389 for xx in xrange(pc._nFrom + len(vTabsInfo), len(pc._pagesInfoVec)):
2390
2391 pc._pagesInfoVec[xx].SetPosition(wx.Point(-1, -1))
2392 pc._pagesInfoVec[xx].GetRegion().Clear()
2393
2394 # Draw the left/right/close buttons
2395 # Left arrow
2396 self.DrawLeftArrow(pc, dc)
2397 self.DrawRightArrow(pc, dc)
2398 self.DrawX(pc, dc)
2399 self.DrawDropDownArrow(pc, dc)
2400
2401
2402 def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
2403 """ Draws a tab using VC8 style. """
2404
2405 pc = pageContainer
2406 borderPen = wx.Pen(pc._pParent.GetBorderColour())
2407 tabPoints = [wx.Point() for ii in xrange(8)]
2408
2409 # If we draw the first tab or the active tab,
2410 # we draw a full tab, else we draw a truncated tab
2411 #
2412 # X(2) X(3)
2413 # X(1) X(4)
2414 #
2415 # X(5)
2416 #
2417 # X(0),(7) X(6)
2418 #
2419 #
2420
2421 tabPoints[0].x = (pc.HasFlag(FNB_BOTTOM) and [posx] or [posx+self._factor])[0]
2422 tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 3])[0]
2423
2424 tabPoints[1].x = tabPoints[0].x + tabHeight - VERTICAL_BORDER_PADDING - 3 - self._factor
2425 tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
2426
2427 tabPoints[2].x = tabPoints[1].x + 4
2428 tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
2429
2430 tabPoints[3].x = tabPoints[2].x + tabWidth - 2
2431 tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
2432
2433 tabPoints[4].x = tabPoints[3].x + 1
2434 tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
2435
2436 tabPoints[5].x = tabPoints[4].x + 1
2437 tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
2438
2439 tabPoints[6].x = tabPoints[2].x + tabWidth
2440 tabPoints[6].y = tabPoints[0].y
2441
2442 tabPoints[7].x = tabPoints[0].x
2443 tabPoints[7].y = tabPoints[0].y
2444
2445 pc._pagesInfoVec[tabIdx].SetRegion(tabPoints)
2446
2447 # Draw the polygon
2448 br = dc.GetBrush()
2449 dc.SetBrush(wx.Brush((tabIdx == pc.GetSelection() and [pc._activeTabColor] or [pc._colorTo])[0]))
2450 dc.SetPen(wx.Pen((tabIdx == pc.GetSelection() and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colorBorder])[0]))
2451 dc.DrawPolygon(tabPoints)
2452
2453 # Restore the brush
2454 dc.SetBrush(br)
2455 rect = pc.GetClientRect()
2456
2457 if tabIdx != pc.GetSelection() and not pc.HasFlag(FNB_BOTTOM):
2458
2459 # Top default tabs
2460 dc.SetPen(wx.Pen(pc._pParent.GetBorderColour()))
2461 lineY = rect.height
2462 curPen = dc.GetPen()
2463 curPen.SetWidth(1)
2464 dc.SetPen(curPen)
2465 dc.DrawLine(posx, lineY, posx+rect.width, lineY)
2466
2467 # Incase we are drawing the selected tab, we draw the border of it as well
2468 # but without the bottom (upper line incase of wxBOTTOM)
2469 if tabIdx == pc.GetSelection():
2470
2471 borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
2472 dc.SetPen(borderPen)
2473 dc.SetBrush(wx.TRANSPARENT_BRUSH)
2474 dc.DrawPolygon(tabPoints)
2475
2476 # Delete the bottom line (or the upper one, incase we use wxBOTTOM)
2477 dc.SetPen(wx.WHITE_PEN)
2478 dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y)
2479
2480 self.FillVC8GradientColour(pc, dc, tabPoints, tabIdx == pc.GetSelection(), tabIdx)
2481
2482 # Draw a thin line to the right of the non-selected tab
2483 if tabIdx != pc.GetSelection():
2484
2485 dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
2486 dc.DrawLine(tabPoints[4].x-1, tabPoints[4].y, tabPoints[5].x-1, tabPoints[5].y)
2487 dc.DrawLine(tabPoints[5].x-1, tabPoints[5].y, tabPoints[6].x-1, tabPoints[6].y)
2488
2489 # Text drawing offset from the left border of the
2490 # rectangle
2491
2492 # The width of the images are 16 pixels
2493 vc8ShapeLen = tabHeight - VERTICAL_BORDER_PADDING - 2
2494 if pc.TabHasImage(tabIdx):
2495 textOffset = 2*pc._pParent.GetPadding() + 16 + vc8ShapeLen
2496 else:
2497 textOffset = pc._pParent.GetPadding() + vc8ShapeLen
2498
2499 # Draw the image for the tab if any
2500 imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
2501
2502 if pc.TabHasImage(tabIdx):
2503
2504 imageXOffset = textOffset - 16 - pc._pParent.GetPadding()
2505 pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
2506 posx + imageXOffset, imageYCoord,
2507 wx.IMAGELIST_DRAW_TRANSPARENT, True)
2508
2509 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
2510
2511 # if selected tab, draw text in bold
2512 if tabIdx == pc.GetSelection():
2513 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
2514
2515 dc.SetFont(boldFont)
2516 dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
2517
2518 # draw 'x' on tab (if enabled)
2519 if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
2520
2521 textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
2522 tabCloseButtonXCoord = posx + textOffset + textWidth + 1
2523
2524 # take a bitmap from the position of the 'x' button (the x on tab button)
2525 # this bitmap will be used later to delete old buttons
2526 tabCloseButtonYCoord = imageYCoord
2527 x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
2528 self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
2529 # Draw the tab
2530 self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
2531
2532
2533 def FillVC8GradientColour(self, pageContainer, dc, tabPoints, bSelectedTab, tabIdx):
2534 """ Fills a tab with a gradient shading. """
2535
2536 # calculate gradient coefficients
2537 pc = pageContainer
2538
2539 if self._first:
2540 self._first = False
2541 pc._colorTo = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 0)
2542 pc._colorFrom = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 60)
2543
2544 col2 = pc._pParent.GetGradientColourTo()
2545 col1 = pc._pParent.GetGradientColourFrom()
2546
2547 # If colorful tabs style is set, override the tab color
2548 if pc.HasFlag(FNB_COLORFUL_TABS):
2549
2550 if not pc._pagesInfoVec[tabIdx].GetColour():
2551
2552 # First time, generate color, and keep it in the vector
2553 tabColor = RandomColour()
2554 pc._pagesInfoVec[tabIdx].SetColour(tabColor)
2555
2556 if pc.HasFlag(FNB_BOTTOM):
2557
2558 col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
2559 col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
2560
2561 else:
2562
2563 col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
2564 col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
2565
2566 size = abs(tabPoints[2].y - tabPoints[0].y) - 1
2567
2568 rf, gf, bf = 0, 0, 0
2569 rstep = float(col2.Red() - col1.Red())/float(size)
2570 gstep = float(col2.Green() - col1.Green())/float(size)
2571 bstep = float(col2.Blue() - col1.Blue())/float(size)
2572
2573 y = tabPoints[0].y
2574
2575 # If we are drawing the selected tab, we need also to draw a line
2576 # from 0.tabPoints[0].x and tabPoints[6].x . end, we achieve this
2577 # by drawing the rectangle with transparent brush
2578 # the line under the selected tab will be deleted by the drwaing loop
2579 if bSelectedTab:
2580 self.DrawTabsLine(pc, dc)
2581
2582 while 1:
2583
2584 if pc.HasFlag(FNB_BOTTOM):
2585
2586 if y > tabPoints[0].y + size:
2587 break
2588
2589 else:
2590
2591 if y < tabPoints[0].y - size:
2592 break
2593
2594 currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
2595
2596 dc.SetPen((bSelectedTab and [wx.Pen(pc._activeTabColor)] or [wx.Pen(currCol)])[0])
2597 startX = self.GetStartX(tabPoints, y, pc.GetParent().GetWindowStyleFlag())
2598 endX = self.GetEndX(tabPoints, y, pc.GetParent().GetWindowStyleFlag())
2599 dc.DrawLine(startX, y, endX, y)
2600
2601 # Draw the border using the 'edge' point
2602 dc.SetPen(wx.Pen((bSelectedTab and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colorBorder])[0]))
2603
2604 dc.DrawPoint(startX, y)
2605 dc.DrawPoint(endX, y)
2606
2607 # Progress the color
2608 rf += rstep
2609 gf += gstep
2610 bf += bstep
2611
2612 if pc.HasFlag(FNB_BOTTOM):
2613 y = y + 1
2614 else:
2615 y = y - 1
2616
2617
2618 def GetStartX(self, tabPoints, y, style):
2619 """ Returns the x start position of a tab. """
2620
2621 x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
2622
2623 # We check the 3 points to the left
2624
2625 bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
2626 match = False
2627
2628 if bBottomStyle:
2629
2630 for i in xrange(3):
2631
2632 if y >= tabPoints[i].y and y < tabPoints[i+1].y:
2633
2634 x1 = tabPoints[i].x
2635 x2 = tabPoints[i+1].x
2636 y1 = tabPoints[i].y
2637 y2 = tabPoints[i+1].y
2638 match = True
2639 break
2640
2641 else:
2642
2643 for i in xrange(3):
2644
2645 if y <= tabPoints[i].y and y > tabPoints[i+1].y:
2646
2647 x1 = tabPoints[i].x
2648 x2 = tabPoints[i+1].x
2649 y1 = tabPoints[i].y
2650 y2 = tabPoints[i+1].y
2651 match = True
2652 break
2653
2654 if not match:
2655 return tabPoints[2].x
2656
2657 # According to the equation y = ax + b => x = (y-b)/a
2658 # We know the first 2 points
2659
2660 if x2 == x1:
2661 return x2
2662 else:
2663 a = (y2 - y1)/(x2 - x1)
2664
2665 b = y1 - ((y2 - y1)/(x2 - x1))*x1
2666
2667 if a == 0:
2668 return int(x1)
2669
2670 x = (y - b)/a
2671
2672 return int(x)
2673
2674
2675 def GetEndX(self, tabPoints, y, style):
2676 """ Returns the x end position of a tab. """
2677
2678 x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
2679
2680 # We check the 3 points to the left
2681 bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
2682 match = False
2683
2684 if bBottomStyle:
2685
2686 for i in xrange(7, 3, -1):
2687
2688 if y >= tabPoints[i].y and y < tabPoints[i-1].y:
2689
2690 x1 = tabPoints[i].x
2691 x2 = tabPoints[i-1].x
2692 y1 = tabPoints[i].y
2693 y2 = tabPoints[i-1].y
2694 match = True
2695 break
2696
2697 else:
2698
2699 for i in xrange(7, 3, -1):
2700
2701 if y <= tabPoints[i].y and y > tabPoints[i-1].y:
2702
2703 x1 = tabPoints[i].x
2704 x2 = tabPoints[i-1].x
2705 y1 = tabPoints[i].y
2706 y2 = tabPoints[i-1].y
2707 match = True
2708 break
2709
2710 if not match:
2711 return tabPoints[3].x
2712
2713 # According to the equation y = ax + b => x = (y-b)/a
2714 # We know the first 2 points
2715
2716 # Vertical line
2717 if x1 == x2:
2718 return int(x1)
2719
2720 a = (y2 - y1)/(x2 - x1)
2721 b = y1 - ((y2 - y1)/(x2 - x1))*x1
2722
2723 if a == 0:
2724 return int(x1)
2725
2726 x = (y - b)/a
2727
2728 return int(x)
2729
2730
2731 def NumberTabsCanFit(self, pageContainer, fr=-1):
2732 """ Returns the number of tabs that can fit in the visible area. """
2733
2734 pc = pageContainer
2735
2736 rect = pc.GetClientRect()
2737 clientWidth = rect.width
2738
2739 # Empty results
2740 vTabInfo = []
2741 tabHeight = self.CalcTabHeight(pageContainer)
2742
2743 # The drawing starts from posx
2744 posx = pc._pParent.GetPadding()
2745
2746 if fr < 0:
2747 fr = pc._nFrom
2748
2749 for i in xrange(fr, len(pc._pagesInfoVec)):
2750
2751 vc8glitch = tabHeight + FNB_HEIGHT_SPACER
2752 tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
2753
2754 if posx + tabWidth + vc8glitch + self.GetButtonsAreaLength(pc) >= clientWidth:
2755 break
2756
2757 # Add a result to the returned vector
2758 tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth, tabHeight)
2759 vTabInfo.append(tabRect)
2760
2761 # Advance posx
2762 posx += tabWidth + FNB_HEIGHT_SPACER
2763
2764 return vTabInfo
2765
2766
2767 # ---------------------------------------------------------------------------- #
2768 # Class FlatNotebook
2769 # ---------------------------------------------------------------------------- #
2770
2771 class FlatNotebook(wx.Panel):
2772 """
2773 Display one or more windows in a notebook.
2774
2775 B{Events}:
2776 - B{EVT_FLATNOTEBOOK_PAGE_CHANGING}: sent when the active
2777 page in the notebook is changing
2778 - B{EVT_FLATNOTEBOOK_PAGE_CHANGED}: sent when the active
2779 page in the notebook has changed
2780 - B{EVT_FLATNOTEBOOK_PAGE_CLOSING}: sent when a page in the
2781 notebook is closing
2782 - B{EVT_FLATNOTEBOOK_PAGE_CLOSED}: sent when a page in the
2783 notebook has been closed
2784 - B{EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU}: sent when the user
2785 clicks a tab in the notebook with the right mouse
2786 button
2787 """
2788
2789 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
2790 style=0, name="FlatNotebook"):
2791 """
2792 Default class constructor.
2793
2794 All the parameters are as in wxPython class construction, except the
2795 'style': this can be assigned to whatever combination of FNB_* styles.
2796
2797 """
2798
2799 self._bForceSelection = False
2800 self._nPadding = 6
2801 self._nFrom = 0
2802 style |= wx.TAB_TRAVERSAL
2803 self._pages = None
2804 self._windows = []
2805 self._popupWin = None
2806
2807 wx.Panel.__init__(self, parent, id, pos, size, style)
2808
2809 self._pages = PageContainer(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, style)
2810
2811 self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
2812
2813 self.Init()
2814
2815
2816 def Init(self):
2817 """ Initializes all the class attributes. """
2818
2819 self._pages._colorBorder = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
2820
2821 self._mainSizer = wx.BoxSizer(wx.VERTICAL)
2822 self.SetSizer(self._mainSizer)
2823
2824 # The child panels will inherit this bg color, so leave it at the default value
2825 #self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_APPWORKSPACE))
2826
2827 # Set default page height
2828 dc = wx.ClientDC(self)
2829
2830 if "__WXGTK__" in wx.PlatformInfo:
2831 # For GTK it seems that we must do this steps in order
2832 # for the tabs will get the proper height on initialization
2833 # on MSW, preforming these steps yields wierd results
2834 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
2835 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
2836 dc.SetFont(boldFont)
2837
2838 height = dc.GetCharHeight()
2839
2840 tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
2841
2842 if "__WXGTK__" in wx.PlatformInfo:
2843 tabHeight += 6
2844
2845 self._pages.SetSizeHints(-1, tabHeight)
2846 # Add the tab container to the sizer
2847 self._mainSizer.Insert(0, self._pages, 0, wx.EXPAND)
2848 self._mainSizer.Layout()
2849
2850 self._pages._nFrom = self._nFrom
2851 self._pDropTarget = FNBDropTarget(self)
2852 self.SetDropTarget(self._pDropTarget)
2853
2854
2855 def SetActiveTabTextColour(self, textColour):
2856 """ Sets the text colour for the active tab. """
2857
2858 self._pages._activeTextColor = textColour
2859
2860
2861 def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
2862 """ Handles the drop action from a DND operation. """
2863
2864 return self._pages.OnDropTarget(x, y, nTabPage, wnd_oldContainer)
2865
2866
2867 def GetPreviousSelection(self):
2868 """ Returns the previous selection. """
2869
2870 return self._pages._iPreviousActivePage
2871
2872
2873 def AddPage(self, page, text, select=True, imageId=-1):
2874 """
2875 Add a page to the L{FlatNotebook}.
2876
2877 @param page: Specifies the new page.
2878 @param text: Specifies the text for the new page.
2879 @param select: Specifies whether the page should be selected.
2880 @param imageId: Specifies the optional image index for the new page.
2881
2882 Return value:
2883 True if successful, False otherwise.
2884 """
2885
2886 # sanity check
2887 if not page:
2888 return False
2889
2890 # reparent the window to us
2891 page.Reparent(self)
2892
2893 # Add tab
2894 bSelected = select or len(self._windows) == 0
2895
2896 if bSelected:
2897
2898 bSelected = False
2899
2900 # Check for selection and send events
2901 oldSelection = self._pages._iActivePage
2902 tabIdx = len(self._windows)
2903
2904 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
2905 event.SetSelection(tabIdx)
2906 event.SetOldSelection(oldSelection)
2907 event.SetEventObject(self)
2908
2909 if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
2910 bSelected = True
2911
2912 curSel = self._pages.GetSelection()
2913
2914 if not self._pages.IsShown():
2915 self._pages.Show()
2916
2917 self._pages.AddPage(text, bSelected, imageId)
2918 self._windows.append(page)
2919
2920 self.Freeze()
2921
2922 # Check if a new selection was made
2923 if bSelected:
2924
2925 if curSel >= 0:
2926
2927 # Remove the window from the main sizer
2928 self._mainSizer.Detach(self._windows[curSel])
2929 self._windows[curSel].Hide()
2930
2931 if self.GetWindowStyleFlag() & FNB_BOTTOM:
2932
2933 self._mainSizer.Insert(0, page, 1, wx.EXPAND)
2934
2935 else:
2936
2937 # We leave a space of 1 pixel around the window
2938 self._mainSizer.Add(page, 1, wx.EXPAND)
2939
2940 # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
2941 event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
2942 event.SetOldSelection(oldSelection)
2943 self.GetEventHandler().ProcessEvent(event)
2944
2945 else:
2946
2947 # Hide the page
2948 page.Hide()
2949
2950 self.Thaw()
2951 self._mainSizer.Layout()
2952 self.Refresh()
2953
2954 return True
2955
2956
2957 def SetImageList(self, imageList):
2958 """
2959 Sets the image list for the page control. It does not take ownership
2960 of the image list, you must delete it yourself.
2961 """
2962
2963 self._pages.SetImageList(imageList)
2964
2965
2966 def GetImageList(self):
2967 """ Returns the associated image list. """
2968
2969 return self._pages.GetImageList()
2970
2971
2972 def InsertPage(self, indx, page, text, select=True, imageId=-1):
2973 """
2974 Inserts a new page at the specified position.
2975
2976 @param indx: Specifies the position of the new page.
2977 @param page: Specifies the new page.
2978 @param text: Specifies the text for the new page.
2979 @param select: Specifies whether the page should be selected.
2980 @param imageId: Specifies the optional image index for the new page.
2981
2982 Return value:
2983 True if successful, False otherwise.
2984 """
2985
2986 # sanity check
2987 if not page:
2988 return False
2989
2990 # reparent the window to us
2991 page.Reparent(self)
2992
2993 if not self._windows:
2994
2995 self.AddPage(page, text, select, imageId)
2996 return True
2997
2998 # Insert tab
2999 bSelected = select or not self._windows
3000 curSel = self._pages.GetSelection()
3001
3002 indx = max(0, min(indx, len(self._windows)))
3003
3004 if indx <= len(self._windows):
3005
3006 self._windows.insert(indx, page)
3007
3008 else:
3009
3010 self._windows.append(page)
3011
3012 if bSelected:
3013
3014 bSelected = False
3015
3016 # Check for selection and send events
3017 oldSelection = self._pages._iActivePage
3018
3019 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
3020 event.SetSelection(indx)
3021 event.SetOldSelection(oldSelection)
3022 event.SetEventObject(self)
3023
3024 if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
3025 bSelected = True
3026
3027 self._pages.InsertPage(indx, text, bSelected, imageId)
3028
3029 if indx <= curSel:
3030 curSel = curSel + 1
3031
3032 self.Freeze()
3033
3034 # Check if a new selection was made
3035 if bSelected:
3036
3037 if curSel >= 0:
3038
3039 # Remove the window from the main sizer
3040 self._mainSizer.Detach(self._windows[curSel])
3041 self._windows[curSel].Hide()
3042
3043 self._pages.SetSelection(indx)
3044
3045 # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
3046 event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
3047 event.SetOldSelection(oldSelection)
3048 self.GetEventHandler().ProcessEvent(event)
3049
3050 else:
3051
3052 # Hide the page
3053 page.Hide()
3054
3055 self.Thaw()
3056 self._mainSizer.Layout()
3057 self.Refresh()
3058
3059 return True
3060
3061
3062 def SetSelection(self, page):
3063 """
3064 Sets the selection for the given page.
3065 The call to this function generates the page changing events
3066 """
3067
3068 if page >= len(self._windows) or not self._windows:
3069 return
3070
3071 # Support for disabed tabs
3072 if not self._pages.GetEnabled(page) and len(self._windows) > 1 and not self._bForceSelection:
3073 return
3074
3075 curSel = self._pages.GetSelection()
3076
3077 # program allows the page change
3078 self.Freeze()
3079 if curSel >= 0:
3080
3081 # Remove the window from the main sizer
3082 self._mainSizer.Detach(self._windows[curSel])
3083 self._windows[curSel].Hide()
3084
3085 if self.GetWindowStyleFlag() & FNB_BOTTOM:
3086
3087 self._mainSizer.Insert(0, self._windows[page], 1, wx.EXPAND)
3088
3089 else:
3090
3091 # We leave a space of 1 pixel around the window
3092 self._mainSizer.Add(self._windows[page], 1, wx.EXPAND)
3093
3094 self._windows[page].Show()
3095 self.Thaw()
3096
3097 self._mainSizer.Layout()
3098
3099 if page != self._pages._iActivePage:
3100 # there is a real page changing
3101 self._pages._iPreviousActivePage = self._pages._iActivePage
3102
3103 self._pages._iActivePage = page
3104 self._pages.DoSetSelection(page)
3105
3106
3107 def DeletePage(self, page):
3108 """
3109 Deletes the specified page, and the associated window.
3110 The call to this function generates the page changing events.
3111 """
3112
3113 if page >= len(self._windows) or page < 0:
3114 return
3115
3116 # Fire a closing event
3117 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
3118 event.SetSelection(page)
3119 event.SetEventObject(self)
3120 self.GetEventHandler().ProcessEvent(event)
3121
3122 # The event handler allows it?
3123 if not event.IsAllowed():
3124 return
3125
3126 self.Freeze()
3127
3128 # Delete the requested page
3129 pageRemoved = self._windows[page]
3130
3131 # If the page is the current window, remove it from the sizer
3132 # as well
3133 if page == self._pages.GetSelection():
3134 self._mainSizer.Detach(pageRemoved)
3135
3136 # Remove it from the array as well
3137 self._windows.pop(page)
3138
3139 # Now we can destroy it in wxWidgets use Destroy instead of delete
3140 pageRemoved.Destroy()
3141
3142 self.Thaw()
3143
3144 self._pages.DoDeletePage(page)
3145 self.Refresh()
3146
3147 # Fire a closed event
3148 closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId())
3149 closedEvent.SetSelection(page)
3150 closedEvent.SetEventObject(self)
3151 self.GetEventHandler().ProcessEvent(closedEvent)
3152
3153
3154 def DeleteAllPages(self):
3155 """ Deletes all the pages. """
3156
3157 if not self._windows:
3158 return False
3159
3160 self.Freeze()
3161
3162 for page in self._windows:
3163 page.Destroy()
3164
3165 self._windows = []
3166 self.Thaw()
3167
3168 # Clear the container of the tabs as well
3169 self._pages.DeleteAllPages()
3170 return True
3171
3172
3173 def GetCurrentPage(self):
3174 """ Returns the currently selected notebook page or None. """
3175
3176 sel = self._pages.GetSelection()
3177 if sel < 0:
3178 return None
3179
3180 return self._windows[sel]
3181
3182
3183 def GetPage(self, page):
3184 """ Returns the window at the given page position, or None. """
3185
3186 if page >= len(self._windows):
3187 return None
3188
3189 return self._windows[page]
3190
3191
3192 def GetPageIndex(self, win):
3193 """ Returns the index at which the window is found. """
3194
3195 try:
3196 return self._windows.index(win)
3197 except:
3198 return -1
3199
3200
3201 def GetSelection(self):
3202 """ Returns the currently selected page, or -1 if none was selected. """
3203
3204 return self._pages.GetSelection()
3205
3206
3207 def AdvanceSelection(self, forward=True):
3208 """
3209 Cycles through the tabs.
3210 The call to this function generates the page changing events.
3211 """
3212
3213 self._pages.AdvanceSelection(forward)
3214
3215
3216 def GetPageCount(self):
3217 """ Returns the number of pages in the L{FlatNotebook} control. """
3218
3219 return self._pages.GetPageCount()
3220
3221
3222 def OnNavigationKey(self, event):
3223 """ Handles the wx.EVT_NAVIGATION_KEY event for L{FlatNotebook}. """
3224
3225 if event.IsWindowChange():
3226 if len(self._windows) == 0:
3227 return
3228 # change pages
3229 if self.HasFlag(FNB_SMART_TABS):
3230 if not self._popupWin:
3231 self._popupWin = TabNavigatorWindow(self)
3232 self._popupWin.SetReturnCode(wx.ID_OK)
3233 self._popupWin.ShowModal()
3234 self._popupWin.Destroy()
3235 self._popupWin = None
3236 else:
3237 # a dialog is already opened
3238 self._popupWin.OnNavigationKey(event)
3239 return
3240 else:
3241 # change pages
3242 self.AdvanceSelection(event.GetDirection())
3243 else:
3244 # pass to the parent
3245 if self.GetParent():
3246 event.SetCurrentFocus(self)
3247 self.GetParent().ProcessEvent(event)
3248
3249
3250 def GetPageShapeAngle(self, page_index):
3251 """ Returns the angle associated to a tab. """
3252
3253 if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
3254 return None, False
3255
3256 result = self._pages._pagesInfoVec[page_index].GetTabAngle()
3257 return result, True
3258
3259
3260 def SetPageShapeAngle(self, page_index, angle):
3261 """ Sets the angle associated to a tab. """
3262
3263 if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
3264 return
3265
3266 if angle > 15:
3267 return
3268
3269 self._pages._pagesInfoVec[page_index].SetTabAngle(angle)
3270
3271
3272 def SetAllPagesShapeAngle(self, angle):
3273 """ Sets the angle associated to all the tab. """
3274
3275 if angle > 15:
3276 return
3277
3278 for ii in xrange(len(self._pages._pagesInfoVec)):
3279 self._pages._pagesInfoVec[ii].SetTabAngle(angle)
3280
3281 self.Refresh()
3282
3283
3284 def GetPageBestSize(self):
3285 """ Return the page best size. """
3286
3287 return self._pages.GetClientSize()
3288
3289
3290 def SetPageText(self, page, text):
3291 """ Sets the text for the given page. """
3292
3293 bVal = self._pages.SetPageText(page, text)
3294 self._pages.Refresh()
3295
3296 return bVal
3297
3298
3299 def SetPadding(self, padding):
3300 """
3301 Sets the amount of space around each page's icon and label, in pixels.
3302 NB: only the horizontal padding is considered.
3303 """
3304
3305 self._nPadding = padding.GetWidth()
3306
3307
3308 def GetTabArea(self):
3309 """ Returns the associated page. """
3310
3311 return self._pages
3312
3313
3314 def GetPadding(self):
3315 """ Returns the amount of space around each page's icon and label, in pixels. """
3316
3317 return self._nPadding
3318
3319
3320 def SetWindowStyleFlag(self, style):
3321 """ Sets the L{FlatNotebook} window style flags. """
3322
3323 wx.Panel.SetWindowStyleFlag(self, style)
3324 renderer = self._pages._mgr.GetRenderer(self.GetWindowStyleFlag())
3325 renderer._tabHeight = None
3326
3327 if self._pages:
3328
3329 # For changing the tab position (i.e. placing them top/bottom)
3330 # refreshing the tab container is not enough
3331 self.SetSelection(self._pages._iActivePage)
3332
3333 if not self._pages.HasFlag(FNB_HIDE_ON_SINGLE_TAB):
3334 #For Redrawing the Tabs once you remove the Hide tyle
3335 self._pages._ReShow()
3336
3337
3338 def RemovePage(self, page):
3339 """ Deletes the specified page, without deleting the associated window. """
3340
3341 if page >= len(self._windows):
3342 return False
3343
3344 # Fire a closing event
3345 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
3346 event.SetSelection(page)
3347 event.SetEventObject(self)
3348 self.GetEventHandler().ProcessEvent(event)
3349
3350 # The event handler allows it?
3351 if not event.IsAllowed():
3352 return False
3353
3354 self.Freeze()
3355
3356 # Remove the requested page
3357 pageRemoved = self._windows[page]
3358
3359 # If the page is the current window, remove it from the sizer
3360 # as well
3361 if page == self._pages.GetSelection():
3362 self._mainSizer.Detach(pageRemoved)
3363
3364 # Remove it from the array as well
3365 self._windows.pop(page)
3366 self.Thaw()
3367
3368 self._pages.DoDeletePage(page)
3369
3370 return True
3371
3372
3373 def SetRightClickMenu(self, menu):
3374 """ Sets the popup menu associated to a right click on a tab. """
3375
3376 self._pages._pRightClickMenu = menu
3377
3378
3379 def GetPageText(self, nPage):
3380 """ Returns the tab caption. """
3381
3382 return self._pages.GetPageText(nPage)
3383
3384
3385 def SetGradientColours(self, fr, to, border):
3386 """ Sets the gradient colours for the tab. """
3387
3388 self._pages._colorFrom = fr
3389 self._pages._colorTo = to
3390 self._pages._colorBorder = border
3391
3392
3393 def SetGradientColourFrom(self, fr):
3394 """ Sets the starting colour for the gradient. """
3395
3396 self._pages._colorFrom = fr
3397
3398
3399 def SetGradientColourTo(self, to):
3400 """ Sets the ending colour for the gradient. """
3401
3402 self._pages._colorTo = to
3403
3404
3405 def SetGradientColourBorder(self, border):
3406 """ Sets the tab border colour. """
3407
3408 self._pages._colorBorder = border
3409
3410
3411 def GetGradientColourFrom(self):
3412 """ Gets first gradient colour. """
3413
3414 return self._pages._colorFrom
3415
3416
3417 def GetGradientColourTo(self):
3418 """ Gets second gradient colour. """
3419
3420 return self._pages._colorTo
3421
3422
3423 def GetGradientColourBorder(self):
3424 """ Gets the tab border colour. """
3425
3426 return self._pages._colorBorder
3427
3428
3429 def GetBorderColour(self):
3430 """ Returns the border colour. """
3431
3432 return self._pages._colorBorder
3433
3434
3435 def GetActiveTabTextColour(self):
3436 """ Get the active tab text colour. """
3437
3438 return self._pages._activeTextColor
3439
3440
3441 def SetPageImage(self, page, image):
3442 """
3443 Sets the image index for the given page. Image is an index into the
3444 image list which was set with SetImageList.
3445 """
3446
3447 self._pages.SetPageImage(page, image)
3448
3449
3450 def GetPageImage(self, nPage):
3451 """
3452 Returns the image index for the given page. Image is an index into the
3453 image list which was set with SetImageList.
3454 """
3455
3456 return self._pages.GetPageImage(nPage)
3457
3458
3459 def GetEnabled(self, page):
3460 """ Returns whether a tab is enabled or not. """
3461
3462 return self._pages.GetEnabled(page)
3463
3464
3465 def Enable(self, page, enabled=True):
3466 """ Enables or disables a tab. """
3467
3468 if page >= len(self._windows):
3469 return
3470
3471 self._windows[page].Enable(enabled)
3472 self._pages.Enable(page, enabled)
3473
3474
3475 def GetNonActiveTabTextColour(self):
3476 """ Returns the non active tabs text colour. """
3477
3478 return self._pages._nonActiveTextColor
3479
3480
3481 def SetNonActiveTabTextColour(self, color):
3482 """ Sets the non active tabs text colour. """
3483
3484 self._pages._nonActiveTextColor = color
3485
3486
3487 def SetTabAreaColour(self, color):
3488 """ Sets the area behind the tabs colour. """
3489
3490 self._pages._tabAreaColor = color
3491
3492
3493 def GetTabAreaColour(self):
3494 """ Returns the area behind the tabs colour. """
3495
3496 return self._pages._tabAreaColor
3497
3498
3499 def SetActiveTabColour(self, color):
3500 """ Sets the active tab colour. """
3501
3502 self._pages._activeTabColor = color
3503
3504
3505 def GetActiveTabColour(self):
3506 """ Returns the active tab colour. """
3507
3508 return self._pages._activeTabColor
3509
3510
3511 # ---------------------------------------------------------------------------- #
3512 # Class PageContainer
3513 # Acts as a container for the pages you add to FlatNotebook
3514 # ---------------------------------------------------------------------------- #
3515
3516 class PageContainer(wx.Panel):
3517 """
3518 This class acts as a container for the pages you add to L{FlatNotebook}.
3519 """
3520
3521 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
3522 size=wx.DefaultSize, style=0):
3523 """ Default class constructor. """
3524
3525 self._ImageList = None
3526 self._iActivePage = -1
3527 self._pDropTarget = None
3528 self._nLeftClickZone = FNB_NOWHERE
3529 self._iPreviousActivePage = -1
3530
3531 self._pRightClickMenu = None
3532 self._nXButtonStatus = FNB_BTN_NONE
3533 self._nArrowDownButtonStatus = FNB_BTN_NONE
3534 self._pParent = parent
3535 self._nRightButtonStatus = FNB_BTN_NONE
3536 self._nLeftButtonStatus = FNB_BTN_NONE
3537 self._nTabXButtonStatus = FNB_BTN_NONE
3538
3539 self._pagesInfoVec = []
3540
3541 self._colorTo = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
3542 self._colorFrom = wx.WHITE
3543 self._activeTabColor = wx.WHITE
3544 self._activeTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT)
3545 self._nonActiveTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
3546 self._tabAreaColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE)
3547
3548 self._nFrom = 0
3549 self._isdragging = False
3550
3551 # Set default page height, this is done according to the system font
3552 memDc = wx.MemoryDC()
3553 memDc.SelectObject(wx.EmptyBitmap(1,1))
3554
3555 if "__WXGTK__" in wx.PlatformInfo:
3556 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
3557 boldFont.SetWeight(wx.BOLD)
3558 memDc.SetFont(boldFont)
3559
3560 height = memDc.GetCharHeight()
3561 tabHeight = height + FNB_HEIGHT_SPACER # We use 10 pixels as padding
3562
3563 wx.Panel.__init__(self, parent, id, pos, wx.Size(size.x, tabHeight),
3564 style|wx.NO_BORDER|wx.NO_FULL_REPAINT_ON_RESIZE)
3565
3566 self._pDropTarget = FNBDropTarget(self)
3567 self.SetDropTarget(self._pDropTarget)
3568 self._mgr = FNBRendererMgr()
3569
3570 self.Bind(wx.EVT_PAINT, self.OnPaint)
3571 self.Bind(wx.EVT_SIZE, self.OnSize)
3572 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
3573 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
3574 self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
3575 self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
3576 self.Bind(wx.EVT_MOTION, self.OnMouseMove)
3577 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
3578 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
3579 self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
3580 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
3581
3582
3583 def OnEraseBackground(self, event):
3584 """ Handles the wx.EVT_ERASE_BACKGROUND event for L{PageContainer} (does nothing)."""
3585
3586 pass
3587
3588
3589 def _ReShow(self):
3590 """ Handles the Redraw of the tabs when the FNB_HIDE_ON_SINGLE_TAB has been removed """
3591 self.Show()
3592 self.GetParent()._mainSizer.Layout()
3593 self.Refresh()
3594
3595
3596 def OnPaint(self, event):
3597 """ Handles the wx.EVT_PAINT event for L{PageContainer}."""
3598
3599 dc = wx.BufferedPaintDC(self)
3600 renderer = self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag())
3601 renderer.DrawTabs(self, dc)
3602
3603 if self.HasFlag(FNB_HIDE_ON_SINGLE_TAB) and len(self._pagesInfoVec) <= 1:
3604 self.Hide()
3605 self.GetParent()._mainSizer.Layout()
3606 self.Refresh()
3607
3608
3609 def AddPage(self, caption, selected=True, imgindex=-1):
3610 """
3611 Add a page to the L{FlatNotebook}.
3612
3613 @param window: Specifies the new page.
3614 @param caption: Specifies the text for the new page.
3615 @param selected: Specifies whether the page should be selected.
3616 @param imgindex: Specifies the optional image index for the new page.
3617
3618 Return value:
3619 True if successful, False otherwise.
3620 """
3621
3622 if selected:
3623
3624 self._iPreviousActivePage = self._iActivePage
3625 self._iActivePage = len(self._pagesInfoVec)
3626
3627 # Create page info and add it to the vector
3628 pageInfo = PageInfo(caption, imgindex)
3629 self._pagesInfoVec.append(pageInfo)
3630 self.Refresh()
3631
3632
3633 def InsertPage(self, indx, text, selected=True, imgindex=-1):
3634 """
3635 Inserts a new page at the specified position.
3636
3637 @param indx: Specifies the position of the new page.
3638 @param page: Specifies the new page.
3639 @param text: Specifies the text for the new page.
3640 @param select: Specifies whether the page should be selected.
3641 @param imgindex: Specifies the optional image index for the new page.
3642
3643 Return value:
3644 True if successful, False otherwise.
3645 """
3646
3647 if selected:
3648
3649 self._iPreviousActivePage = self._iActivePage
3650 self._iActivePage = len(self._pagesInfoVec)
3651
3652 self._pagesInfoVec.insert(indx, PageInfo(text, imgindex))
3653
3654 self.Refresh()
3655 return True
3656
3657
3658 def OnSize(self, event):
3659 """ Handles the wx.EVT_SIZE events for L{PageContainer}. """
3660
3661 # When resizing the control, try to fit to screen as many tabs as we can
3662 style = self.GetParent().GetWindowStyleFlag()
3663 renderer = self._mgr.GetRenderer(style)
3664
3665 fr = 0
3666 page = self.GetSelection()
3667
3668 for fr in xrange(self._nFrom):
3669 vTabInfo = renderer.NumberTabsCanFit(self, fr)
3670 if page - fr >= len(vTabInfo):
3671 continue
3672 break
3673
3674 self._nFrom = fr
3675
3676 self.Refresh() # Call on paint
3677 event.Skip()
3678
3679
3680 def OnMiddleDown(self, event):
3681 """ Handles the wx.EVT_MIDDLE_DOWN events for L{PageContainer}. """
3682
3683 # Test if this style is enabled
3684 style = self.GetParent().GetWindowStyleFlag()
3685
3686 if not style & FNB_MOUSE_MIDDLE_CLOSES_TABS:
3687 return
3688
3689 where, tabIdx = self.HitTest(event.GetPosition())
3690
3691 if where == FNB_TAB:
3692 self.DeletePage(tabIdx)
3693
3694 event.Skip()
3695
3696
3697 def OnRightDown(self, event):
3698 """ Handles the wx.EVT_RIGHT_DOWN events for L{PageContainer}. """
3699
3700 where, tabIdx = self.HitTest(event.GetPosition())
3701
3702 if where in [FNB_TAB, FNB_TAB_X]:
3703
3704 if self._pagesInfoVec[tabIdx].GetEnabled():
3705 # Fire events and eventually (if allowed) change selection
3706 self.FireEvent(tabIdx)
3707
3708 # send a message to popup a custom menu
3709 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, self.GetParent().GetId())
3710 event.SetSelection(tabIdx)
3711 event.SetOldSelection(self._iActivePage)
3712 event.SetEventObject(self.GetParent())
3713 self.GetParent().GetEventHandler().ProcessEvent(event)
3714
3715 if self._pRightClickMenu:
3716 self.PopupMenu(self._pRightClickMenu)
3717
3718 event.Skip()
3719
3720
3721 def OnLeftDown(self, event):
3722 """ Handles the wx.EVT_LEFT_DOWN events for L{PageContainer}. """
3723
3724 # Reset buttons status
3725 self._nXButtonStatus = FNB_BTN_NONE
3726 self._nLeftButtonStatus = FNB_BTN_NONE
3727 self._nRightButtonStatus = FNB_BTN_NONE
3728 self._nTabXButtonStatus = FNB_BTN_NONE
3729 self._nArrowDownButtonStatus = FNB_BTN_NONE
3730
3731 self._nLeftClickZone, tabIdx = self.HitTest(event.GetPosition())
3732
3733 if self._nLeftClickZone == FNB_DROP_DOWN_ARROW:
3734 self._nArrowDownButtonStatus = FNB_BTN_PRESSED
3735 self.Refresh()
3736 elif self._nLeftClickZone == FNB_LEFT_ARROW:
3737 self._nLeftButtonStatus = FNB_BTN_PRESSED
3738 self.Refresh()
3739 elif self._nLeftClickZone == FNB_RIGHT_ARROW:
3740 self._nRightButtonStatus = FNB_BTN_PRESSED
3741 self.Refresh()
3742 elif self._nLeftClickZone == FNB_X:
3743 self._nXButtonStatus = FNB_BTN_PRESSED
3744 self.Refresh()
3745 elif self._nLeftClickZone == FNB_TAB_X:
3746 self._nTabXButtonStatus = FNB_BTN_PRESSED
3747 self.Refresh()
3748
3749 elif self._nLeftClickZone == FNB_TAB:
3750
3751 if self._iActivePage != tabIdx:
3752
3753 # In case the tab is disabled, we dont allow to choose it
3754 if self._pagesInfoVec[tabIdx].GetEnabled():
3755 self.FireEvent(tabIdx)
3756
3757
3758 def OnLeftUp(self, event):
3759 """ Handles the wx.EVT_LEFT_UP events for L{PageContainer}. """
3760
3761 # forget the zone that was initially clicked
3762 self._nLeftClickZone = FNB_NOWHERE
3763
3764 where, tabIdx = self.HitTest(event.GetPosition())
3765
3766 if where == FNB_LEFT_ARROW:
3767
3768 if self._nFrom == 0:
3769 return
3770
3771 # Make sure that the button was pressed before
3772 if self._nLeftButtonStatus != FNB_BTN_PRESSED:
3773 return
3774
3775 self._nLeftButtonStatus = FNB_BTN_HOVER
3776
3777 # We scroll left with bulks of 5
3778 scrollLeft = self.GetNumTabsCanScrollLeft()
3779
3780 self._nFrom -= scrollLeft
3781 if self._nFrom < 0:
3782 self._nFrom = 0
3783
3784 self.Refresh()
3785
3786 elif where == FNB_RIGHT_ARROW:
3787
3788 if self._nFrom >= len(self._pagesInfoVec) - 1:
3789 return
3790
3791 # Make sure that the button was pressed before
3792 if self._nRightButtonStatus != FNB_BTN_PRESSED:
3793 return
3794
3795 self._nRightButtonStatus = FNB_BTN_HOVER
3796
3797 # Check if the right most tab is visible, if it is
3798 # don't rotate right anymore
3799 if self._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1):
3800 return
3801
3802 lastVisibleTab = self.GetLastVisibleTab()
3803 if lastVisibleTab < 0:
3804 # Probably the screen is too small for displaying even a single
3805 # tab, in this case we do nothing
3806 return
3807
3808 self._nFrom += self.GetNumOfVisibleTabs()
3809 self.Refresh()
3810
3811 elif where == FNB_X:
3812
3813 # Make sure that the button was pressed before
3814 if self._nXButtonStatus != FNB_BTN_PRESSED:
3815 return
3816
3817 self._nXButtonStatus = FNB_BTN_HOVER
3818
3819 self.DeletePage(self._iActivePage)
3820
3821 elif where == FNB_TAB_X:
3822
3823 # Make sure that the button was pressed before
3824 if self._nTabXButtonStatus != FNB_BTN_PRESSED:
3825 return
3826
3827 self._nTabXButtonStatus = FNB_BTN_HOVER
3828
3829 self.DeletePage(self._iActivePage)
3830
3831 elif where == FNB_DROP_DOWN_ARROW:
3832
3833 # Make sure that the button was pressed before
3834 if self._nArrowDownButtonStatus != FNB_BTN_PRESSED:
3835 return
3836
3837 self._nArrowDownButtonStatus = FNB_BTN_NONE
3838
3839 # Refresh the button status
3840 renderer = self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag())
3841 dc = wx.ClientDC(self)
3842 renderer.DrawDropDownArrow(self, dc)
3843
3844 self.PopupTabsMenu()
3845
3846
3847 def HitTest(self, pt):
3848 """
3849 HitTest method for L{PageContainer}.
3850 Returns the flag (if any) and the hit page (if any).
3851 """
3852
3853 style = self.GetParent().GetWindowStyleFlag()
3854 render = self._mgr.GetRenderer(style)
3855
3856 fullrect = self.GetClientRect()
3857 btnLeftPos = render.GetLeftButtonPos(self)
3858 btnRightPos = render.GetRightButtonPos(self)
3859 btnXPos = render.GetXPos(self)
3860
3861 tabIdx = -1
3862
3863 if len(self._pagesInfoVec) == 0:
3864 return FNB_NOWHERE, tabIdx
3865
3866 rect = wx.Rect(btnXPos, 8, 16, 16)
3867 if rect.Contains(pt):
3868 return (style & FNB_NO_X_BUTTON and [FNB_NOWHERE] or [FNB_X])[0], tabIdx
3869
3870 rect = wx.Rect(btnRightPos, 8, 16, 16)
3871 if style & FNB_DROPDOWN_TABS_LIST:
3872 rect = wx.Rect(render.GetDropArrowButtonPos(self), 8, 16, 16)
3873 if rect.Contains(pt):
3874 return FNB_DROP_DOWN_ARROW, tabIdx
3875
3876 if rect.Contains(pt):
3877 return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_RIGHT_ARROW])[0], tabIdx
3878
3879 rect = wx.Rect(btnLeftPos, 8, 16, 16)
3880 if rect.Contains(pt):
3881 return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_LEFT_ARROW])[0], tabIdx
3882
3883 # Test whether a left click was made on a tab
3884 bFoundMatch = False
3885
3886 for cur in xrange(self._nFrom, len(self._pagesInfoVec)):
3887
3888 pgInfo = self._pagesInfoVec[cur]
3889
3890 if pgInfo.GetPosition() == wx.Point(-1, -1):
3891 continue
3892
3893 if style & FNB_X_ON_TAB and cur == self.GetSelection():
3894 # 'x' button exists on a tab
3895 if self._pagesInfoVec[cur].GetXRect().Contains(pt):
3896 return FNB_TAB_X, cur
3897
3898 if style & FNB_VC8:
3899
3900 if self._pagesInfoVec[cur].GetRegion().Contains(pt.x, pt.y):
3901 if bFoundMatch or cur == self.GetSelection():
3902 return FNB_TAB, cur
3903
3904 tabIdx = cur
3905 bFoundMatch = True
3906
3907 else:
3908
3909 tabRect = wx.Rect(pgInfo.GetPosition().x, pgInfo.GetPosition().y,
3910 pgInfo.GetSize().x, pgInfo.GetSize().y)
3911
3912 if tabRect.Contains(pt):
3913 # We have a match
3914 return FNB_TAB, cur
3915
3916 if bFoundMatch:
3917 return FNB_TAB, tabIdx
3918
3919 if self._isdragging:
3920 # We are doing DND, so check also the region outside the tabs
3921 # try before the first tab
3922 pgInfo = self._pagesInfoVec[0]
3923 tabRect = wx.Rect(0, pgInfo.GetPosition().y, pgInfo.GetPosition().x, self.GetParent().GetSize().y)
3924 if tabRect.Contains(pt):
3925 return FNB_TAB, 0
3926
3927 # try after the last tab
3928 pgInfo = self._pagesInfoVec[-1]
3929 startpos = pgInfo.GetPosition().x+pgInfo.GetSize().x
3930 tabRect = wx.Rect(startpos, pgInfo.GetPosition().y, fullrect.width-startpos, self.GetParent().GetSize().y)
3931
3932 if tabRect.Contains(pt):
3933 return FNB_TAB, len(self._pagesInfoVec)
3934
3935 # Default
3936 return FNB_NOWHERE, -1
3937
3938
3939 def SetSelection(self, page):
3940 """ Sets the selected page. """
3941
3942 book = self.GetParent()
3943 book.SetSelection(page)
3944 self.DoSetSelection(page)
3945
3946
3947 def DoSetSelection(self, page):
3948 """ Does the actual selection of a page. """
3949
3950 if page < len(self._pagesInfoVec):
3951 #! fix for tabfocus
3952 da_page = self._pParent.GetPage(page)
3953
3954 if da_page != None:
3955 da_page.SetFocus()
3956
3957 if not self.IsTabVisible(page):
3958 # Try to remove one tab from start and try again
3959
3960 if not self.CanFitToScreen(page):
3961
3962 if self._nFrom > page:
3963 self._nFrom = page
3964 else:
3965 while self._nFrom < page:
3966 self._nFrom += 1
3967 if self.CanFitToScreen(page):
3968 break
3969
3970 self.Refresh()
3971
3972
3973 def DeletePage(self, page):
3974 """ Delete the specified page from L{FlatNotebook}. """
3975
3976 book = self.GetParent()
3977 book.DeletePage(page)
3978 book.Refresh()
3979
3980
3981 def IsTabVisible(self, page):
3982 """ Returns whether a tab is visible or not. """
3983
3984 iLastVisiblePage = self.GetLastVisibleTab()
3985 return page <= iLastVisiblePage and page >= self._nFrom
3986
3987
3988 def DoDeletePage(self, page):
3989 """ Does the actual page deletion. """
3990
3991 # Remove the page from the vector
3992 book = self.GetParent()
3993 self._pagesInfoVec.pop(page)
3994
3995 # Thanks to Yiaanis AKA Mandrav
3996 if self._iActivePage >= page:
3997 self._iActivePage = self._iActivePage - 1
3998 self._iPreviousActivePage = -1
3999
4000 # The delete page was the last first on the array,
4001 # but the book still has more pages, so we set the
4002 # active page to be the first one (0)
4003 if self._iActivePage < 0 and len(self._pagesInfoVec) > 0:
4004 self._iActivePage = 0
4005 self._iPreviousActivePage = -1
4006
4007 # Refresh the tabs
4008 if self._iActivePage >= 0:
4009
4010 book._bForceSelection = True
4011
4012 # Check for selection and send event
4013 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
4014 event.SetSelection(self._iActivePage)
4015 event.SetOldSelection(self._iPreviousActivePage)
4016 event.SetEventObject(self.GetParent())
4017
4018 book.SetSelection(self._iActivePage)
4019 book._bForceSelection = False
4020
4021 # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
4022 event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
4023 event.SetOldSelection(self._iPreviousActivePage)
4024 self.GetParent().GetEventHandler().ProcessEvent(event)
4025
4026 if not self._pagesInfoVec:
4027 # Erase the page container drawings
4028 dc = wx.ClientDC(self)
4029 dc.Clear()
4030
4031
4032 def DeleteAllPages(self):
4033 """ Deletes all the pages. """
4034
4035 self._iActivePage = -1
4036 self._iPreviousActivePage = -1
4037 self._nFrom = 0
4038 self._pagesInfoVec = []
4039
4040 # Erase the page container drawings
4041 dc = wx.ClientDC(self)
4042 dc.Clear()
4043
4044
4045 def OnMouseMove(self, event):
4046 """ Handles the wx.EVT_MOTION for L{PageContainer}. """
4047
4048 if self._pagesInfoVec and self.IsShown():
4049
4050 xButtonStatus = self._nXButtonStatus
4051 xTabButtonStatus = self._nTabXButtonStatus
4052 rightButtonStatus = self._nRightButtonStatus
4053 leftButtonStatus = self._nLeftButtonStatus
4054 dropDownButtonStatus = self._nArrowDownButtonStatus
4055
4056 style = self.GetParent().GetWindowStyleFlag()
4057
4058 self._nXButtonStatus = FNB_BTN_NONE
4059 self._nRightButtonStatus = FNB_BTN_NONE
4060 self._nLeftButtonStatus = FNB_BTN_NONE
4061 self._nTabXButtonStatus = FNB_BTN_NONE
4062 self._nArrowDownButtonStatus = FNB_BTN_NONE
4063
4064 where, tabIdx = self.HitTest(event.GetPosition())
4065
4066 if where == FNB_X:
4067 if event.LeftIsDown():
4068
4069 self._nXButtonStatus = (self._nLeftClickZone==FNB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
4070
4071 else:
4072
4073 self._nXButtonStatus = FNB_BTN_HOVER
4074
4075 elif where == FNB_DROP_DOWN_ARROW:
4076 if event.LeftIsDown():
4077
4078 self._nArrowDownButtonStatus = (self._nLeftClickZone==FNB_DROP_DOWN_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
4079
4080 else:
4081
4082 self._nArrowDownButtonStatus = FNB_BTN_HOVER
4083
4084 elif where == FNB_TAB_X:
4085 if event.LeftIsDown():
4086
4087 self._nTabXButtonStatus = (self._nLeftClickZone==FNB_TAB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
4088
4089 else:
4090
4091 self._nTabXButtonStatus = FNB_BTN_HOVER
4092
4093 elif where == FNB_RIGHT_ARROW:
4094 if event.LeftIsDown():
4095
4096 self._nRightButtonStatus = (self._nLeftClickZone==FNB_RIGHT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
4097
4098 else:
4099
4100 self._nRightButtonStatus = FNB_BTN_HOVER
4101
4102 elif where == FNB_LEFT_ARROW:
4103 if event.LeftIsDown():
4104
4105 self._nLeftButtonStatus = (self._nLeftClickZone==FNB_LEFT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
4106
4107 else:
4108
4109 self._nLeftButtonStatus = FNB_BTN_HOVER
4110
4111 elif where == FNB_TAB:
4112 # Call virtual method for showing tooltip
4113 self.ShowTabTooltip(tabIdx)
4114
4115 if not self.GetEnabled(tabIdx):
4116 # Set the cursor to be 'No-entry'
4117 wx.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
4118
4119 # Support for drag and drop
4120 if event.Dragging() and not (style & FNB_NODRAG):
4121
4122 self._isdragging = True
4123 draginfo = FNBDragInfo(self, tabIdx)
4124 drginfo = cPickle.dumps(draginfo)
4125 dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook"))
4126 dataobject.SetData(drginfo)
4127 dragSource = FNBDropSource(self)
4128 dragSource.SetData(dataobject)
4129 dragSource.DoDragDrop(wx.Drag_DefaultMove)
4130
4131 bRedrawX = self._nXButtonStatus != xButtonStatus
4132 bRedrawRight = self._nRightButtonStatus != rightButtonStatus
4133 bRedrawLeft = self._nLeftButtonStatus != leftButtonStatus
4134 bRedrawTabX = self._nTabXButtonStatus != xTabButtonStatus
4135 bRedrawDropArrow = self._nArrowDownButtonStatus != dropDownButtonStatus
4136
4137 render = self._mgr.GetRenderer(style)
4138
4139 if (bRedrawX or bRedrawRight or bRedrawLeft or bRedrawTabX or bRedrawDropArrow):
4140
4141 dc = wx.ClientDC(self)
4142
4143 if bRedrawX:
4144
4145 render.DrawX(self, dc)
4146
4147 if bRedrawLeft:
4148
4149 render.DrawLeftArrow(self, dc)
4150
4151 if bRedrawRight:
4152
4153 render.DrawRightArrow(self, dc)
4154
4155 if bRedrawTabX:
4156
4157 render.DrawTabX(self, dc, self._pagesInfoVec[tabIdx].GetXRect(), tabIdx, self._nTabXButtonStatus)
4158
4159 if bRedrawDropArrow:
4160
4161 render.DrawDropDownArrow(self, dc)
4162
4163 event.Skip()
4164
4165
4166 def GetLastVisibleTab(self):
4167 """ Returns the last visible tab. """
4168
4169 if self._nFrom < 0:
4170 return -1
4171
4172 ii = 0
4173
4174 for ii in xrange(self._nFrom, len(self._pagesInfoVec)):
4175
4176 if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
4177 break
4178
4179 return ii-1
4180
4181
4182 def GetNumTabsCanScrollLeft(self):
4183 """ Returns the number of tabs than can be scrolled left. """
4184
4185 # Reserved area for the buttons (<>x)
4186 rect = self.GetClientRect()
4187 clientWidth = rect.width
4188 posx = self._pParent._nPadding
4189 numTabs = 0
4190 pom = 0
4191
4192 # In case we have error prevent crash
4193 if self._nFrom < 0:
4194 return 0
4195
4196 dc = wx.ClientDC(self)
4197
4198 style = self.GetParent().GetWindowStyleFlag()
4199 render = self._mgr.GetRenderer(style)
4200
4201 for ii in xrange(self._nFrom, -1, -1):
4202
4203 boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
4204 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
4205 dc.SetFont(boldFont)
4206
4207 height = dc.GetCharHeight()
4208
4209 tabHeight = height + FNB_HEIGHT_SPACER # We use 6 pixels as padding
4210 if style & FNB_VC71:
4211 tabHeight = (style & FNB_BOTTOM and [tabHeight - 4] or [tabHeight])[0]
4212 elif style & FNB_FANCY_TABS:
4213 tabHeight = (style & FNB_BOTTOM and [tabHeight - 3] or [tabHeight])[0]
4214
4215 width, pom = dc.GetTextExtent(self.GetPageText(ii))
4216 if style != FNB_VC71:
4217 shapePoints = int(tabHeight*math.tan(float(self._pagesInfoVec[ii].GetTabAngle())/180.0*math.pi))
4218 else:
4219 shapePoints = 0
4220
4221 tabWidth = 2*self._pParent._nPadding + width
4222
4223 if not (style & FNB_VC71):
4224 # Default style
4225 tabWidth += 2*shapePoints
4226
4227 hasImage = self._ImageList != None and self._pagesInfoVec[ii].GetImageIndex() != -1
4228
4229 # For VC71 style, we only add the icon size (16 pixels)
4230 if hasImage:
4231
4232 if not self.IsDefaultTabs():
4233 tabWidth += 16 + self._pParent._nPadding
4234 else:
4235 # Default style
4236 tabWidth += 16 + self._pParent._nPadding + shapePoints/2
4237
4238 if posx + tabWidth + render.GetButtonsAreaLength(self) >= clientWidth:
4239 break
4240
4241 numTabs = numTabs + 1
4242 posx += tabWidth
4243
4244 return numTabs
4245
4246
4247 def IsDefaultTabs(self):
4248 """ Returns whether a tab has a default style. """
4249
4250 style = self.GetParent().GetWindowStyleFlag()
4251 res = (style & FNB_VC71) or (style & FNB_FANCY_TABS) or (style & FNB_VC8)
4252 return not res
4253
4254
4255 def AdvanceSelection(self, bForward=True):
4256 """
4257 Cycles through the tabs.
4258 The call to this function generates the page changing events.
4259 """
4260
4261 nSel = self.GetSelection()
4262
4263 if nSel < 0:
4264 return
4265
4266 nMax = self.GetPageCount() - 1
4267
4268 if bForward:
4269 newSelection = (nSel == nMax and [0] or [nSel + 1])[0]
4270 else:
4271 newSelection = (nSel == 0 and [nMax] or [nSel - 1])[0]
4272
4273 if not self._pagesInfoVec[newSelection].GetEnabled():
4274 return
4275
4276 self.FireEvent(newSelection)
4277
4278
4279 def OnMouseLeave(self, event):
4280 """ Handles the wx.EVT_LEAVE_WINDOW event for L{PageContainer}. """
4281
4282 self._nLeftButtonStatus = FNB_BTN_NONE
4283 self._nXButtonStatus = FNB_BTN_NONE
4284 self._nRightButtonStatus = FNB_BTN_NONE
4285 self._nTabXButtonStatus = FNB_BTN_NONE
4286 self._nArrowDownButtonStatus = FNB_BTN_NONE
4287
4288 style = self.GetParent().GetWindowStyleFlag()
4289 render = self._mgr.GetRenderer(style)
4290
4291 dc = wx.ClientDC(self)
4292
4293 render.DrawX(self, dc)
4294 render.DrawLeftArrow(self, dc)
4295 render.DrawRightArrow(self, dc)
4296
4297 selection = self.GetSelection()
4298
4299 if selection == -1:
4300 event.Skip()
4301 return
4302
4303 if not self.IsTabVisible(selection):
4304 if selection == len(self._pagesInfoVec) - 1:
4305 if not self.CanFitToScreen(selection):
4306 event.Skip()
4307 return
4308 else:
4309 event.Skip()
4310 return
4311
4312 render.DrawTabX(self, dc, self._pagesInfoVec[selection].GetXRect(), selection, self._nTabXButtonStatus)
4313
4314 event.Skip()
4315
4316
4317 def OnMouseEnterWindow(self, event):
4318 """ Handles the wx.EVT_ENTER_WINDOW event for L{PageContainer}. """
4319
4320 self._nLeftButtonStatus = FNB_BTN_NONE
4321 self._nXButtonStatus = FNB_BTN_NONE
4322 self._nRightButtonStatus = FNB_BTN_NONE
4323 self._nLeftClickZone = FNB_BTN_NONE
4324 self._nArrowDownButtonStatus = FNB_BTN_NONE
4325
4326 event.Skip()
4327
4328
4329 def ShowTabTooltip(self, tabIdx):
4330 """ Shows a tab tooltip. """
4331
4332 pWindow = self._pParent.GetPage(tabIdx)
4333
4334 if pWindow:
4335 pToolTip = pWindow.GetToolTip()
4336 if pToolTip and pToolTip.GetWindow() == pWindow:
4337 self.SetToolTipString(pToolTip.GetTip())
4338
4339
4340 def SetPageImage(self, page, imgindex):
4341 """ Sets the image index associated to a page. """
4342
4343 if page < len(self._pagesInfoVec):
4344
4345 self._pagesInfoVec[page].SetImageIndex(imgindex)
4346 self.Refresh()
4347
4348
4349 def GetPageImage(self, page):
4350 """ Returns the image index associated to a page. """
4351
4352 if page < len(self._pagesInfoVec):
4353
4354 return self._pagesInfoVec[page].GetImageIndex()
4355
4356 return -1
4357
4358
4359 def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
4360 """ Handles the drop action from a DND operation. """
4361
4362 # Disable drag'n'drop for disabled tab
4363 if not wnd_oldContainer._pagesInfoVec[nTabPage].GetEnabled():
4364 return wx.DragCancel
4365
4366 self._isdragging = True
4367 oldContainer = wnd_oldContainer
4368 nIndex = -1
4369
4370 where, nIndex = self.HitTest(wx.Point(x, y))
4371
4372 oldNotebook = oldContainer.GetParent()
4373 newNotebook = self.GetParent()
4374
4375 if oldNotebook == newNotebook:
4376
4377 if nTabPage >= 0:
4378
4379 if where == FNB_TAB:
4380 self.MoveTabPage(nTabPage, nIndex)
4381
4382 elif self.GetParent().GetWindowStyleFlag() & FNB_ALLOW_FOREIGN_DND:
4383
4384 if wx.Platform in ["__WXMSW__", "__WXGTK__"]:
4385 if nTabPage >= 0:
4386
4387 window = oldNotebook.GetPage(nTabPage)
4388
4389 if window:
4390 where, nIndex = newNotebook._pages.HitTest(wx.Point(x, y))
4391 caption = oldContainer.GetPageText(nTabPage)
4392 imageindex = oldContainer.GetPageImage(nTabPage)
4393 oldNotebook.RemovePage(nTabPage)
4394 window.Reparent(newNotebook)
4395
4396 if imageindex >= 0:
4397
4398 bmp = oldNotebook.GetImageList().GetIcon(imageindex)
4399 newImageList = newNotebook.GetImageList()
4400
4401 if not newImageList:
4402 xbmp, ybmp = bmp.GetWidth(), bmp.GetHeight()
4403 newImageList = wx.ImageList(xbmp, ybmp)
4404 imageindex = 0
4405 else:
4406 imageindex = newImageList.GetImageCount()
4407
4408 newImageList.AddIcon(bmp)
4409 newNotebook.SetImageList(newImageList)
4410
4411 newNotebook.InsertPage(nIndex, window, caption, True, imageindex)
4412
4413 self._isdragging = False
4414
4415 return wx.DragMove
4416
4417
4418 def MoveTabPage(self, nMove, nMoveTo):
4419 """ Moves a tab inside the same L{FlatNotebook}. """
4420
4421 if nMove == nMoveTo:
4422 return
4423
4424 elif nMoveTo < len(self._pParent._windows):
4425 nMoveTo = nMoveTo + 1
4426
4427 self._pParent.Freeze()
4428
4429 # Remove the window from the main sizer
4430 nCurSel = self._pParent._pages.GetSelection()
4431 self._pParent._mainSizer.Detach(self._pParent._windows[nCurSel])
4432 self._pParent._windows[nCurSel].Hide()
4433
4434 pWindow = self._pParent._windows[nMove]
4435 self._pParent._windows.pop(nMove)
4436 self._pParent._windows.insert(nMoveTo-1, pWindow)
4437
4438 pgInfo = self._pagesInfoVec[nMove]
4439
4440 self._pagesInfoVec.pop(nMove)
4441 self._pagesInfoVec.insert(nMoveTo - 1, pgInfo)
4442
4443 # Add the page according to the style
4444 pSizer = self._pParent._mainSizer
4445 style = self.GetParent().GetWindowStyleFlag()
4446
4447 if style & FNB_BOTTOM:
4448
4449 pSizer.Insert(0, pWindow, 1, wx.EXPAND)
4450
4451 else:
4452
4453 # We leave a space of 1 pixel around the window
4454 pSizer.Add(pWindow, 1, wx.EXPAND)
4455
4456 pWindow.Show()
4457
4458 pSizer.Layout()
4459 self._iActivePage = nMoveTo - 1
4460 self._iPreviousActivePage = -1
4461 self.DoSetSelection(self._iActivePage)
4462 self.Refresh()
4463 self._pParent.Thaw()
4464
4465
4466 def CanFitToScreen(self, page):
4467 """ Returns wheter a tab can fit in the left space in the screen or not. """
4468
4469 # Incase the from is greater than page,
4470 # we need to reset the self._nFrom, so in order
4471 # to force the caller to do so, we return false
4472 if self._nFrom > page:
4473 return False
4474
4475 style = self.GetParent().GetWindowStyleFlag()
4476 render = self._mgr.GetRenderer(style)
4477
4478 vTabInfo = render.NumberTabsCanFit(self)
4479
4480 if page - self._nFrom >= len(vTabInfo):
4481 return False
4482
4483 return True
4484
4485
4486 def GetNumOfVisibleTabs(self):
4487 """ Returns the number of visible tabs. """
4488
4489 count = 0
4490 for ii in xrange(self._nFrom, len(self._pagesInfoVec)):
4491 if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
4492 break
4493 count = count + 1
4494
4495 return count
4496
4497
4498 def GetEnabled(self, page):
4499 """ Returns whether a tab is enabled or not. """
4500
4501 if page >= len(self._pagesInfoVec):
4502 return True # Seems strange, but this is the default
4503
4504 return self._pagesInfoVec[page].GetEnabled()
4505
4506
4507 def Enable(self, page, enabled=True):
4508 """ Enables or disables a tab. """
4509
4510 if page >= len(self._pagesInfoVec):
4511 return
4512
4513 self._pagesInfoVec[page].Enable(enabled)
4514
4515
4516 def GetSingleLineBorderColour(self):
4517 """ Returns the colour for the single line border. """
4518
4519 if self.HasFlag(FNB_FANCY_TABS):
4520 return self._colorFrom
4521
4522 return wx.WHITE
4523
4524
4525 def HasFlag(self, flag):
4526 """ Returns whether a flag is present in the L{FlatNotebook} style. """
4527
4528 style = self.GetParent().GetWindowStyleFlag()
4529 res = (style & flag and [True] or [False])[0]
4530 return res
4531
4532
4533 def ClearFlag(self, flag):
4534 """ Deletes a flag from the L{FlatNotebook} style. """
4535
4536 style = self.GetParent().GetWindowStyleFlag()
4537 style &= ~flag
4538 self.SetWindowStyleFlag(style)
4539
4540
4541 def TabHasImage(self, tabIdx):
4542 """ Returns whether a tab has an associated image index or not. """
4543
4544 if self._ImageList:
4545 return self._pagesInfoVec[tabIdx].GetImageIndex() != -1
4546
4547 return False
4548
4549
4550 def OnLeftDClick(self, event):
4551 """ Handles the wx.EVT_LEFT_DCLICK event for L{PageContainer}. """
4552
4553 if self.HasFlag(FNB_DCLICK_CLOSES_TABS):
4554
4555 where, tabIdx = self.HitTest(event.GetPosition())
4556
4557 if where == FNB_TAB:
4558 self.DeletePage(tabIdx)
4559
4560 else:
4561
4562 event.Skip()
4563
4564
4565 def PopupTabsMenu(self):
4566 """ Pops up the menu activated with the drop down arrow in the navigation area. """
4567
4568 popupMenu = wx.Menu()
4569
4570 for i in xrange(len(self._pagesInfoVec)):
4571 pi = self._pagesInfoVec[i]
4572 item = wx.MenuItem(popupMenu, i, pi.GetCaption(), pi.GetCaption(), wx.ITEM_NORMAL)
4573 self.Bind(wx.EVT_MENU, self.OnTabMenuSelection, item)
4574
4575 # This code is commented, since there is an alignment problem with wx2.6.3 & Menus
4576 # if self.TabHasImage(ii):
4577 # item.SetBitmaps( (*m_ImageList)[pi.GetImageIndex()] );
4578
4579 popupMenu.AppendItem(item)
4580 item.Enable(pi.GetEnabled())
4581
4582 self.PopupMenu(popupMenu)
4583
4584
4585 def OnTabMenuSelection(self, event):
4586 """ Handles the wx.EVT_MENU event for L{PageContainer}. """
4587
4588 selection = event.GetId()
4589 self.FireEvent(selection)
4590
4591
4592 def FireEvent(self, selection):
4593 """
4594 Fires the wxEVT_FLATNOTEBOOK_PAGE_CHANGING and wxEVT_FLATNOTEBOOK_PAGE_CHANGED events
4595 called from other methods (from menu selection or Smart Tabbing).
4596 Utility function.
4597 """
4598
4599 if selection == self._iActivePage:
4600 # No events for the same selection
4601 return
4602
4603 oldSelection = self._iActivePage
4604
4605 event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
4606 event.SetSelection(selection)
4607 event.SetOldSelection(oldSelection)
4608 event.SetEventObject(self.GetParent())
4609
4610 if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed():
4611
4612 self.SetSelection(selection)
4613
4614 # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
4615 event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
4616 event.SetOldSelection(oldSelection)
4617 self.GetParent().GetEventHandler().ProcessEvent(event)
4618
4619
4620 def SetImageList(self, imglist):
4621 """ Sets the image list for the page control. """
4622
4623 self._ImageList = imglist
4624
4625
4626 def GetImageList(self):
4627 """ Returns the image list for the page control. """
4628
4629 return self._ImageList
4630
4631
4632 def GetSelection(self):
4633 """ Returns the current selected page. """
4634
4635 return self._iActivePage
4636
4637
4638 def GetPageCount(self):
4639 """ Returns the number of tabs in the L{FlatNotebook} control. """
4640
4641 return len(self._pagesInfoVec)
4642
4643
4644 def GetPageText(self, page):
4645 """ Returns the tab caption of the page. """
4646
4647 return self._pagesInfoVec[page].GetCaption()
4648
4649
4650 def SetPageText(self, page, text):
4651 """ Sets the tab caption of the page. """
4652
4653 self._pagesInfoVec[page].SetCaption(text)
4654 return True
4655
4656
4657 def DrawDragHint(self):
4658 """ Draws small arrow at the place that the tab will be placed. """
4659
4660 # get the index of tab that will be replaced with the dragged tab
4661 pt = wx.GetMousePosition()
4662 client_pt = self.ScreenToClient(pt)
4663 where, tabIdx = self.HitTest(client_pt)
4664 self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag()).DrawDragHint(self, tabIdx)
4665