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