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