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