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