comparison orpg/tools/FlatNotebook.py @ 0:4385a7d0efd1 grumpy-goblin

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