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